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 }