crypto: refactor for stargate (#559)

* changelog

* update changelog

* crypto: refactor for stargate

* fixes

* fix keys

* changelog
This commit is contained in:
Federico Kunze
2020-10-06 20:57:55 +02:00
committed by GitHub
parent e7a19a1c0a
commit 4e01da905a
32 changed files with 165 additions and 146 deletions
+27
View File
@@ -0,0 +1,27 @@
package ethsecp256k1
import (
cryptoamino "github.com/tendermint/tendermint/crypto/encoding/amino"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/crypto/keys"
)
// CryptoCodec is the default amino codec used by ethermint
var CryptoCodec = codec.New()
func init() {
// replace the keyring codec with the ethermint crypto codec to prevent
// amino panics because of unregistered Priv/PubKey
keys.CryptoCdc = CryptoCodec
keys.RegisterCodec(CryptoCodec)
cryptoamino.RegisterAmino(CryptoCodec)
RegisterCodec(CryptoCodec)
}
// RegisterCodec registers all the necessary types with amino for the given
// codec.
func RegisterCodec(cdc *codec.Codec) {
cdc.RegisterConcrete(PubKey{}, PubKeyName, nil)
cdc.RegisterConcrete(PrivKey{}, PrivKeyName, nil)
}
+135
View File
@@ -0,0 +1,135 @@
package ethsecp256k1
import (
"bytes"
"crypto/ecdsa"
ethcrypto "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/secp256k1"
tmcrypto "github.com/tendermint/tendermint/crypto"
)
const (
// PrivKeySize defines the size of the PrivKey bytes
PrivKeySize = 32
// KeyType is the string constant for the EthSecp256k1 algorithm
KeyType = "eth_secp256k1"
)
// Amino encoding names
const (
// PrivKeyName defines the amino encoding name for the EthSecp256k1 private key
PrivKeyName = "ethermint/PrivKeyEthSecp256k1"
// PubKeyName defines the amino encoding name for the EthSecp256k1 public key
PubKeyName = "ethermint/PubKeyEthSecp256k1"
)
// ----------------------------------------------------------------------------
// secp256k1 Private Key
var _ tmcrypto.PrivKey = PrivKey{}
// PrivKey defines a type alias for an ecdsa.PrivateKey that implements
// Tendermint's PrivateKey interface.
type PrivKey []byte
// GenerateKey generates a new random private key. It returns an error upon
// failure.
func GenerateKey() (PrivKey, error) {
priv, err := ethcrypto.GenerateKey()
if err != nil {
return PrivKey{}, err
}
return PrivKey(ethcrypto.FromECDSA(priv)), nil
}
// PubKey returns the ECDSA private key's public key.
func (privkey PrivKey) PubKey() tmcrypto.PubKey {
ecdsaPKey := privkey.ToECDSA()
return PubKey(ethcrypto.CompressPubkey(&ecdsaPKey.PublicKey))
}
// Bytes returns the raw ECDSA private key bytes.
func (privkey PrivKey) Bytes() []byte {
return CryptoCodec.MustMarshalBinaryBare(privkey)
}
// Sign creates a recoverable ECDSA signature on the secp256k1 curve over the
// Keccak256 hash of the provided message. The produced signature is 65 bytes
// where the last byte contains the recovery ID.
func (privkey PrivKey) Sign(msg []byte) ([]byte, error) {
return ethcrypto.Sign(ethcrypto.Keccak256Hash(msg).Bytes(), privkey.ToECDSA())
}
// Equals returns true if two ECDSA private keys are equal and false otherwise.
func (privkey PrivKey) Equals(other tmcrypto.PrivKey) bool {
if other, ok := other.(PrivKey); ok {
return bytes.Equal(privkey.Bytes(), other.Bytes())
}
return false
}
// ToECDSA returns the ECDSA private key as a reference to ecdsa.PrivateKey type.
// The function will panic if the private key is invalid.
func (privkey PrivKey) ToECDSA() *ecdsa.PrivateKey {
key, err := ethcrypto.ToECDSA(privkey)
if err != nil {
panic(err)
}
return key
}
// ----------------------------------------------------------------------------
// secp256k1 Public Key
var _ tmcrypto.PubKey = (*PubKey)(nil)
// PubKey defines a type alias for an ecdsa.PublicKey that implements Tendermint's PubKey
// interface. It represents the 33-byte compressed public key format.
type PubKey []byte
// Address returns the address of the ECDSA public key.
// The function will panic if the public key is invalid.
func (key PubKey) Address() tmcrypto.Address {
pubk, err := ethcrypto.DecompressPubkey(key)
if err != nil {
panic(err)
}
return tmcrypto.Address(ethcrypto.PubkeyToAddress(*pubk).Bytes())
}
// Bytes returns the raw bytes of the ECDSA public key.
// The function panics if the key cannot be marshaled to bytes.
func (key PubKey) Bytes() []byte {
bz, err := CryptoCodec.MarshalBinaryBare(key)
if err != nil {
panic(err)
}
return bz
}
// VerifyBytes verifies that the ECDSA public key created a given signature over
// the provided message. It will calculate the Keccak256 hash of the message
// prior to verification.
func (key PubKey) VerifyBytes(msg []byte, sig []byte) bool {
if len(sig) == 65 {
// remove recovery ID if contained in the signature
sig = sig[:len(sig)-1]
}
// the signature needs to be in [R || S] format when provided to VerifySignature
return secp256k1.VerifySignature(key, ethcrypto.Keccak256Hash(msg).Bytes(), sig)
}
// Equals returns true if two ECDSA public keys are equal and false otherwise.
func (key PubKey) Equals(other tmcrypto.PubKey) bool {
if other, ok := other.(PubKey); ok {
return bytes.Equal(key.Bytes(), other.Bytes())
}
return false
}
+61
View File
@@ -0,0 +1,61 @@
package ethsecp256k1
import (
"testing"
"github.com/stretchr/testify/require"
ethcrypto "github.com/ethereum/go-ethereum/crypto"
ethsecp256k1 "github.com/ethereum/go-ethereum/crypto/secp256k1"
tmcrypto "github.com/tendermint/tendermint/crypto"
)
func TestPrivKeyPrivKey(t *testing.T) {
// validate type and equality
privKey, err := GenerateKey()
require.NoError(t, err)
require.True(t, privKey.Equals(privKey))
require.Implements(t, (*tmcrypto.PrivKey)(nil), privKey)
// validate inequality
privKey2, err := GenerateKey()
require.NoError(t, err)
require.False(t, privKey.Equals(privKey2))
// validate Ethereum address equality
addr := privKey.PubKey().Address()
expectedAddr := ethcrypto.PubkeyToAddress(privKey.ToECDSA().PublicKey)
require.Equal(t, expectedAddr.Bytes(), addr.Bytes())
// validate we can sign some bytes
msg := []byte("hello world")
sigHash := ethcrypto.Keccak256Hash(msg)
expectedSig, _ := ethsecp256k1.Sign(sigHash.Bytes(), privKey)
sig, err := privKey.Sign(msg)
require.NoError(t, err)
require.Equal(t, expectedSig, sig)
}
func TestPrivKeyPubKey(t *testing.T) {
privKey, err := GenerateKey()
require.NoError(t, err)
// validate type and equality
pubKey := privKey.PubKey().(PubKey)
require.Implements(t, (*tmcrypto.PubKey)(nil), pubKey)
// validate inequality
privKey2, err := GenerateKey()
require.NoError(t, err)
require.False(t, pubKey.Equals(privKey2.PubKey()))
// validate signature
msg := []byte("hello world")
sig, err := privKey.Sign(msg)
require.NoError(t, err)
res := pubKey.VerifyBytes(msg, sig)
require.True(t, res)
}