github.com/0xsequence/ethkit@v1.25.0/ethwallet/utils.go (about)

     1  package ethwallet
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  
     7  	"github.com/0xsequence/ethkit/ethcoder"
     8  	"github.com/0xsequence/ethkit/go-ethereum/common"
     9  	"github.com/0xsequence/ethkit/go-ethereum/crypto"
    10  )
    11  
    12  func RecoverAddress(message, signature []byte) (common.Address, error) {
    13  	msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%v%s", len(message), message)
    14  	if len(signature) != 65 {
    15  		return common.Address{}, fmt.Errorf("signature is not of proper length")
    16  	}
    17  	return RecoverAddressFromDigest(crypto.Keccak256([]byte(msg)), signature)
    18  }
    19  
    20  func RecoverAddressFromDigest(digest, signature []byte) (common.Address, error) {
    21  	if len(digest) != 32 {
    22  		return common.Address{}, fmt.Errorf("digest is not of proper length (=32)")
    23  	}
    24  	if len(signature) != 65 {
    25  		return common.Address{}, fmt.Errorf("signature is not of proper length (=65)")
    26  	}
    27  
    28  	sig := make([]byte, 65)
    29  	copy(sig, signature)
    30  
    31  	if sig[64] > 1 {
    32  		sig[64] -= 27 // recovery ID
    33  	}
    34  
    35  	pubkey, err := crypto.SigToPub(digest, sig)
    36  	if err != nil {
    37  		return common.Address{}, err
    38  	}
    39  	address := crypto.PubkeyToAddress(*pubkey)
    40  
    41  	return address, nil
    42  }
    43  
    44  func IsValidEOASignature(address common.Address, digest, signature []byte) (bool, error) {
    45  	if len(digest) == 0 || len(signature) == 0 {
    46  		return false, fmt.Errorf("digest and signature must not be empty")
    47  	}
    48  	if len(signature) != 65 {
    49  		return false, fmt.Errorf("signature is not of proper length")
    50  	}
    51  
    52  	sig := make([]byte, 65)
    53  	copy(sig, signature)
    54  
    55  	if sig[64] > 1 {
    56  		sig[64] -= 27 // recovery ID
    57  	}
    58  
    59  	pubkey, err := crypto.SigToPub(digest, sig)
    60  	if err != nil {
    61  		return false, err
    62  	}
    63  	recoveredAddress := crypto.PubkeyToAddress(*pubkey)
    64  	if recoveredAddress == address {
    65  		return true, nil
    66  	}
    67  	return false, fmt.Errorf("invalid signature")
    68  }
    69  
    70  func IsValid191Signature(address common.Address, message, signature []byte) (bool, error) {
    71  	if len(message) == 0 || len(signature) == 0 {
    72  		return false, fmt.Errorf("message and signature must not be empty")
    73  	}
    74  	if len(signature) != 65 {
    75  		return false, fmt.Errorf("signature is not of proper length")
    76  	}
    77  
    78  	message191 := []byte("\x19Ethereum Signed Message:\n")
    79  	if !bytes.HasPrefix(message, message191) {
    80  		mlen := fmt.Sprintf("%d", len(message))
    81  		message191 = append(message191, []byte(mlen)...)
    82  		message191 = append(message191, message...)
    83  	} else {
    84  		message191 = message
    85  	}
    86  
    87  	sig := make([]byte, 65)
    88  	copy(sig, signature)
    89  
    90  	hash := crypto.Keccak256(message191)
    91  	if sig[64] > 1 {
    92  		sig[64] -= 27 // recovery ID
    93  	}
    94  
    95  	pubkey, err := crypto.SigToPub(hash, sig)
    96  	if err != nil {
    97  		return false, err
    98  	}
    99  	recoveredAddress := crypto.PubkeyToAddress(*pubkey)
   100  	if recoveredAddress == address {
   101  		return true, nil
   102  	}
   103  	return false, fmt.Errorf("invalid signature")
   104  }
   105  
   106  // Validate the public key address of a signed message
   107  func ValidateEthereumSignature(address string, message []byte, signatureHex string) (bool, error) {
   108  	sig, err := ethcoder.HexDecode(signatureHex)
   109  	if err != nil {
   110  		return false, err
   111  	}
   112  	return IsValid191Signature(common.HexToAddress(address), message, sig)
   113  }