Skip to content
This repository has been archived by the owner on Oct 4, 2019. It is now read-only.

Commit

Permalink
Merge pull request #566 from ethereumproject/feat/api-ecrecover-1
Browse files Browse the repository at this point in the history
implement personal_ecRecover API method
  • Loading branch information
whilei committed May 2, 2018
2 parents bb9f394 + 7889b00 commit 7b02fe8
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 9 deletions.
18 changes: 18 additions & 0 deletions crypto/crypto_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
49 changes: 40 additions & 9 deletions eth/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -504,12 +504,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
Expand Down Expand Up @@ -1328,14 +1329,44 @@ 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) {
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.
Expand Down
6 changes: 6 additions & 0 deletions internal/web3ext/web3ext.go
Original file line number Diff line number Diff line change
Expand Up @@ -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]
})
]
});
Expand Down

0 comments on commit 7b02fe8

Please sign in to comment.