From 114c03da2617ca4dff6cab564942f3c4370a7b66 Mon Sep 17 00:00:00 2001 From: marbar3778 Date: Tue, 15 Feb 2022 17:16:26 +0100 Subject: [PATCH 1/8] add new cgo --- .../secp256k1/internal/secp256k1/README.md | 5 - .../secp256k1/internal/secp256k1/curve.go | 60 ++--- .../secp256k1/internal/secp256k1/dummy.go | 21 ++ .../internal/secp256k1/libsecp256k1/README.md | 59 +++-- .../secp256k1/libsecp256k1/contrib/dummy.go | 7 + .../internal/secp256k1/libsecp256k1/dummy.go | 7 + .../secp256k1/libsecp256k1/include/dummy.go | 7 + .../libsecp256k1/src/asm/field_10x26_arm.s | 4 +- .../secp256k1/libsecp256k1/src/dummy.go | 7 + .../libsecp256k1/src/modules/dummy.go | 7 + .../libsecp256k1/src/modules/ecdh/dummy.go | 7 + .../src/modules/recovery/dummy.go | 7 + .../secp256k1/internal/secp256k1/panic_cb.go | 3 + .../internal/secp256k1/scalar_mult_cgo.go | 57 +++++ .../internal/secp256k1/scalar_mult_nocgo.go | 14 ++ .../secp256k1/internal/secp256k1/secp256.go | 16 +- .../internal/secp256k1/secp256_test.go | 238 ++++++++++++++++++ crypto/keys/secp256k1/secp256k1_cgo.go | 5 +- 18 files changed, 445 insertions(+), 86 deletions(-) delete mode 100644 crypto/keys/secp256k1/internal/secp256k1/README.md create mode 100644 crypto/keys/secp256k1/internal/secp256k1/dummy.go create mode 100644 crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/contrib/dummy.go create mode 100644 crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/dummy.go create mode 100644 crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/include/dummy.go create mode 100644 crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/dummy.go create mode 100644 crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/modules/dummy.go create mode 100644 crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/modules/ecdh/dummy.go create mode 100644 crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/modules/recovery/dummy.go create mode 100644 crypto/keys/secp256k1/internal/secp256k1/scalar_mult_cgo.go create mode 100644 crypto/keys/secp256k1/internal/secp256k1/scalar_mult_nocgo.go create mode 100644 crypto/keys/secp256k1/internal/secp256k1/secp256_test.go diff --git a/crypto/keys/secp256k1/internal/secp256k1/README.md b/crypto/keys/secp256k1/internal/secp256k1/README.md deleted file mode 100644 index 3a558a130a93..000000000000 --- a/crypto/keys/secp256k1/internal/secp256k1/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# secp256k1 - -This package is copied from https://github.com/ethereum/go-ethereum/tree/729bf365b5f17325be9107b63b233da54100eec6/crypto/secp256k1 - -Unlike the rest of go-ethereum it is MIT licensed so compatible with our Apache2.0 license. We opt to copy in here rather than depend on go-ethereum to avoid issues with vendoring of the GPL parts of that repository by downstream. diff --git a/crypto/keys/secp256k1/internal/secp256k1/curve.go b/crypto/keys/secp256k1/internal/secp256k1/curve.go index 7a2387365c6b..fa1b199a3484 100644 --- a/crypto/keys/secp256k1/internal/secp256k1/curve.go +++ b/crypto/keys/secp256k1/internal/secp256k1/curve.go @@ -30,23 +30,13 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// nolint:gocritic package secp256k1 import ( "crypto/elliptic" "math/big" - "unsafe" ) -/* -#include "libsecp256k1/include/secp256k1.h" -extern int secp256k1_ext_scalar_mul(const secp256k1_context* ctx, - const unsigned char *point, - const unsigned char *scalar); -*/ -import "C" - const ( // number of bits in a big.Word wordBits = 32 << (uint64(^big.Word(0)) >> 63) @@ -119,6 +109,10 @@ func (BitCurve *BitCurve) IsOnCurve(x, y *big.Int) bool { // affineFromJacobian reverses the Jacobian transform. See the comment at the // top of the file. func (BitCurve *BitCurve) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big.Int) { + if z.Sign() == 0 { + return new(big.Int), new(big.Int) + } + zinv := new(big.Int).ModInverse(z, BitCurve.P) zinvsq := new(big.Int).Mul(zinv, zinv) @@ -132,7 +126,18 @@ func (BitCurve *BitCurve) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big. // Add returns the sum of (x1,y1) and (x2,y2) func (BitCurve *BitCurve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) { + // If one point is at infinity, return the other point. + // Adding the point at infinity to any point will preserve the other point. + if x1.Sign() == 0 && y1.Sign() == 0 { + return x2, y2 + } + if x2.Sign() == 0 && y2.Sign() == 0 { + return x1, y1 + } z := new(big.Int).SetInt64(1) + if x1.Cmp(x2) == 0 && y1.Cmp(y2) == 0 { + return BitCurve.affineFromJacobian(BitCurve.doubleJacobian(x1, y1, z)) + } return BitCurve.affineFromJacobian(BitCurve.addJacobian(x1, y1, z, x2, y2, z)) } @@ -241,41 +246,6 @@ func (BitCurve *BitCurve) doubleJacobian(x, y, z *big.Int) (*big.Int, *big.Int, return x3, y3, z3 } -func (BitCurve *BitCurve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int, *big.Int) { - // Ensure scalar is exactly 32 bytes. We pad always, even if - // scalar is 32 bytes long, to avoid a timing side channel. - if len(scalar) > 32 { - panic("can't handle scalars > 256 bits") - } - // NOTE: potential timing issue - padded := make([]byte, 32) - copy(padded[32-len(scalar):], scalar) - scalar = padded - - // Do the multiplication in C, updating point. - point := make([]byte, 64) - readBits(Bx, point[:32]) - readBits(By, point[32:]) - - pointPtr := (*C.uchar)(unsafe.Pointer(&point[0])) - scalarPtr := (*C.uchar)(unsafe.Pointer(&scalar[0])) - res := C.secp256k1_ext_scalar_mul(context, pointPtr, scalarPtr) - - // Unpack the result and clear temporaries. - x := new(big.Int).SetBytes(point[:32]) - y := new(big.Int).SetBytes(point[32:]) - for i := range point { - point[i] = 0 - } - for i := range padded { - scalar[i] = 0 - } - if res != 1 { - return nil, nil - } - return x, y -} - // ScalarBaseMult returns k*G, where G is the base point of the group and k is // an integer in big-endian form. func (BitCurve *BitCurve) ScalarBaseMult(k []byte) (*big.Int, *big.Int) { diff --git a/crypto/keys/secp256k1/internal/secp256k1/dummy.go b/crypto/keys/secp256k1/internal/secp256k1/dummy.go new file mode 100644 index 000000000000..65a75080f60a --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/dummy.go @@ -0,0 +1,21 @@ +//go:build dummy +// +build dummy + +// This file is part of a workaround for `go mod vendor` which won't vendor +// C files if there's no Go file in the same directory. +// This would prevent the crypto/secp256k1/libsecp256k1/include/secp256k1.h file to be vendored. +// +// This Go file imports the c directory where there is another dummy.go file which +// is the second part of this workaround. +// +// These two files combined make it so `go mod vendor` behaves correctly. +// +// See this issue for reference: https://github.com/golang/go/issues/26366 + +package secp256k1 + +import ( + _ "github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/include" + _ "github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src" + _ "github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/modules/recovery" +) diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/README.md b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/README.md index 687300173583..8cd344ea8123 100644 --- a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/README.md +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/README.md @@ -8,7 +8,6 @@ Optimized C library for EC operations on curve secp256k1. This library is a work in progress and is being used to research best practices. Use at your own risk. Features: - * secp256k1 ECDSA signing/verification and key generation. * Adding/multiplying private/public keys. * Serialization/parsing of private keys, public keys, signatures. @@ -20,43 +19,43 @@ Implementation details ---------------------- * General - * No runtime heap allocation. - * Extensive testing infrastructure. - * Structured to facilitate review and analysis. - * Intended to be portable to any system with a C89 compiler and uint64_t support. - * Expose only higher level interfaces to minimize the API surface and improve application security. ("Be difficult to use insecurely.") + * No runtime heap allocation. + * Extensive testing infrastructure. + * Structured to facilitate review and analysis. + * Intended to be portable to any system with a C89 compiler and uint64_t support. + * Expose only higher level interfaces to minimize the API surface and improve application security. ("Be difficult to use insecurely.") * Field operations - * Optimized implementation of arithmetic modulo the curve's field size (2^256 - 0x1000003D1). - * Using 5 52-bit limbs (including hand-optimized assembly for x86_64, by Diederik Huys). - * Using 10 26-bit limbs. - * Field inverses and square roots using a sliding window over blocks of 1s (by Peter Dettman). + * Optimized implementation of arithmetic modulo the curve's field size (2^256 - 0x1000003D1). + * Using 5 52-bit limbs (including hand-optimized assembly for x86_64, by Diederik Huys). + * Using 10 26-bit limbs. + * Field inverses and square roots using a sliding window over blocks of 1s (by Peter Dettman). * Scalar operations - * Optimized implementation without data-dependent branches of arithmetic modulo the curve's order. - * Using 4 64-bit limbs (relying on __int128 support in the compiler). - * Using 8 32-bit limbs. + * Optimized implementation without data-dependent branches of arithmetic modulo the curve's order. + * Using 4 64-bit limbs (relying on __int128 support in the compiler). + * Using 8 32-bit limbs. * Group operations - * Point addition formula specifically simplified for the curve equation (y^2 = x^3 + 7). - * Use addition between points in Jacobian and affine coordinates where possible. - * Use a unified addition/doubling formula where necessary to avoid data-dependent branches. - * Point/x comparison without a field inversion by comparison in the Jacobian coordinate space. + * Point addition formula specifically simplified for the curve equation (y^2 = x^3 + 7). + * Use addition between points in Jacobian and affine coordinates where possible. + * Use a unified addition/doubling formula where necessary to avoid data-dependent branches. + * Point/x comparison without a field inversion by comparison in the Jacobian coordinate space. * Point multiplication for verification (a*P + b*G). - * Use wNAF notation for point multiplicands. - * Use a much larger window for multiples of G, using precomputed multiples. - * Use Shamir's trick to do the multiplication with the public key and the generator simultaneously. - * Optionally (off by default) use secp256k1's efficiently-computable endomorphism to split the P multiplicand into 2 half-sized ones. + * Use wNAF notation for point multiplicands. + * Use a much larger window for multiples of G, using precomputed multiples. + * Use Shamir's trick to do the multiplication with the public key and the generator simultaneously. + * Optionally (off by default) use secp256k1's efficiently-computable endomorphism to split the P multiplicand into 2 half-sized ones. * Point multiplication for signing - * Use a precomputed table of multiples of powers of 16 multiplied with the generator, so general multiplication becomes a series of additions. - * Access the table with branch-free conditional moves so memory access is uniform. - * No data-dependent branches - * The precomputed tables add and eventually subtract points for which no known scalar (private key) is known, preventing even an attacker with control over the private key used to control the data internally. + * Use a precomputed table of multiples of powers of 16 multiplied with the generator, so general multiplication becomes a series of additions. + * Access the table with branch-free conditional moves so memory access is uniform. + * No data-dependent branches + * The precomputed tables add and eventually subtract points for which no known scalar (private key) is known, preventing even an attacker with control over the private key used to control the data internally. Build steps ----------- libsecp256k1 is built using autotools: - ./autogen.sh - ./configure - make - ./tests - sudo make install # optional + $ ./autogen.sh + $ ./configure + $ make + $ ./tests + $ sudo make install # optional diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/contrib/dummy.go b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/contrib/dummy.go new file mode 100644 index 000000000000..fda594be9914 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/contrib/dummy.go @@ -0,0 +1,7 @@ +// +build dummy + +// Package c contains only a C file. +// +// This Go file is part of a workaround for `go mod vendor`. +// Please see the file crypto/secp256k1/dummy.go for more information. +package contrib diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/dummy.go b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/dummy.go new file mode 100644 index 000000000000..379b16992f47 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/dummy.go @@ -0,0 +1,7 @@ +// +build dummy + +// Package c contains only a C file. +// +// This Go file is part of a workaround for `go mod vendor`. +// Please see the file crypto/secp256k1/dummy.go for more information. +package libsecp256k1 diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/include/dummy.go b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/include/dummy.go new file mode 100644 index 000000000000..5af540c73c4a --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/include/dummy.go @@ -0,0 +1,7 @@ +// +build dummy + +// Package c contains only a C file. +// +// This Go file is part of a workaround for `go mod vendor`. +// Please see the file crypto/secp256k1/dummy.go for more information. +package include diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/asm/field_10x26_arm.s b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/asm/field_10x26_arm.s index 5df561f2fc93..5a9cc3ffcfda 100644 --- a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/asm/field_10x26_arm.s +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/asm/field_10x26_arm.s @@ -11,7 +11,7 @@ Note: - To avoid unnecessary loads and make use of available registers, two 'passes' have every time been interleaved, with the odd passes accumulating c' and d' - which will be added to c and d respectively in the the even passes + which will be added to c and d respectively in the even passes */ @@ -23,7 +23,7 @@ Note: .eabi_attribute 10, 0 @ Tag_FP_arch = none .eabi_attribute 24, 1 @ Tag_ABI_align_needed = 8-byte .eabi_attribute 25, 1 @ Tag_ABI_align_preserved = 8-byte, except leaf SP - .eabi_attribute 30, 2 @ Tag_ABI_optimization_goals = Agressive Speed + .eabi_attribute 30, 2 @ Tag_ABI_optimization_goals = Aggressive Speed .eabi_attribute 34, 1 @ Tag_CPU_unaligned_access = v6 .text diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/dummy.go b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/dummy.go new file mode 100644 index 000000000000..65868f38a8ea --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/dummy.go @@ -0,0 +1,7 @@ +// +build dummy + +// Package c contains only a C file. +// +// This Go file is part of a workaround for `go mod vendor`. +// Please see the file crypto/secp256k1/dummy.go for more information. +package src diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/modules/dummy.go b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/modules/dummy.go new file mode 100644 index 000000000000..3c7a696439f0 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/modules/dummy.go @@ -0,0 +1,7 @@ +// +build dummy + +// Package c contains only a C file. +// +// This Go file is part of a workaround for `go mod vendor`. +// Please see the file crypto/secp256k1/dummy.go for more information. +package module diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/modules/ecdh/dummy.go b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/modules/ecdh/dummy.go new file mode 100644 index 000000000000..b6fc38327ec8 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/modules/ecdh/dummy.go @@ -0,0 +1,7 @@ +// +build dummy + +// Package c contains only a C file. +// +// This Go file is part of a workaround for `go mod vendor`. +// Please see the file crypto/secp256k1/dummy.go for more information. +package ecdh diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/modules/recovery/dummy.go b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/modules/recovery/dummy.go new file mode 100644 index 000000000000..b9491f0cb9f4 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/modules/recovery/dummy.go @@ -0,0 +1,7 @@ +// +build dummy + +// Package c contains only a C file. +// +// This Go file is part of a workaround for `go mod vendor`. +// Please see the file crypto/secp256k1/dummy.go for more information. +package recovery diff --git a/crypto/keys/secp256k1/internal/secp256k1/panic_cb.go b/crypto/keys/secp256k1/internal/secp256k1/panic_cb.go index 6d59a1d247ea..a30b04f51b42 100644 --- a/crypto/keys/secp256k1/internal/secp256k1/panic_cb.go +++ b/crypto/keys/secp256k1/internal/secp256k1/panic_cb.go @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be found in // the LICENSE file. +//go:build !gofuzz && cgo +// +build !gofuzz,cgo + package secp256k1 import "C" diff --git a/crypto/keys/secp256k1/internal/secp256k1/scalar_mult_cgo.go b/crypto/keys/secp256k1/internal/secp256k1/scalar_mult_cgo.go new file mode 100644 index 000000000000..8afa9d023b07 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/scalar_mult_cgo.go @@ -0,0 +1,57 @@ +// Copyright 2015 Jeffrey Wilcke, Felix Lange, Gustav Simonsson. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be found in +// the LICENSE file. + +//go:build !gofuzz && cgo +// +build !gofuzz,cgo + +package secp256k1 + +import ( + "math/big" + "unsafe" +) + +/* + +#include "libsecp256k1/include/secp256k1.h" + +extern int secp256k1_ext_scalar_mul(const secp256k1_context* ctx, const unsigned char *point, const unsigned char *scalar); + +*/ +import "C" + +func (BitCurve *BitCurve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int, *big.Int) { + // Ensure scalar is exactly 32 bytes. We pad always, even if + // scalar is 32 bytes long, to avoid a timing side channel. + if len(scalar) > 32 { + panic("can't handle scalars > 256 bits") + } + // NOTE: potential timing issue + padded := make([]byte, 32) + copy(padded[32-len(scalar):], scalar) + scalar = padded + + // Do the multiplication in C, updating point. + point := make([]byte, 64) + readBits(Bx, point[:32]) + readBits(By, point[32:]) + + pointPtr := (*C.uchar)(unsafe.Pointer(&point[0])) + scalarPtr := (*C.uchar)(unsafe.Pointer(&scalar[0])) + res := C.secp256k1_ext_scalar_mul(context, pointPtr, scalarPtr) + + // Unpack the result and clear temporaries. + x := new(big.Int).SetBytes(point[:32]) + y := new(big.Int).SetBytes(point[32:]) + for i := range point { + point[i] = 0 + } + for i := range padded { + scalar[i] = 0 + } + if res != 1 { + return nil, nil + } + return x, y +} diff --git a/crypto/keys/secp256k1/internal/secp256k1/scalar_mult_nocgo.go b/crypto/keys/secp256k1/internal/secp256k1/scalar_mult_nocgo.go new file mode 100644 index 000000000000..22f53ac6ae65 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/scalar_mult_nocgo.go @@ -0,0 +1,14 @@ +// Copyright 2015 Jeffrey Wilcke, Felix Lange, Gustav Simonsson. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be found in +// the LICENSE file. + +//go:build gofuzz || !cgo +// +build gofuzz !cgo + +package secp256k1 + +import "math/big" + +func (BitCurve *BitCurve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int, *big.Int) { + panic("ScalarMult is not available when secp256k1 is built without cgo") +} diff --git a/crypto/keys/secp256k1/internal/secp256k1/secp256.go b/crypto/keys/secp256k1/internal/secp256k1/secp256.go index 35d0eef34ace..c9c01b3209af 100644 --- a/crypto/keys/secp256k1/internal/secp256k1/secp256.go +++ b/crypto/keys/secp256k1/internal/secp256k1/secp256.go @@ -2,16 +2,28 @@ // Use of this source code is governed by a BSD-style license that can be found in // the LICENSE file. +//go:build !gofuzz && cgo +// +build !gofuzz,cgo + // Package secp256k1 wraps the bitcoin secp256k1 C library. package secp256k1 /* #cgo CFLAGS: -I./libsecp256k1 #cgo CFLAGS: -I./libsecp256k1/src/ + +#ifdef __SIZEOF_INT128__ +# define HAVE___INT128 +# define USE_FIELD_5X52 +# define USE_SCALAR_4X64 +#else +# define USE_FIELD_10X26 +# define USE_SCALAR_8X32 +#endif + +#define USE_ENDOMORPHISM #define USE_NUM_NONE -#define USE_FIELD_10X26 #define USE_FIELD_INV_BUILTIN -#define USE_SCALAR_8X32 #define USE_SCALAR_INV_BUILTIN #define NDEBUG #include "./libsecp256k1/src/secp256k1.c" diff --git a/crypto/keys/secp256k1/internal/secp256k1/secp256_test.go b/crypto/keys/secp256k1/internal/secp256k1/secp256_test.go new file mode 100644 index 000000000000..ef2a3a3790b4 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/secp256_test.go @@ -0,0 +1,238 @@ +// Copyright 2015 Jeffrey Wilcke, Felix Lange, Gustav Simonsson. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be found in +// the LICENSE file. + +package secp256k1 + +import ( + "bytes" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "encoding/hex" + "io" + "testing" +) + +const TestCount = 1000 + +func generateKeyPair() (pubkey, privkey []byte) { + key, err := ecdsa.GenerateKey(S256(), rand.Reader) + if err != nil { + panic(err) + } + pubkey = elliptic.Marshal(S256(), key.X, key.Y) + + privkey = make([]byte, 32) + blob := key.D.Bytes() + copy(privkey[32-len(blob):], blob) + + return pubkey, privkey +} + +func csprngEntropy(n int) []byte { + buf := make([]byte, n) + if _, err := io.ReadFull(rand.Reader, buf); err != nil { + panic("reading from crypto/rand failed: " + err.Error()) + } + return buf +} + +func randSig() []byte { + sig := csprngEntropy(65) + sig[32] &= 0x70 + sig[64] %= 4 + return sig +} + +// tests for malleability +// highest bit of signature ECDSA s value must be 0, in the 33th byte +func compactSigCheck(t *testing.T, sig []byte) { + var b = int(sig[32]) + if b < 0 { + t.Errorf("highest bit is negative: %d", b) + } + if ((b >> 7) == 1) != ((b & 0x80) == 0x80) { + t.Errorf("highest bit: %d bit >> 7: %d", b, b>>7) + } + if (b & 0x80) == 0x80 { + t.Errorf("highest bit: %d bit & 0x80: %d", b, b&0x80) + } +} + +func TestSignatureValidity(t *testing.T) { + pubkey, seckey := generateKeyPair() + msg := csprngEntropy(32) + sig, err := Sign(msg, seckey) + if err != nil { + t.Errorf("signature error: %s", err) + } + compactSigCheck(t, sig) + if len(pubkey) != 65 { + t.Errorf("pubkey length mismatch: want: 65 have: %d", len(pubkey)) + } + if len(seckey) != 32 { + t.Errorf("seckey length mismatch: want: 32 have: %d", len(seckey)) + } + if len(sig) != 65 { + t.Errorf("sig length mismatch: want: 65 have: %d", len(sig)) + } + recid := int(sig[64]) + if recid > 4 || recid < 0 { + t.Errorf("sig recid mismatch: want: within 0 to 4 have: %d", int(sig[64])) + } +} + +func TestInvalidRecoveryID(t *testing.T) { + _, seckey := generateKeyPair() + msg := csprngEntropy(32) + sig, _ := Sign(msg, seckey) + sig[64] = 99 + _, err := RecoverPubkey(msg, sig) + if err != ErrInvalidRecoveryID { + t.Fatalf("got %q, want %q", err, ErrInvalidRecoveryID) + } +} + +func TestSignAndRecover(t *testing.T) { + pubkey1, seckey := generateKeyPair() + msg := csprngEntropy(32) + sig, err := Sign(msg, seckey) + if err != nil { + t.Errorf("signature error: %s", err) + } + pubkey2, err := RecoverPubkey(msg, sig) + if err != nil { + t.Errorf("recover error: %s", err) + } + if !bytes.Equal(pubkey1, pubkey2) { + t.Errorf("pubkey mismatch: want: %x have: %x", pubkey1, pubkey2) + } +} + +func TestSignDeterministic(t *testing.T) { + _, seckey := generateKeyPair() + msg := make([]byte, 32) + copy(msg, "hi there") + + sig1, err := Sign(msg, seckey) + if err != nil { + t.Fatal(err) + } + sig2, err := Sign(msg, seckey) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(sig1, sig2) { + t.Fatal("signatures not equal") + } +} + +func TestRandomMessagesWithSameKey(t *testing.T) { + pubkey, seckey := generateKeyPair() + keys := func() ([]byte, []byte) { + return pubkey, seckey + } + signAndRecoverWithRandomMessages(t, keys) +} + +func TestRandomMessagesWithRandomKeys(t *testing.T) { + keys := func() ([]byte, []byte) { + pubkey, seckey := generateKeyPair() + return pubkey, seckey + } + signAndRecoverWithRandomMessages(t, keys) +} + +func signAndRecoverWithRandomMessages(t *testing.T, keys func() ([]byte, []byte)) { + for i := 0; i < TestCount; i++ { + pubkey1, seckey := keys() + msg := csprngEntropy(32) + sig, err := Sign(msg, seckey) + if err != nil { + t.Fatalf("signature error: %s", err) + } + if sig == nil { + t.Fatal("signature is nil") + } + compactSigCheck(t, sig) + + // TODO: why do we flip around the recovery id? + sig[len(sig)-1] %= 4 + + pubkey2, err := RecoverPubkey(msg, sig) + if err != nil { + t.Fatalf("recover error: %s", err) + } + if pubkey2 == nil { + t.Error("pubkey is nil") + } + if !bytes.Equal(pubkey1, pubkey2) { + t.Fatalf("pubkey mismatch: want: %x have: %x", pubkey1, pubkey2) + } + } +} + +func TestRecoveryOfRandomSignature(t *testing.T) { + pubkey1, _ := generateKeyPair() + msg := csprngEntropy(32) + + for i := 0; i < TestCount; i++ { + // recovery can sometimes work, but if so should always give wrong pubkey + pubkey2, _ := RecoverPubkey(msg, randSig()) + if bytes.Equal(pubkey1, pubkey2) { + t.Fatalf("iteration: %d: pubkey mismatch: do NOT want %x: ", i, pubkey2) + } + } +} + +func TestRandomMessagesAgainstValidSig(t *testing.T) { + pubkey1, seckey := generateKeyPair() + msg := csprngEntropy(32) + sig, _ := Sign(msg, seckey) + + for i := 0; i < TestCount; i++ { + msg = csprngEntropy(32) + pubkey2, _ := RecoverPubkey(msg, sig) + // recovery can sometimes work, but if so should always give wrong pubkey + if bytes.Equal(pubkey1, pubkey2) { + t.Fatalf("iteration: %d: pubkey mismatch: do NOT want %x: ", i, pubkey2) + } + } +} + +// Useful when the underlying libsecp256k1 API changes to quickly +// check only recover function without use of signature function +func TestRecoverSanity(t *testing.T) { + msg, _ := hex.DecodeString("ce0677bb30baa8cf067c88db9811f4333d131bf8bcf12fe7065d211dce971008") + sig, _ := hex.DecodeString("90f27b8b488db00b00606796d2987f6a5f59ae62ea05effe84fef5b8b0e549984a691139ad57a3f0b906637673aa2f63d1f55cb1a69199d4009eea23ceaddc9301") + pubkey1, _ := hex.DecodeString("04e32df42865e97135acfb65f3bae71bdc86f4d49150ad6a440b6f15878109880a0a2b2667f7e725ceea70c673093bf67663e0312623c8e091b13cf2c0f11ef652") + pubkey2, err := RecoverPubkey(msg, sig) + if err != nil { + t.Fatalf("recover error: %s", err) + } + if !bytes.Equal(pubkey1, pubkey2) { + t.Errorf("pubkey mismatch: want: %x have: %x", pubkey1, pubkey2) + } +} + +func BenchmarkSign(b *testing.B) { + _, seckey := generateKeyPair() + msg := csprngEntropy(32) + b.ResetTimer() + + for i := 0; i < b.N; i++ { + Sign(msg, seckey) + } +} + +func BenchmarkRecover(b *testing.B) { + msg := csprngEntropy(32) + _, seckey := generateKeyPair() + sig, _ := Sign(msg, seckey) + b.ResetTimer() + + for i := 0; i < b.N; i++ { + RecoverPubkey(msg, sig) + } +} diff --git a/crypto/keys/secp256k1/secp256k1_cgo.go b/crypto/keys/secp256k1/secp256k1_cgo.go index 42088483d3f9..0f024d6add81 100644 --- a/crypto/keys/secp256k1/secp256k1_cgo.go +++ b/crypto/keys/secp256k1/secp256k1_cgo.go @@ -1,3 +1,4 @@ +//go:build libsecp256k1 // +build libsecp256k1 package secp256k1 @@ -21,6 +22,6 @@ func (privKey *PrivKey) Sign(msg []byte) ([]byte, error) { // VerifySignature validates the signature. // The msg will be hashed prior to signature verification. -func (pubKey *PrivKey) VerifySignature(msg []byte, sig []byte) bool { - return secp256k1.VerifySignature(pubKey.Key, crypto.Sha256(msg), sig) +func (pubkey *PubKey) VerifySignature(msg []byte, sig []byte) bool { + return secp256k1.VerifySignature(pubkey.Bytes(), crypto.Sha256(msg), sig) } From bfb78b78bee8332dbeeddf5b29ebe7b5b0fdebbf Mon Sep 17 00:00:00 2001 From: marbar3778 Date: Wed, 16 Feb 2022 18:29:08 +0100 Subject: [PATCH 2/8] migrate --- crypto/keys/secp256k1/secp256k1_cgo.go | 4 ++-- crypto/keys/secp256k1/secp256k1_cgo_test.go | 5 +++-- crypto/keys/secp256k1/secp256k1_nocgo.go | 4 ++-- crypto/keys/secp256k1/secp256k1_nocgo_test.go | 3 ++- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/crypto/keys/secp256k1/secp256k1_cgo.go b/crypto/keys/secp256k1/secp256k1_cgo.go index 0f024d6add81..11181f88aec0 100644 --- a/crypto/keys/secp256k1/secp256k1_cgo.go +++ b/crypto/keys/secp256k1/secp256k1_cgo.go @@ -1,5 +1,5 @@ -//go:build libsecp256k1 -// +build libsecp256k1 +//go:build libsecp256k1_sdk +// +build libsecp256k1_sdk package secp256k1 diff --git a/crypto/keys/secp256k1/secp256k1_cgo_test.go b/crypto/keys/secp256k1/secp256k1_cgo_test.go index bfaa76caf9db..5dfe4fe73869 100644 --- a/crypto/keys/secp256k1/secp256k1_cgo_test.go +++ b/crypto/keys/secp256k1/secp256k1_cgo_test.go @@ -1,4 +1,5 @@ -// +build libsecp256k1 +//go:build libsecp256k1_sdk +// +build libsecp256k1_sdk package secp256k1 @@ -20,7 +21,7 @@ func TestPrivKeySecp256k1SignVerify(t *testing.T) { wantVerifyPasses bool }{ {name: "valid sign-verify round", privKey: priv, wantSignErr: false, wantVerifyPasses: true}, - {name: "invalid private key", privKey: &*PrivKey{Key: [32]byte{}}, wantSignErr: true, wantVerifyPasses: false}, + {name: "invalid private key", privKey: &PrivKey{Key: []byte{}}, wantSignErr: true, wantVerifyPasses: false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/crypto/keys/secp256k1/secp256k1_nocgo.go b/crypto/keys/secp256k1/secp256k1_nocgo.go index 26735b44229b..60fd9577978c 100644 --- a/crypto/keys/secp256k1/secp256k1_nocgo.go +++ b/crypto/keys/secp256k1/secp256k1_nocgo.go @@ -1,5 +1,5 @@ -//go:build !libsecp256k1 -// +build !libsecp256k1 +//go:build !libsecp256k1_sdk +// +build !libsecp256k1_sdk package secp256k1 diff --git a/crypto/keys/secp256k1/secp256k1_nocgo_test.go b/crypto/keys/secp256k1/secp256k1_nocgo_test.go index 4c2c856e13cc..060b2815a01e 100644 --- a/crypto/keys/secp256k1/secp256k1_nocgo_test.go +++ b/crypto/keys/secp256k1/secp256k1_nocgo_test.go @@ -1,4 +1,5 @@ -// +build !libsecp256k1 +//go:build !libsecp256k1_sdk +// +build !libsecp256k1_sdk package secp256k1 From ae10cd90482d499df7cacefaad98b7b31d3a33c2 Mon Sep 17 00:00:00 2001 From: marbar3778 Date: Tue, 1 Mar 2022 10:03:06 +0100 Subject: [PATCH 3/8] minor change --- crypto/keys/secp256k1/secp256k1_cgo.go | 4 ++-- crypto/keys/secp256k1/secp256k1_cgo_test.go | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/crypto/keys/secp256k1/secp256k1_cgo.go b/crypto/keys/secp256k1/secp256k1_cgo.go index 11181f88aec0..a033b0ffd2b2 100644 --- a/crypto/keys/secp256k1/secp256k1_cgo.go +++ b/crypto/keys/secp256k1/secp256k1_cgo.go @@ -22,6 +22,6 @@ func (privKey *PrivKey) Sign(msg []byte) ([]byte, error) { // VerifySignature validates the signature. // The msg will be hashed prior to signature verification. -func (pubkey *PubKey) VerifySignature(msg []byte, sig []byte) bool { - return secp256k1.VerifySignature(pubkey.Bytes(), crypto.Sha256(msg), sig) +func (pubKey *PubKey) VerifySignature(msg []byte, sigStr []byte) bool { + return secp256k1.VerifySignature(pubKey.Bytes(), crypto.Sha256(msg), sigStr) } diff --git a/crypto/keys/secp256k1/secp256k1_cgo_test.go b/crypto/keys/secp256k1/secp256k1_cgo_test.go index 5dfe4fe73869..bd23b1a1b14c 100644 --- a/crypto/keys/secp256k1/secp256k1_cgo_test.go +++ b/crypto/keys/secp256k1/secp256k1_cgo_test.go @@ -4,6 +4,7 @@ package secp256k1 import ( + "fmt" "testing" "github.com/magiconair/properties/assert" From 2022239ccc6a70c763d8cf48dfc4dc50e69d8c1f Mon Sep 17 00:00:00 2001 From: marbar3778 Date: Tue, 1 Mar 2022 10:23:01 +0100 Subject: [PATCH 4/8] fix test --- crypto/keys/secp256k1/secp256k1_cgo_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crypto/keys/secp256k1/secp256k1_cgo_test.go b/crypto/keys/secp256k1/secp256k1_cgo_test.go index bd23b1a1b14c..2023175e8689 100644 --- a/crypto/keys/secp256k1/secp256k1_cgo_test.go +++ b/crypto/keys/secp256k1/secp256k1_cgo_test.go @@ -4,7 +4,6 @@ package secp256k1 import ( - "fmt" "testing" "github.com/magiconair/properties/assert" @@ -36,7 +35,7 @@ func TestPrivKeySecp256k1SignVerify(t *testing.T) { require.NotNil(t, got) pub := tt.privKey.PubKey() - assert.Equal(t, tt.wantVerifyPasses, pub.VerifyBytes(msg, got)) + assert.Equal(t, tt.wantVerifyPasses, pub.VerifySignature(msg, got)) }) } } From 5eefc0c70027a9bc51a08c8e6694cd31ee3011ee Mon Sep 17 00:00:00 2001 From: marbar3778 Date: Tue, 1 Mar 2022 10:33:07 +0100 Subject: [PATCH 5/8] makefile & changelog --- CHANGELOG.md | 1 + Makefile | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d70f1886f28..b3b881314cd8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -197,6 +197,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * [\#10990](https://github.com/cosmos/cosmos-sdk/pull/10990) Fixes missing `iavl-cache-size` config parsing in `GetConfig` method. * (crypto) [#11027] Remove dependency on Tendermint core for xsalsa20symmetric. * (x/authz) [\#10447](https://github.com/cosmos/cosmos-sdk/pull/10447) Fix authz `NewGrant` expiration check. +* (crypto) [\#11298](https://github.com/cosmos/cosmos-sdk/pull/11298) Fic cgo secp signature verification and update libscep256k1 library. ### State Machine Breaking diff --git a/Makefile b/Makefile index 3ef8241658f6..730647e06c9b 100644 --- a/Makefile +++ b/Makefile @@ -50,6 +50,10 @@ ifeq (cleveldb,$(findstring cleveldb,$(COSMOS_BUILD_OPTIONS))) build_tags += gcc endif +ifeq (secp,$(findstring secp,$(COSMOS_BUILD_OPTIONS))) + build_tags += libsecp256k1_sdk +endif + whitespace := whitespace += $(whitespace) comma := , From aa158cc337f4aad11723b513412be6663f766fa5 Mon Sep 17 00:00:00 2001 From: marbar3778 Date: Wed, 2 Mar 2022 15:35:02 +0100 Subject: [PATCH 6/8] bring back readme --- crypto/keys/secp256k1/internal/secp256k1/README.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 crypto/keys/secp256k1/internal/secp256k1/README.md diff --git a/crypto/keys/secp256k1/internal/secp256k1/README.md b/crypto/keys/secp256k1/internal/secp256k1/README.md new file mode 100644 index 000000000000..5d811b8f0b34 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/README.md @@ -0,0 +1,5 @@ +# secp256k1 + + This package is copied from https://github.com/ethereum/go-ethereum/tree/8fddf27a989e246659fd018ea9be37b2b4f55326/crypto/secp256k1 + + Unlike the rest of go-ethereum it is MIT licensed so compatible with our Apache2.0 license. We opt to copy in here rather than depend on go-ethereum to avoid issues with vendoring of the GPL parts of that repository by downstream. From 9f64cb8067cc148be423d85df5605c46b9a9d80c Mon Sep 17 00:00:00 2001 From: Marko Date: Mon, 7 Mar 2022 11:32:49 +0100 Subject: [PATCH 7/8] Update CHANGELOG.md Co-authored-by: Ismail Khoffi --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc47cb0e793a..c0eb6e648d68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -214,7 +214,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * [#11222](https://github.com/cosmos/cosmos-sdk/pull/11222) reject query with block height in the future * [#11229](https://github.com/cosmos/cosmos-sdk/pull/11229) Handled the error message of `transaction encountered error` from tendermint. * (x/authz) [\#11252](https://github.com/cosmos/cosmos-sdk/pull/11252) Allow insufficient funds error for authz simulation -* (crypto) [\#11298](https://github.com/cosmos/cosmos-sdk/pull/11298) Fic cgo secp signature verification and update libscep256k1 library. +* (crypto) [\#11298](https://github.com/cosmos/cosmos-sdk/pull/11298) Fix cgo secp signature verification and update libscep256k1 library. ### State Machine Breaking From fcd446e50033fd46d6b65902432081ba78e738e6 Mon Sep 17 00:00:00 2001 From: marbar3778 Date: Mon, 7 Mar 2022 11:51:00 +0100 Subject: [PATCH 8/8] change license in sentence --- crypto/keys/secp256k1/internal/secp256k1/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/keys/secp256k1/internal/secp256k1/README.md b/crypto/keys/secp256k1/internal/secp256k1/README.md index 5d811b8f0b34..b621008dff71 100644 --- a/crypto/keys/secp256k1/internal/secp256k1/README.md +++ b/crypto/keys/secp256k1/internal/secp256k1/README.md @@ -2,4 +2,4 @@ This package is copied from https://github.com/ethereum/go-ethereum/tree/8fddf27a989e246659fd018ea9be37b2b4f55326/crypto/secp256k1 - Unlike the rest of go-ethereum it is MIT licensed so compatible with our Apache2.0 license. We opt to copy in here rather than depend on go-ethereum to avoid issues with vendoring of the GPL parts of that repository by downstream. + Unlike the rest of go-ethereum it is [3-clause BSD](https://opensource.org/licenses/BSD-3-Clause) licensed so compatible with our Apache2.0 license. We opt to copy in here rather than depend on go-ethereum to avoid issues with vendoring of the GPL parts of that repository by downstream.