fix HashMessage to decode hex only if needed (#1450)

* hash message expects a string and not a hex string

* refactor HashMessage

* swap returned values
This commit is contained in:
Andrea Franz 2019-04-30 01:26:41 +02:00 committed by GitHub
parent 354e6981ba
commit 28ec255d77
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 96 additions and 33 deletions

View File

@ -1,7 +1,10 @@
package api
import (
"fmt"
"bytes"
"encoding/hex"
"strconv"
"strings"
"github.com/ethereum/go-ethereum/crypto"
)
@ -21,7 +24,36 @@ func RunAsync(f func() error) <-chan error {
// The hash is calulcated as
// keccak256("\x19Ethereum Signed Message:\n"${message length}${message}).
// This gives context to the signed message and prevents signing of transactions.
func HashMessage(data []byte) []byte {
msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), data)
return crypto.Keccak256([]byte(msg))
func HashMessage(message string) ([]byte, error) {
buf := bytes.NewBufferString("\x19Ethereum Signed Message:\n")
if value, ok := decodeHexStrict(message); ok {
if _, err := buf.WriteString(strconv.Itoa(len(value))); err != nil {
return nil, err
}
if _, err := buf.Write(value); err != nil {
return nil, err
}
} else {
if _, err := buf.WriteString(strconv.Itoa(len(message))); err != nil {
return nil, err
}
if _, err := buf.WriteString(message); err != nil {
return nil, err
}
}
return crypto.Keccak256(buf.Bytes()), nil
}
func decodeHexStrict(s string) ([]byte, bool) {
if !strings.HasPrefix(s, "0x") {
return nil, false
}
value, err := hex.DecodeString(s[2:])
if err != nil {
return nil, false
}
return value, true
}

View File

@ -26,20 +26,53 @@ func TestHashMessage(t *testing.T) {
require.NoError(t, err)
addr := crypto.PubkeyToAddress(key.PublicKey)
originalMessage := []byte{0x01, 0x02, 0x03}
hash := HashMessage(originalMessage)
// simulate signature from external signer like a keycard
sig, err := crypto.Sign(hash, key)
require.NoError(t, err)
sig[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper
// check that the message was wrapped correctly before hashing it
recParams := personal.RecoverParams{
Message: fmt.Sprintf("0x%x", originalMessage),
Signature: fmt.Sprintf("0x%x", sig),
scenarios := []struct {
message string
expectedHash string
recoverMessage string
}{
{
message: "XYZ",
expectedHash: "634349abf2de883d23e8b46972896c7652a06670c990410d3436d9b44db09e6b",
recoverMessage: fmt.Sprintf("0x%x", "XYZ"),
},
{
message: "0xXYZ",
expectedHash: "f9c57a8998c71a2c8d74d70abe6561838f0d6cb6d82bc85bd70afcc82368055c",
recoverMessage: fmt.Sprintf("0x%x", "0xXYZ"),
},
{
message: "1122",
expectedHash: "3f07e02a153f02bdf97d77161746257626e9c39e4c3cf59896365fd1e6a9c7c3",
recoverMessage: fmt.Sprintf("0x%x", "1122"),
},
{
message: "0x1122",
expectedHash: "86d79d0957efa9b7d91f1116e70d0ee934cb9cdeccefa07756aed2bee119a2f3",
recoverMessage: "0x1122",
},
}
for _, s := range scenarios {
t.Run(s.message, func(t *testing.T) {
hash, err := HashMessage(s.message)
require.Nil(t, err)
require.Equal(t, s.expectedHash, fmt.Sprintf("%x", hash))
// simulate signature from external signer like a keycard
sig, err := crypto.Sign(hash, key)
require.NoError(t, err)
sig[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper
// check that the message was wrapped correctly before hashing it
recParams := personal.RecoverParams{
Message: s.recoverMessage,
Signature: fmt.Sprintf("0x%x", sig),
}
recoveredAddr, err := backend.Recover(recParams)
require.NoError(t, err)
assert.Equal(t, addr, recoveredAddr)
})
}
recoveredAddr, err := backend.Recover(recParams)
require.NoError(t, err)
assert.Equal(t, addr, recoveredAddr)
}

View File

@ -459,14 +459,13 @@ func HashTransaction(txArgsJSON *C.char) *C.char {
// keccak256("\x19Ethereum Signed Message:\n"${message length}${message}).
// This gives context to the signed message and prevents signing of transactions.
//export HashMessage
func HashMessage(messageString *C.char) *C.char {
message, err := hex.DecodeString(C.GoString(messageString))
if err != nil {
return C.CString(prepareJSONResponseWithCode(nil, err, codeFailedParseParams))
func HashMessage(message *C.char) *C.char {
hash, err := api.HashMessage(C.GoString(message))
code := codeUnknown
if c, ok := errToCodeMap[err]; ok {
code = c
}
hash := api.HashMessage(message)
return C.CString(prepareJSONResponseWithCode(fmt.Sprintf("0x%x", hash), err, codeUnknown))
return C.CString(prepareJSONResponseWithCode(fmt.Sprintf("0x%x", hash), err, code))
}
// SignTypedData unmarshall data into TypedData, validate it and signs with selected account,

View File

@ -460,14 +460,13 @@ func HashTransaction(txArgsJSON string) string {
// The hash is calulcated as
// keccak256("\x19Ethereum Signed Message:\n"${message length}${message}).
// This gives context to the signed message and prevents signing of transactions.
func HashMessage(messageString string) string {
message, err := hex.DecodeString(messageString)
if err != nil {
return prepareJSONResponseWithCode(nil, err, codeFailedParseParams)
func HashMessage(message string) string {
hash, err := api.HashMessage(message)
code := codeUnknown
if c, ok := errToCodeMap[err]; ok {
code = c
}
hash := api.HashMessage(message)
return prepareJSONResponseWithCode(fmt.Sprintf("0x%x", hash), err, codeUnknown)
return prepareJSONResponseWithCode(fmt.Sprintf("0x%x", hash), err, code)
}
// StartCPUProfile runs pprof for CPU.