github.com/InjectiveLabs/sdk-go@v1.53.0/chain/peggy/types/ethereum_signer.go (about)

     1  package types
     2  
     3  import (
     4  	"crypto/ecdsa"
     5  	"fmt"
     6  	"math/big"
     7  
     8  	"cosmossdk.io/errors"
     9  	"github.com/ethereum/go-ethereum/common"
    10  	"github.com/ethereum/go-ethereum/crypto"
    11  )
    12  
    13  const (
    14  	signaturePrefix = "\x19Ethereum Signed Message:\n32"
    15  )
    16  
    17  // NewEthereumSignature creates a new signuature over a given byte array
    18  func NewEthereumSignature(hash common.Hash, privateKey *ecdsa.PrivateKey) ([]byte, error) {
    19  	if privateKey == nil {
    20  		return nil, errors.Wrap(ErrEmpty, "private key")
    21  	}
    22  	protectedHash := crypto.Keccak256Hash(append([]uint8(signaturePrefix), hash[:]...))
    23  	return crypto.Sign(protectedHash.Bytes(), privateKey)
    24  }
    25  
    26  // decodeSignature was duplicated from go-ethereum with slight modifications
    27  func decodeSignature(sig []byte) (r, s *big.Int, v byte) {
    28  	if len(sig) != crypto.SignatureLength {
    29  		panic(fmt.Sprintf("wrong size for signature: got %d, want %d", len(sig), crypto.SignatureLength))
    30  	}
    31  	r = new(big.Int).SetBytes(sig[:32])
    32  	s = new(big.Int).SetBytes(sig[32:64])
    33  	if sig[64] == 27 || sig[64] == 28 {
    34  		v = sig[64] - 27
    35  	} else {
    36  		v = sig[64]
    37  	}
    38  	return r, s, v
    39  }
    40  
    41  // ValidateEthereumSignature takes a message, an associated signature and public key and
    42  // returns an error if the signature isn't valid
    43  func EthAddressFromSignature(hash common.Hash, signature []byte) (common.Address, error) {
    44  	if len(signature) < 65 {
    45  		return common.Address{}, errors.Wrap(ErrInvalid, "signature too short")
    46  	}
    47  
    48  	r, s, v := decodeSignature(signature)
    49  	if !crypto.ValidateSignatureValues(v, r, s, true) {
    50  		return common.Address{}, errors.Wrap(ErrInvalid, "Signature values failed validation")
    51  	}
    52  
    53  	// To verify signature
    54  	// - use crypto.SigToPub to get the public key
    55  	// - use crypto.PubkeyToAddress to get the address
    56  	// - compare this to the address given.
    57  
    58  	// for backwards compatibility reasons  the V value of an Ethereum sig is presented
    59  	// as 27 or 28, internally though it should be a 0-3 value due to changed formats.
    60  	// It seems that go-ethereum expects this to be done before sigs actually reach it's
    61  	// internal validation functions. In order to comply with this requirement we check
    62  	// the sig an dif it's in standard format we correct it. If it's in go-ethereum's expected
    63  	// format already we make no changes.
    64  	//
    65  	// We could attempt to break or otherwise exit early on obviously invalid values for this
    66  	// byte, but that's a task best left to go-ethereum
    67  	if signature[64] == 27 || signature[64] == 28 {
    68  		signature[64] -= 27
    69  	}
    70  
    71  	protectedHash := crypto.Keccak256Hash(append([]byte(signaturePrefix), hash[:]...))
    72  
    73  	pubkey, err := crypto.SigToPub(protectedHash.Bytes(), signature)
    74  	if err != nil {
    75  		return common.Address{}, errors.Wrap(err, "signature to public key")
    76  	}
    77  
    78  	addr := crypto.PubkeyToAddress(*pubkey)
    79  
    80  	return addr, nil
    81  }
    82  
    83  // ValidateEthereumSignature takes a message, an associated signature and public key and
    84  // returns an error if the signature isn't valid
    85  func ValidateEthereumSignature(hash common.Hash, signature []byte, ethAddress common.Address) error {
    86  	addr, err := EthAddressFromSignature(hash, signature)
    87  
    88  	if err != nil {
    89  		return errors.Wrap(err, "")
    90  	}
    91  
    92  	if addr != ethAddress {
    93  		return errors.Wrap(ErrInvalid, "signature not matching")
    94  	}
    95  
    96  	return nil
    97  }