* fix: cgosecp256k1 verification (#11298)
## Description
Closes: #10747
- update secp256k1 cgo fork,
- debug verify bytes
```
benchmark old ns/op new ns/op delta
BenchmarkKeyGeneration-10 407 413 +1.35%
BenchmarkSigning-10 95099 36754 -61.35%
BenchmarkVerification-10 215551 48053 -77.71%
benchmark old allocs new allocs delta
BenchmarkKeyGeneration-10 2 2 +0.00%
BenchmarkSigning-10 83 4 -95.18%
BenchmarkVerification-10 74 1 -98.65%
benchmark old bytes new bytes delta
BenchmarkKeyGeneration-10 96 96 +0.00%
BenchmarkSigning-10 5283 196 -96.29%
BenchmarkVerification-10 3537 32 -99.10%
```
---
### Author Checklist
*All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.*
I have...
- [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] added `!` to the type prefix if API or client breaking change
- [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting))
- [ ] provided a link to the relevant issue or specification
- [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules)
- [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing)
- [ ] added a changelog entry to `CHANGELOG.md`
- [ ] included comments for [documenting Go code](https://blog.golang.org/godoc)
- [ ] updated the relevant documentation or specification
- [ ] reviewed "Files changed" and left comments if necessary
- [ ] confirmed all CI checks have passed
### Reviewers Checklist
*All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.*
I have...
- [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] confirmed `!` in the type prefix if API or client breaking change
- [ ] confirmed all author checklist items have been addressed
- [ ] reviewed state machine logic
- [ ] reviewed API design and naming
- [ ] reviewed documentation is accurate
- [ ] reviewed tests and test coverage
- [ ] manually tested (if applicable)
(cherry picked from commit 361c837e52
)
# Conflicts:
# CHANGELOG.md
# crypto/keys/secp256k1/internal/secp256k1/README.md
* fix conflicts
Co-authored-by: Marko <marbar3778@yahoo.com>
This commit is contained in:
parent
b073f35f82
commit
a4644a3797
|
@ -51,6 +51,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
|||
* (store) [\#11177](https://github.com/cosmos/cosmos-sdk/pull/11177) Update the prune `everything` strategy to store the last two heights.
|
||||
* (store) [\#11117](https://github.com/cosmos/cosmos-sdk/pull/11117) Fix data race in store trace component
|
||||
* (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) Fix cgo secp signature verification and update libscep256k1 library.
|
||||
|
||||
### Improvements
|
||||
|
||||
|
|
4
Makefile
4
Makefile
|
@ -46,6 +46,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 := ,
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
This package is copied from https://github.com/ethereum/go-ethereum/tree/729bf365b5f17325be9107b63b233da54100eec6/crypto/secp256k1
|
||||
# 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.
|
||||
This package is copied from https://github.com/ethereum/go-ethereum/tree/8fddf27a989e246659fd018ea9be37b2b4f55326/crypto/secp256k1
|
||||
|
||||
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.
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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"
|
||||
)
|
|
@ -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.
|
||||
|
@ -55,8 +54,8 @@ 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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
// +build libsecp256k1
|
||||
//go:build libsecp256k1_sdk
|
||||
// +build libsecp256k1_sdk
|
||||
|
||||
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, sigStr []byte) bool {
|
||||
return secp256k1.VerifySignature(pubKey.Bytes(), crypto.Sha256(msg), sigStr)
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
@ -34,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))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//go:build !libsecp256k1
|
||||
// +build !libsecp256k1
|
||||
//go:build !libsecp256k1_sdk
|
||||
// +build !libsecp256k1_sdk
|
||||
|
||||
package secp256k1
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// +build !libsecp256k1
|
||||
//go:build !libsecp256k1_sdk
|
||||
// +build !libsecp256k1_sdk
|
||||
|
||||
package secp256k1
|
||||
|
||||
|
|
Loading…
Reference in New Issue