github.com/ethersphere/bee/v2@v2.2.0/pkg/crypto/signer.go (about)

     1  // Copyright 2020 The Swarm Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package crypto
     6  
     7  import (
     8  	"crypto/ecdsa"
     9  	"errors"
    10  	"fmt"
    11  	"math/big"
    12  
    13  	"github.com/btcsuite/btcd/btcec/v2"
    14  	btcecdsa "github.com/btcsuite/btcd/btcec/v2/ecdsa"
    15  	"github.com/ethereum/go-ethereum/common"
    16  	"github.com/ethereum/go-ethereum/core/types"
    17  	"github.com/ethersphere/bee/v2/pkg/crypto/eip712"
    18  )
    19  
    20  var (
    21  	ErrInvalidLength = errors.New("invalid signature length")
    22  )
    23  
    24  type Signer interface {
    25  	// Sign signs data with ethereum prefix (eip191 type 0x45).
    26  	Sign(data []byte) ([]byte, error)
    27  	// SignTx signs an ethereum transaction.
    28  	SignTx(transaction *types.Transaction, chainID *big.Int) (*types.Transaction, error)
    29  	// SignTypedData signs data according to eip712.
    30  	SignTypedData(typedData *eip712.TypedData) ([]byte, error)
    31  	// PublicKey returns the public key this signer uses.
    32  	PublicKey() (*ecdsa.PublicKey, error)
    33  	// EthereumAddress returns the ethereum address this signer uses.
    34  	EthereumAddress() (common.Address, error)
    35  }
    36  
    37  // addEthereumPrefix adds the ethereum prefix to the data.
    38  func addEthereumPrefix(data []byte) []byte {
    39  	return []byte(fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), data))
    40  }
    41  
    42  // hashWithEthereumPrefix returns the hash that should be signed for the given data.
    43  func hashWithEthereumPrefix(data []byte) ([]byte, error) {
    44  	return LegacyKeccak256(addEthereumPrefix(data))
    45  }
    46  
    47  // Recover verifies signature with the data base provided.
    48  // It is using `btcec.RecoverCompact` function.
    49  func Recover(signature, data []byte) (*ecdsa.PublicKey, error) {
    50  	if len(signature) != 65 {
    51  		return nil, ErrInvalidLength
    52  	}
    53  	// Convert to btcec input format with 'recovery id' v at the beginning.
    54  	btcsig := make([]byte, 65)
    55  	btcsig[0] = signature[64]
    56  	copy(btcsig[1:], signature)
    57  
    58  	hash, err := hashWithEthereumPrefix(data)
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  
    63  	pbk, _, err := btcecdsa.RecoverCompact(btcsig, hash)
    64  	if err != nil {
    65  		return nil, err
    66  	}
    67  	return pbk.ToECDSA(), err
    68  }
    69  
    70  type defaultSigner struct {
    71  	key *ecdsa.PrivateKey
    72  }
    73  
    74  func NewDefaultSigner(key *ecdsa.PrivateKey) Signer {
    75  	return &defaultSigner{
    76  		key: key,
    77  	}
    78  }
    79  
    80  // PublicKey returns the public key this signer uses.
    81  func (d *defaultSigner) PublicKey() (*ecdsa.PublicKey, error) {
    82  	return &d.key.PublicKey, nil
    83  }
    84  
    85  // Sign signs data with ethereum prefix (eip191 type 0x45).
    86  func (d *defaultSigner) Sign(data []byte) (signature []byte, err error) {
    87  	hash, err := hashWithEthereumPrefix(data)
    88  	if err != nil {
    89  		return nil, err
    90  	}
    91  
    92  	return d.sign(hash, true)
    93  }
    94  
    95  // SignTx signs an ethereum transaction.
    96  func (d *defaultSigner) SignTx(transaction *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
    97  	txSigner := types.NewLondonSigner(chainID)
    98  	hash := txSigner.Hash(transaction).Bytes()
    99  	// isCompressedKey is false here so we get the expected v value (27 or 28)
   100  	signature, err := d.sign(hash, false)
   101  	if err != nil {
   102  		return nil, err
   103  	}
   104  
   105  	// v value needs to be adjusted by 27 as transaction.WithSignature expects it to be 0 or 1
   106  	signature[64] -= 27
   107  	return transaction.WithSignature(txSigner, signature)
   108  }
   109  
   110  // EthereumAddress returns the ethereum address this signer uses.
   111  func (d *defaultSigner) EthereumAddress() (common.Address, error) {
   112  	publicKey, err := d.PublicKey()
   113  	if err != nil {
   114  		return common.Address{}, err
   115  	}
   116  	eth, err := NewEthereumAddress(*publicKey)
   117  	if err != nil {
   118  		return common.Address{}, err
   119  	}
   120  	var ethAddress common.Address
   121  	copy(ethAddress[:], eth)
   122  	return ethAddress, nil
   123  }
   124  
   125  // SignTypedData signs data according to eip712.
   126  func (d *defaultSigner) SignTypedData(typedData *eip712.TypedData) ([]byte, error) {
   127  	rawData, err := eip712.EncodeForSigning(typedData)
   128  	if err != nil {
   129  		return nil, err
   130  	}
   131  
   132  	sighash, err := LegacyKeccak256(rawData)
   133  	if err != nil {
   134  		return nil, err
   135  	}
   136  
   137  	return d.sign(sighash, false)
   138  }
   139  
   140  // sign the provided hash and convert it to the ethereum (r,s,v) format.
   141  func (d *defaultSigner) sign(sighash []byte, isCompressedKey bool) ([]byte, error) {
   142  	pvk, _ := btcec.PrivKeyFromBytes(d.key.D.Bytes())
   143  	signature, err := btcecdsa.SignCompact(pvk, sighash, false)
   144  	if err != nil {
   145  		return nil, err
   146  	}
   147  
   148  	// Convert to Ethereum signature format with 'recovery id' v at the end.
   149  	v := signature[0]
   150  	copy(signature, signature[1:])
   151  	signature[64] = v
   152  	return signature, nil
   153  }
   154  
   155  // RecoverEIP712 recovers the public key for eip712 signed data.
   156  func RecoverEIP712(signature []byte, data *eip712.TypedData) (*ecdsa.PublicKey, error) {
   157  	if len(signature) != 65 {
   158  		return nil, errors.New("invalid length")
   159  	}
   160  	// Convert to btcec input format with 'recovery id' v at the beginning.
   161  	btcsig := make([]byte, 65)
   162  	btcsig[0] = signature[64]
   163  	copy(btcsig[1:], signature)
   164  
   165  	rawData, err := eip712.EncodeForSigning(data)
   166  	if err != nil {
   167  		return nil, err
   168  	}
   169  
   170  	sighash, err := LegacyKeccak256(rawData)
   171  	if err != nil {
   172  		return nil, err
   173  	}
   174  
   175  	pbk, _, err := btcecdsa.RecoverCompact(btcsig, sighash)
   176  	if err != nil {
   177  		return nil, err
   178  	}
   179  	return pbk.ToECDSA(), err
   180  }