Use embedded lib key type in sdk pub keys instead of bytes. (#7672)

* Remove duplicate print message on keys add command (#7654)

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>

* ed25519: use stdlib/crypto types

* use standard package testing

* use crypto/ed25519 instead of golang.org/x/crypto/ed25519

In Go 1.13 the new crypto/ed25519 package implements the Ed25519 signature scheme.
This functionality was previously provided by the golang.org/x/crypto/ed25519 package,
which becomes a wrapper for crypto/ed25519 when used with Go 1.13+.

* use standard package testing for secp256k1 tests

* secp256k1: add cross packages signature checks

* ed25519: rollback the _test package name

* rename underlyingSecp256k1 to btcSecp256k1

* package update

Co-authored-by: Denis Fadeev <denis@fadeev.org>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
This commit is contained in:
Robert Zaremba 2020-10-28 11:24:41 +01:00 committed by GitHub
parent 426d195387
commit bd9af94174
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 668 additions and 635 deletions

View File

@ -1,6 +1,7 @@
package ed25519
import (
"crypto/ed25519"
"crypto/subtle"
"fmt"
"io"
@ -8,7 +9,6 @@ import (
"github.com/tendermint/tendermint/crypto"
tmed25519 "github.com/tendermint/tendermint/crypto/ed25519"
"github.com/tendermint/tendermint/crypto/tmhash"
"golang.org/x/crypto/ed25519"
"github.com/cosmos/cosmos-sdk/codec"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
@ -50,7 +50,7 @@ func (privKey *PrivKey) Bytes() []byte {
// If these conditions aren't met, Sign will panic or produce an
// incorrect signature.
func (privKey *PrivKey) Sign(msg []byte) ([]byte, error) {
return ed25519.Sign(ed25519.PrivateKey(privKey.Key), msg), nil
return ed25519.Sign(privKey.Key, msg), nil
}
// PubKey gets the corresponding public key from the private key.
@ -171,7 +171,7 @@ func (pubKey *PubKey) VerifySignature(msg []byte, sig []byte) bool {
return false
}
return ed25519.Verify(ed25519.PublicKey(pubKey.Key), msg, sig)
return ed25519.Verify(pubKey.Key, msg, sig)
}
func (pubKey *PubKey) String() string {

View File

@ -1,6 +1,7 @@
package ed25519_test
import (
stded25519 "crypto/ed25519"
"encoding/base64"
"testing"
@ -11,7 +12,7 @@ import (
"github.com/tendermint/tendermint/crypto/sr25519"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
ed25519 "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
)
@ -19,17 +20,28 @@ func TestSignAndValidateEd25519(t *testing.T) {
privKey := ed25519.GenPrivKey()
pubKey := privKey.PubKey()
msg := crypto.CRandBytes(128)
msg := crypto.CRandBytes(1000)
sig, err := privKey.Sign(msg)
require.Nil(t, err)
// Test the signature
assert.True(t, pubKey.VerifySignature(msg, sig))
// ----
// Test cross packages verification
stdPrivKey := stded25519.PrivateKey(privKey.Key)
stdPubKey := stdPrivKey.Public().(stded25519.PublicKey)
assert.Equal(t, stdPubKey, pubKey.(*ed25519.PubKey).Key)
assert.Equal(t, stdPrivKey, privKey.Key)
assert.True(t, stded25519.Verify(stdPubKey, msg, sig))
sig2 := stded25519.Sign(stdPrivKey, msg)
assert.True(t, pubKey.VerifySignature(msg, sig2))
// ----
// Mutate the signature, just one bit.
// TODO: Replace this with a much better fuzzer, tendermint/ed25519/issues/10
sig[7] ^= byte(0x01)
assert.False(t, pubKey.VerifySignature(msg, sig))
}

View File

@ -4,6 +4,7 @@
package ed25519
import (
crypto_ed25519 "crypto/ed25519"
fmt "fmt"
_ "github.com/gogo/protobuf/gogoproto"
proto "github.com/gogo/protobuf/proto"
@ -29,7 +30,7 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
// the x-coordinate. Otherwise the first byte is a 0x03.
// This prefix is followed with the x-coordinate.
type PubKey struct {
Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
Key crypto_ed25519.PublicKey `protobuf:"bytes,1,opt,name=key,proto3,casttype=crypto/ed25519.PublicKey" json:"key,omitempty"`
}
func (m *PubKey) Reset() { *m = PubKey{} }
@ -64,7 +65,7 @@ func (m *PubKey) XXX_DiscardUnknown() {
var xxx_messageInfo_PubKey proto.InternalMessageInfo
func (m *PubKey) GetKey() []byte {
func (m *PubKey) GetKey() crypto_ed25519.PublicKey {
if m != nil {
return m.Key
}
@ -73,7 +74,7 @@ func (m *PubKey) GetKey() []byte {
// PrivKey defines a ed25519 private key.
type PrivKey struct {
Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
Key crypto_ed25519.PrivateKey `protobuf:"bytes,1,opt,name=key,proto3,casttype=crypto/ed25519.PrivateKey" json:"key,omitempty"`
}
func (m *PrivKey) Reset() { *m = PrivKey{} }
@ -109,7 +110,7 @@ func (m *PrivKey) XXX_DiscardUnknown() {
var xxx_messageInfo_PrivKey proto.InternalMessageInfo
func (m *PrivKey) GetKey() []byte {
func (m *PrivKey) GetKey() crypto_ed25519.PrivateKey {
if m != nil {
return m.Key
}
@ -124,19 +125,21 @@ func init() {
func init() { proto.RegisterFile("cosmos/crypto/ed25519/keys.proto", fileDescriptor_48fe3336771e732d) }
var fileDescriptor_48fe3336771e732d = []byte{
// 183 bytes of a gzipped FileDescriptorProto
// 221 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x48, 0xce, 0x2f, 0xce,
0xcd, 0x2f, 0xd6, 0x4f, 0x2e, 0xaa, 0x2c, 0x28, 0xc9, 0xd7, 0x4f, 0x4d, 0x31, 0x32, 0x35, 0x35,
0xb4, 0xd4, 0xcf, 0x4e, 0xad, 0x2c, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x85, 0xa8,
0xd0, 0x83, 0xa8, 0xd0, 0x83, 0xaa, 0x90, 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0xab, 0xd0, 0x07,
0xb1, 0x20, 0x8a, 0x95, 0x14, 0xb8, 0xd8, 0x02, 0x4a, 0x93, 0xbc, 0x53, 0x2b, 0x85, 0x04, 0xb8,
0x98, 0xb3, 0x53, 0x2b, 0x25, 0x18, 0x15, 0x18, 0x35, 0x78, 0x82, 0x40, 0x4c, 0x2b, 0x96, 0x19,
0x0b, 0xe4, 0x19, 0x94, 0xa4, 0xb9, 0xd8, 0x03, 0x8a, 0x32, 0xcb, 0xb0, 0x2a, 0x71, 0xf2, 0x3a,
0xf1, 0x48, 0x8e, 0xf1, 0xc2, 0x23, 0x39, 0xc6, 0x07, 0x8f, 0xe4, 0x18, 0x27, 0x3c, 0x96, 0x63,
0xb8, 0xf0, 0x58, 0x8e, 0xe1, 0xc6, 0x63, 0x39, 0x86, 0x28, 0x83, 0xf4, 0xcc, 0x92, 0x8c, 0xd2,
0x24, 0xbd, 0xe4, 0xfc, 0x5c, 0x7d, 0x98, 0x93, 0xc1, 0x94, 0x6e, 0x71, 0x4a, 0x36, 0xcc, 0xf5,
0x20, 0x57, 0xc3, 0xbc, 0x90, 0xc4, 0x06, 0x76, 0x91, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0xcc,
0xf3, 0x11, 0x99, 0xe2, 0x00, 0x00, 0x00,
0xb1, 0x20, 0x8a, 0x95, 0xec, 0xb8, 0xd8, 0x02, 0x4a, 0x93, 0xbc, 0x53, 0x2b, 0x85, 0xf4, 0xb8,
0x98, 0xb3, 0x53, 0x2b, 0x25, 0x18, 0x15, 0x18, 0x35, 0x78, 0x9c, 0x64, 0x7e, 0xdd, 0x93, 0x97,
0x40, 0xb5, 0x42, 0x2f, 0xa0, 0x34, 0x29, 0x27, 0x33, 0xd9, 0x3b, 0xb5, 0x32, 0x08, 0xa4, 0xd0,
0x8a, 0x65, 0xc6, 0x02, 0x79, 0x06, 0x25, 0x2b, 0x2e, 0xf6, 0x80, 0xa2, 0xcc, 0x32, 0x90, 0x01,
0xfa, 0xc8, 0x06, 0xc8, 0xfe, 0xba, 0x27, 0x2f, 0x89, 0x6e, 0x40, 0x51, 0x66, 0x59, 0x62, 0x49,
0x2a, 0xcc, 0x04, 0x27, 0xaf, 0x13, 0x8f, 0xe4, 0x18, 0x2f, 0x3c, 0x92, 0x63, 0x7c, 0xf0, 0x48,
0x8e, 0x71, 0xc2, 0x63, 0x39, 0x86, 0x0b, 0x8f, 0xe5, 0x18, 0x6e, 0x3c, 0x96, 0x63, 0x88, 0x32,
0x48, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x87, 0xf9, 0x17, 0x4c, 0xe9,
0x16, 0xa7, 0x64, 0xc3, 0xbc, 0x0e, 0xf2, 0x32, 0xcc, 0xec, 0x24, 0x36, 0xb0, 0x77, 0x8c, 0x01,
0x01, 0x00, 0x00, 0xff, 0xff, 0xb0, 0xd8, 0x01, 0xc0, 0x1f, 0x01, 0x00, 0x00,
}
func (m *PubKey) Marshal() (dAtA []byte, err error) {

View File

@ -9,13 +9,12 @@ import (
"math/big"
secp256k1 "github.com/btcsuite/btcd/btcec"
"github.com/tendermint/tendermint/crypto"
"golang.org/x/crypto/ripemd160" // nolint: staticcheck // necessary for Bitcoin address format
"github.com/cosmos/cosmos-sdk/codec"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
"github.com/cosmos/cosmos-sdk/types/errors"
"github.com/tendermint/tendermint/crypto"
)
var _ cryptotypes.PrivKey = &PrivKey{}

View File

@ -19,6 +19,8 @@ func (privKey *PrivKey) Sign(msg []byte) ([]byte, error) {
return rs, nil
}
// 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)
}

View File

@ -5,9 +5,8 @@ import (
"math/big"
"testing"
btcSecp256k1 "github.com/btcsuite/btcd/btcec"
"github.com/stretchr/testify/require"
underlyingSecp256k1 "github.com/btcsuite/btcd/btcec"
)
func Test_genPrivKey(t *testing.T) {
@ -25,7 +24,7 @@ func Test_genPrivKey(t *testing.T) {
shouldPanic bool
}{
{"empty bytes (panics because 1st 32 bytes are zero and 0 is not a valid field element)", empty, true},
{"curve order: N", underlyingSecp256k1.S256().N.Bytes(), true},
{"curve order: N", btcSecp256k1.S256().N.Bytes(), true},
{"valid because 0 < 1 < N", validOne, false},
}
for _, tt := range tests {
@ -39,7 +38,7 @@ func Test_genPrivKey(t *testing.T) {
}
got := genPrivKey(bytes.NewReader(tt.notSoRand))
fe := new(big.Int).SetBytes(got[:])
require.True(t, fe.Cmp(underlyingSecp256k1.S256().N) < 0)
require.True(t, fe.Cmp(btcSecp256k1.S256().N) < 0)
require.True(t, fe.Sign() > 0)
})
}

View File

@ -1,20 +1,19 @@
package secp256k1_test
import (
"crypto/ecdsa"
"encoding/base64"
"encoding/hex"
"math/big"
"testing"
btcSecp256k1 "github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcutil/base58"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/sr25519"
underlyingSecp256k1 "github.com/btcsuite/btcd/btcec"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
@ -41,7 +40,7 @@ func TestPubKeySecp256k1Address(t *testing.T) {
addrBbz, _, _ := base58.CheckDecode(d.addr)
addrB := crypto.Address(addrBbz)
var priv secp256k1.PrivKey = secp256k1.PrivKey{Key: privB}
var priv = secp256k1.PrivKey{Key: privB}
pubKey := priv.PubKey()
pubT, _ := pubKey.(*secp256k1.PubKey)
@ -56,15 +55,34 @@ func TestSignAndValidateSecp256k1(t *testing.T) {
privKey := secp256k1.GenPrivKey()
pubKey := privKey.PubKey()
msg := crypto.CRandBytes(128)
msg := crypto.CRandBytes(1000)
sig, err := privKey.Sign(msg)
require.Nil(t, err)
assert.True(t, pubKey.VerifySignature(msg, sig))
// ----
// Test cross packages verification
msgHash := crypto.Sha256(msg)
btcPrivKey, btcPubKey := btcSecp256k1.PrivKeyFromBytes(btcSecp256k1.S256(), privKey.Key)
// This fails: malformed signature: no header magic
// btcSig, err := secp256k1.ParseSignature(sig, secp256k1.S256())
// require.NoError(t, err)
// assert.True(t, btcSig.Verify(msgHash, btcPubKey))
// So we do a hacky way:
r := new(big.Int)
s := new(big.Int)
r.SetBytes(sig[:32])
s.SetBytes(sig[32:])
ok := ecdsa.Verify(btcPubKey.ToECDSA(), msgHash, r, s)
require.True(t, ok)
sig2, err := btcPrivKey.Sign(msgHash)
require.NoError(t, err)
pubKey.VerifySignature(msg, sig2.Serialize())
// ----
// Mutate the signature, just one bit.
sig[3] ^= byte(0x01)
assert.False(t, pubKey.VerifySignature(msg, sig))
}
@ -79,7 +97,7 @@ func TestSecp256k1LoadPrivkeyAndSerializeIsIdentity(t *testing.T) {
// This function creates a private and public key in the underlying libraries format.
// The private key is basically calling new(big.Int).SetBytes(pk), which removes leading zero bytes
priv, _ := underlyingSecp256k1.PrivKeyFromBytes(underlyingSecp256k1.S256(), privKeyBytes[:])
priv, _ := btcSecp256k1.PrivKeyFromBytes(btcSecp256k1.S256(), privKeyBytes[:])
// this takes the bytes returned by `(big int).Bytes()`, and if the length is less than 32 bytes,
// pads the bytes from the left with zero bytes. Therefore these two functions composed
// result in the identity function on privKeyBytes, hence the following equality check
@ -91,7 +109,7 @@ func TestSecp256k1LoadPrivkeyAndSerializeIsIdentity(t *testing.T) {
func TestGenPrivKeyFromSecret(t *testing.T) {
// curve oder N
N := underlyingSecp256k1.S256().N
N := btcSecp256k1.S256().N
tests := []struct {
name string
secret []byte

View File

@ -13,10 +13,10 @@ option go_package = "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519";
message PubKey {
option (gogoproto.goproto_stringer) = false;
bytes key = 1;
bytes key = 1 [(gogoproto.casttype) = "crypto/ed25519.PublicKey"];
}
// PrivKey defines a ed25519 private key.
message PrivKey {
bytes key = 1;
bytes key = 1 [(gogoproto.casttype) = "crypto/ed25519.PrivateKey"];
}

File diff suppressed because it is too large Load Diff