laconicd-deprecated/crypto/ethsecp256k1/ethsecp256k1.go

236 lines
6.9 KiB
Go
Raw Normal View History

package ethsecp256k1
import (
"bytes"
"crypto/ecdsa"
2021-04-17 10:00:07 +00:00
"crypto/subtle"
"fmt"
2021-04-17 10:00:07 +00:00
"github.com/cosmos/cosmos-sdk/codec"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
2021-09-03 18:06:36 +00:00
"github.com/ethereum/go-ethereum/crypto"
Refactor EIP-712 signature verification (#1397) * [WIP] EIP-712 Signature Refactor * Debug and add ante tests * Add tests for failure cases * Add changelog entry * Code cleanup * Add tests for MsgDelegate and MsgWithdrawDelegationReward * Update ethereum/eip712/encoding.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Update ethereum/eip712/encoding.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Update ethereum/eip712/encoding.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Update ethereum/eip712/encoding.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Update ethereum/eip712/encoding.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Update ethereum/eip712/encoding.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Code cleanup * Update ethereum/eip712/encoding.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Minor codefix * Update ethereum/eip712/encoding.go * Minor code revision updates * Refactor EIP712 unit tests to use test suite * Address import cycle and implement minor refactors * Fix lint issues * Add EIP712 unit suite test function * Update ethereum/eip712/encoding.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Update ethereum/eip712/encoding.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Update ethereum/eip712/encoding.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Add minor refactors; increase test coverage * Correct ante_test for change in payload * Add single-signer util and tests * Update ethereum/eip712/encoding.go * Update ethereum/eip712/encoding.go * fix build Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> Co-authored-by: Freddy Caceres <facs95@gmail.com>
2022-11-07 16:50:25 +00:00
"github.com/evmos/ethermint/ethereum/eip712"
tmcrypto "github.com/tendermint/tendermint/crypto"
)
const (
// PrivKeySize defines the size of the PrivKey bytes
PrivKeySize = 32
2021-04-17 10:00:07 +00:00
// PubKeySize defines the size of the PubKey bytes
PubKeySize = 33
// KeyType is the string constant for the Secp256k1 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
2021-04-17 10:00:07 +00:00
var (
_ cryptotypes.PrivKey = &PrivKey{}
_ codec.AminoMarshaler = &PrivKey{}
)
// GenerateKey generates a new random private key. It returns an error upon
// failure.
2021-04-17 10:00:07 +00:00
func GenerateKey() (*PrivKey, error) {
2021-09-03 18:06:36 +00:00
priv, err := crypto.GenerateKey()
if err != nil {
2021-04-17 10:00:07 +00:00
return nil, err
}
2021-04-17 10:00:07 +00:00
return &PrivKey{
2021-09-03 18:06:36 +00:00
Key: crypto.FromECDSA(priv),
2021-04-17 10:00:07 +00:00
}, nil
}
// Bytes returns the byte representation of the ECDSA Private Key.
func (privKey PrivKey) Bytes() []byte {
2021-09-07 17:29:24 +00:00
bz := make([]byte, len(privKey.Key))
copy(bz, privKey.Key)
return bz
}
2021-09-07 17:29:24 +00:00
// PubKey returns the ECDSA private key's public key. If the privkey is not valid
// it returns a nil value.
2021-04-17 10:00:07 +00:00
func (privKey PrivKey) PubKey() cryptotypes.PubKey {
2021-09-07 17:29:24 +00:00
ecdsaPrivKey, err := privKey.ToECDSA()
if err != nil {
return nil
}
2021-04-17 10:00:07 +00:00
return &PubKey{
2021-09-03 18:06:36 +00:00
Key: crypto.CompressPubkey(&ecdsaPrivKey.PublicKey),
2021-04-17 10:00:07 +00:00
}
}
2021-04-17 10:00:07 +00:00
// Equals returns true if two ECDSA private keys are equal and false otherwise.
func (privKey PrivKey) Equals(other cryptotypes.LedgerPrivKey) bool {
return privKey.Type() == other.Type() && subtle.ConstantTimeCompare(privKey.Bytes(), other.Bytes()) == 1
}
2021-04-17 10:00:07 +00:00
// Type returns eth_secp256k1
func (privKey PrivKey) Type() string {
return KeyType
}
2021-08-26 15:45:45 +00:00
// MarshalAmino overrides Amino binary marshaling.
2021-04-17 10:00:07 +00:00
func (privKey PrivKey) MarshalAmino() ([]byte, error) {
return privKey.Key, nil
}
2021-08-26 15:45:45 +00:00
// UnmarshalAmino overrides Amino binary marshaling.
2021-04-17 10:00:07 +00:00
func (privKey *PrivKey) UnmarshalAmino(bz []byte) error {
if len(bz) != PrivKeySize {
return fmt.Errorf("invalid privkey size, expected %d got %d", PrivKeySize, len(bz))
}
2021-04-17 10:00:07 +00:00
privKey.Key = bz
return nil
}
2021-08-26 15:45:45 +00:00
// MarshalAminoJSON overrides Amino JSON marshaling.
2021-04-17 10:00:07 +00:00
func (privKey PrivKey) MarshalAminoJSON() ([]byte, error) {
// When we marshal to Amino JSON, we don't marshal the "key" field itself,
// just its contents (i.e. the key bytes).
return privKey.MarshalAmino()
}
2021-08-26 15:45:45 +00:00
// UnmarshalAminoJSON overrides Amino JSON marshaling.
2021-04-17 10:00:07 +00:00
func (privKey *PrivKey) UnmarshalAminoJSON(bz []byte) error {
return privKey.UnmarshalAmino(bz)
}
// Sign creates a recoverable ECDSA signature on the secp256k1 curve over the
// provided hash of the message. The produced signature is 65 bytes
2021-04-17 10:00:07 +00:00
// where the last byte contains the recovery ID.
func (privKey PrivKey) Sign(digestBz []byte) ([]byte, error) {
2021-09-07 17:29:24 +00:00
// TODO: remove
2021-09-03 18:06:36 +00:00
if len(digestBz) != crypto.DigestLength {
digestBz = crypto.Keccak256Hash(digestBz).Bytes()
}
2021-09-07 17:29:24 +00:00
key, err := privKey.ToECDSA()
if err != nil {
return nil, err
}
return crypto.Sign(digestBz, key)
}
// ToECDSA returns the ECDSA private key as a reference to ecdsa.PrivateKey type.
2021-09-07 17:29:24 +00:00
func (privKey PrivKey) ToECDSA() (*ecdsa.PrivateKey, error) {
return crypto.ToECDSA(privKey.Bytes())
}
// ----------------------------------------------------------------------------
// secp256k1 Public Key
2021-04-17 10:00:07 +00:00
var (
_ cryptotypes.PubKey = &PubKey{}
_ codec.AminoMarshaler = &PubKey{}
)
// Address returns the address of the ECDSA public key.
2021-09-07 17:29:24 +00:00
// The function will return an empty address if the public key is invalid.
2021-04-17 10:00:07 +00:00
func (pubKey PubKey) Address() tmcrypto.Address {
2021-09-03 18:06:36 +00:00
pubk, err := crypto.DecompressPubkey(pubKey.Key)
if err != nil {
2021-09-07 17:29:24 +00:00
return nil
}
2021-09-03 18:06:36 +00:00
return tmcrypto.Address(crypto.PubkeyToAddress(*pubk).Bytes())
}
// Bytes returns the raw bytes of the ECDSA public key.
2021-04-17 10:00:07 +00:00
func (pubKey PubKey) Bytes() []byte {
2021-09-07 17:29:24 +00:00
bz := make([]byte, len(pubKey.Key))
copy(bz, pubKey.Key)
return bz
2021-04-17 10:00:07 +00:00
}
// String implements the fmt.Stringer interface.
func (pubKey PubKey) String() string {
return fmt.Sprintf("EthPubKeySecp256k1{%X}", pubKey.Key)
}
// Type returns eth_secp256k1
func (pubKey PubKey) Type() string {
return KeyType
}
// Equals returns true if the pubkey type is the same and their bytes are deeply equal.
func (pubKey PubKey) Equals(other cryptotypes.PubKey) bool {
return pubKey.Type() == other.Type() && bytes.Equal(pubKey.Bytes(), other.Bytes())
}
2021-08-26 15:45:45 +00:00
// MarshalAmino overrides Amino binary marshaling.
2021-04-17 10:00:07 +00:00
func (pubKey PubKey) MarshalAmino() ([]byte, error) {
return pubKey.Key, nil
}
2021-08-26 15:45:45 +00:00
// UnmarshalAmino overrides Amino binary marshaling.
2021-04-17 10:00:07 +00:00
func (pubKey *PubKey) UnmarshalAmino(bz []byte) error {
if len(bz) != PubKeySize {
return sdkerrors.Wrapf(sdkerrors.ErrInvalidPubKey, "invalid pubkey size, expected %d, got %d", PubKeySize, len(bz))
}
2021-04-17 10:00:07 +00:00
pubKey.Key = bz
return nil
}
2021-08-26 15:45:45 +00:00
// MarshalAminoJSON overrides Amino JSON marshaling.
2021-04-17 10:00:07 +00:00
func (pubKey PubKey) MarshalAminoJSON() ([]byte, error) {
// When we marshal to Amino JSON, we don't marshal the "key" field itself,
// just its contents (i.e. the key bytes).
return pubKey.MarshalAmino()
}
2021-08-26 15:45:45 +00:00
// UnmarshalAminoJSON overrides Amino JSON marshaling.
2021-04-17 10:00:07 +00:00
func (pubKey *PubKey) UnmarshalAminoJSON(bz []byte) error {
return pubKey.UnmarshalAmino(bz)
}
// VerifySignature verifies that the ECDSA public key created a given signature over
// the provided message. It will calculate the Keccak256 hash of the message
Refactor EIP-712 signature verification (#1397) * [WIP] EIP-712 Signature Refactor * Debug and add ante tests * Add tests for failure cases * Add changelog entry * Code cleanup * Add tests for MsgDelegate and MsgWithdrawDelegationReward * Update ethereum/eip712/encoding.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Update ethereum/eip712/encoding.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Update ethereum/eip712/encoding.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Update ethereum/eip712/encoding.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Update ethereum/eip712/encoding.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Update ethereum/eip712/encoding.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Code cleanup * Update ethereum/eip712/encoding.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Minor codefix * Update ethereum/eip712/encoding.go * Minor code revision updates * Refactor EIP712 unit tests to use test suite * Address import cycle and implement minor refactors * Fix lint issues * Add EIP712 unit suite test function * Update ethereum/eip712/encoding.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Update ethereum/eip712/encoding.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Update ethereum/eip712/encoding.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Add minor refactors; increase test coverage * Correct ante_test for change in payload * Add single-signer util and tests * Update ethereum/eip712/encoding.go * Update ethereum/eip712/encoding.go * fix build Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> Co-authored-by: Freddy Caceres <facs95@gmail.com>
2022-11-07 16:50:25 +00:00
// prior to verification and approve verification if the signature can be verified
// from either the original message or its EIP-712 representation.
//
// CONTRACT: The signature should be in [R || S] format.
func (pubKey PubKey) VerifySignature(msg, sig []byte) bool {
Refactor EIP-712 signature verification (#1397) * [WIP] EIP-712 Signature Refactor * Debug and add ante tests * Add tests for failure cases * Add changelog entry * Code cleanup * Add tests for MsgDelegate and MsgWithdrawDelegationReward * Update ethereum/eip712/encoding.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Update ethereum/eip712/encoding.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Update ethereum/eip712/encoding.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Update ethereum/eip712/encoding.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Update ethereum/eip712/encoding.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Update ethereum/eip712/encoding.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Code cleanup * Update ethereum/eip712/encoding.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Minor codefix * Update ethereum/eip712/encoding.go * Minor code revision updates * Refactor EIP712 unit tests to use test suite * Address import cycle and implement minor refactors * Fix lint issues * Add EIP712 unit suite test function * Update ethereum/eip712/encoding.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Update ethereum/eip712/encoding.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Update ethereum/eip712/encoding.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * Add minor refactors; increase test coverage * Correct ante_test for change in payload * Add single-signer util and tests * Update ethereum/eip712/encoding.go * Update ethereum/eip712/encoding.go * fix build Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> Co-authored-by: Freddy Caceres <facs95@gmail.com>
2022-11-07 16:50:25 +00:00
return pubKey.verifySignatureECDSA(msg, sig) || pubKey.verifySignatureAsEIP712(msg, sig)
}
// Verifies the signature as an EIP-712 signature by first converting the message payload
// to an EIP-712 hashed object, performing ECDSA verification on the hash. This is to support
// signing a Cosmos payload using EIP-712.
func (pubKey PubKey) verifySignatureAsEIP712(msg, sig []byte) bool {
eip712Hash, err := eip712.GetEIP712HashForMsg(msg)
if err != nil {
return false
}
return pubKey.verifySignatureECDSA(eip712Hash, sig)
}
// Perform standard ECDSA signature verification for the given raw bytes and signature.
func (pubKey PubKey) verifySignatureECDSA(msg, sig []byte) bool {
2021-09-03 18:06:36 +00:00
if len(sig) == crypto.SignatureLength {
// remove recovery ID (V) if contained in the signature
sig = sig[:len(sig)-1]
}
// the signature needs to be in [R || S] format when provided to VerifySignature
2021-09-03 18:06:36 +00:00
return crypto.VerifySignature(pubKey.Key, crypto.Keccak256Hash(msg).Bytes(), sig)
}