From d79b64fe05faeebb4bd5855690c6a9ef067baa79 Mon Sep 17 00:00:00 2001 From: ia Date: Sat, 14 Apr 2018 06:03:01 -0500 Subject: [PATCH 1/2] solution: implement personal_ecRecover API method --- crypto/crypto_test.go | 18 ++++++++++++++++++ eth/api.go | 38 +++++++++++++++++++++++++++++++++---- internal/web3ext/web3ext.go | 6 ++++++ 3 files changed, 58 insertions(+), 4 deletions(-) diff --git a/crypto/crypto_test.go b/crypto/crypto_test.go index 50acde270..faaaa027c 100644 --- a/crypto/crypto_test.go +++ b/crypto/crypto_test.go @@ -28,12 +28,30 @@ import ( "time" "github.com/ethereumproject/go-ethereum/common" + "github.com/ethereumproject/go-ethereum/common/hexutil" "github.com/ethereumproject/go-ethereum/crypto/secp256k1" ) var testAddrHex = "970e8128ab834e8eac17ab8e3812f010678cf791" var testPrivHex = "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032" +var ( + testmsg = hexutil.MustDecode("0xce0677bb30baa8cf067c88db9811f4333d131bf8bcf12fe7065d211dce971008") + testsig = hexutil.MustDecode("0x90f27b8b488db00b00606796d2987f6a5f59ae62ea05effe84fef5b8b0e549984a691139ad57a3f0b906637673aa2f63d1f55cb1a69199d4009eea23ceaddc9301") + testpubkey = hexutil.MustDecode("0x04e32df42865e97135acfb65f3bae71bdc86f4d49150ad6a440b6f15878109880a0a2b2667f7e725ceea70c673093bf67663e0312623c8e091b13cf2c0f11ef652") + testpubkeyc = hexutil.MustDecode("0x02e32df42865e97135acfb65f3bae71bdc86f4d49150ad6a440b6f15878109880a") +) + +func TestEcrecover(t *testing.T) { + pubkey, err := Ecrecover(testmsg, testsig) + if err != nil { + t.Fatalf("recover error: %s", err) + } + if !bytes.Equal(pubkey, testpubkey) { + t.Errorf("pubkey mismatch: want: %x have: %x", testpubkey, pubkey) + } +} + // These tests are sanity checks. // They should ensure that we don't e.g. use Sha3-224 instead of Sha3-256 // and that the sha3 library uses keccak-f permutation. diff --git a/eth/api.go b/eth/api.go index 1cdcf5fa6..dfae62e6b 100644 --- a/eth/api.go +++ b/eth/api.go @@ -34,6 +34,7 @@ import ( "github.com/ethereumproject/go-ethereum/accounts" "github.com/ethereumproject/go-ethereum/common" "github.com/ethereumproject/go-ethereum/common/compiler" + "github.com/ethereumproject/go-ethereum/common/hexutil" "github.com/ethereumproject/go-ethereum/core" "github.com/ethereumproject/go-ethereum/core/state" "github.com/ethereumproject/go-ethereum/core/types" @@ -493,12 +494,13 @@ func (s *PrivateAccountAPI) LockAccount(addr common.Address) bool { // The key used to calculate the signature is decrypted with the given password. // // https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_sign -func (s *PrivateAccountAPI) Sign(data []byte, addr common.Address, passwd string) (string, error) { +func (s *PrivateAccountAPI) Sign(data hexutil.Bytes, addr common.Address, passwd string) (hexutil.Bytes, error) { signature, err := s.am.SignWithPassphrase(addr, passwd, signHash(data)) - if err == nil { - signature[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper + if err != nil { + return nil, err } - return common.ToHex(signature), nil + signature[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper + return signature, nil } // SendTransaction will create a transaction from the given arguments and @@ -1317,6 +1319,34 @@ func signHash(data []byte) []byte { return crypto.Keccak256([]byte(msg)) } +// EcRecover returns the address for the account that was used to create the signature. +// Note, this function is compatible with eth_sign and personal_sign. As such it recovers +// the address of: +// hash = keccak256("\x19Ethereum Signed Message:\n"${message length}${message}) +// addr = ecrecover(hash, signature) +// +// Note, the signature must conform to the secp256k1 curve R, S and V values, where +// the V value must be be 27 or 28 for legacy reasons. +// +// https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_ecRecover +func (s *PrivateAccountAPI) EcRecover(data, sig hexutil.Bytes) (common.Address, error) { + if len(sig) != 65 { + return common.Address{}, fmt.Errorf("signature must be 65 bytes long") + } + if sig[64] != 27 && sig[64] != 28 { + return common.Address{}, fmt.Errorf("invalid Ethereum signature (V is not 27 or 28)") + } + sig[64] -= 27 // Transform yellow paper V from 27/28 to 0/1 + + rpk, err := crypto.Ecrecover(signHash(data), sig) + if err != nil { + return common.Address{}, err + } + pubKey := crypto.ToECDSAPub(rpk) + recoveredAddr := crypto.PubkeyToAddress(*pubKey) + return recoveredAddr, nil +} + // Sign signs the given hash using the key that matches the address. The key must be // unlocked in order to sign the hash. func (s *PublicTransactionPoolAPI) Sign(addr common.Address, data []byte) (string, error) { diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index ce9ea34ed..9b4dbd297 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -382,6 +382,12 @@ web3._extend({ call: 'personal_sendTransaction', params: 2, inputFormatter: [web3._extend.formatters.inputTransactionFormatter, null] + }), + new web3._extend.Method({ + name: 'ecRecover', + call: 'personal_ecRecover', + params: 2, + inputFormatter: [null, null] }) ] }); From 7889b007116035e35046534fde4177f642acb2e9 Mon Sep 17 00:00:00 2001 From: ia Date: Sat, 14 Apr 2018 06:04:56 -0500 Subject: [PATCH 2/2] problem: eth_sign API uses wrong receiver (txpool) This somehow did not affect the functionality of eth_sign, but receiver should be PublicBlockChainAPI instead of txpool receiver. solution: change method's receiver. Also returns hexutil.Bytes instead of hex string for code cleanliness and consistency --- eth/api.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/eth/api.go b/eth/api.go index dfae62e6b..4a0ff263d 100644 --- a/eth/api.go +++ b/eth/api.go @@ -1349,12 +1349,14 @@ func (s *PrivateAccountAPI) EcRecover(data, sig hexutil.Bytes) (common.Address, // Sign signs the given hash using the key that matches the address. The key must be // unlocked in order to sign the hash. -func (s *PublicTransactionPoolAPI) Sign(addr common.Address, data []byte) (string, error) { - signature, err := s.am.Sign(addr, signHash(data)) - if err == nil { - signature[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper +func (s *PublicBlockChainAPI) Sign(addr common.Address, data hexutil.Bytes) (hexutil.Bytes, error) { + signed := signHash(data) + signature, err := s.am.Sign(addr, signed) + if err != nil { + return nil, err } - return common.ToHex(signature), err + signature[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper + return signature, err } // SignTransactionArgs represents the arguments to sign a transaction.