hash typed data (#1426)

* add HashTypedData to backend

* add HashTypedData to backand, lib, and mobile

* rename typeddata.Hash to typeddata.ValidateAndHash
This commit is contained in:
Andrea Franz 2019-03-28 15:56:21 +01:00 committed by GitHub
parent b80a9f5aaa
commit 8fe14e8f23
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 98 additions and 4 deletions

View File

@ -8,6 +8,7 @@ import (
"math/big"
"sync"
"github.com/ethereum/go-ethereum/common"
gethcommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
ethcrypto "github.com/ethereum/go-ethereum/crypto"
@ -309,6 +310,16 @@ func (b *StatusBackend) SignTypedData(typed typeddata.TypedData, password string
return hexutil.Bytes(sig), err
}
// HashTypedData generates the hash of TypedData.
func (b *StatusBackend) HashTypedData(typed typeddata.TypedData) (common.Hash, error) {
chain := new(big.Int).SetUint64(b.StatusNode().Config().NetworkID)
hash, err := typeddata.ValidateAndHash(typed, chain)
if err != nil {
return common.Hash{}, err
}
return hash, err
}
func (b *StatusBackend) getVerifiedWalletAccount(password string) (*account.SelectedExtKey, error) {
selectedWalletAccount, err := b.accountManager.SelectedWalletAccount()
if err != nil {

View File

@ -2,17 +2,20 @@ package api
import (
"encoding/hex"
"encoding/json"
"fmt"
"math/rand"
"sync"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
"github.com/status-im/status-go/account"
"github.com/status-im/status-go/node"
"github.com/status-im/status-go/params"
"github.com/status-im/status-go/rpc"
"github.com/status-im/status-go/services/typeddata"
"github.com/status-im/status-go/t/utils"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -382,3 +385,48 @@ func TestStartStopMultipleTimes(t *testing.T) {
require.NoError(t, backend.StartNode(config))
require.NoError(t, backend.StopNode())
}
func TestHashTypedData(t *testing.T) {
backend := NewStatusBackend()
config, err := utils.MakeTestNodeConfig(params.StatusChainNetworkID)
require.NoError(t, err)
err = backend.StartNode(config)
require.NoError(t, err)
defer func() {
require.NoError(t, backend.StopNode())
}()
eip712Domain := "EIP712Domain"
mytypes := typeddata.Types{
eip712Domain: []typeddata.Field{
{Name: "name", Type: "string"},
{Name: "version", Type: "string"},
{Name: "chainId", Type: "uint256"},
{Name: "verifyingContract", Type: "address"},
},
"Text": []typeddata.Field{
{Name: "body", Type: "string"},
},
}
domain := map[string]json.RawMessage{
"name": json.RawMessage(`"Ether Text"`),
"version": json.RawMessage(`"1"`),
"chainId": json.RawMessage(fmt.Sprintf("%d", params.StatusChainNetworkID)),
"verifyingContract": json.RawMessage(`"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"`),
}
msg := map[string]json.RawMessage{
"body": json.RawMessage(`"Hello, Bob!"`),
}
typed := typeddata.TypedData{
Types: mytypes,
PrimaryType: "Text",
Domain: domain,
Message: msg,
}
hash, err := backend.HashTypedData(typed)
require.NoError(t, err)
assert.NotEqual(t, common.Hash{}, hash)
}

View File

@ -485,6 +485,21 @@ func SignTypedData(data, password *C.char) *C.char {
return C.CString(prepareJSONResponse(result.String(), err))
}
// HashTypedData unmarshalls data into TypedData, validates it and hashes it.
//export HashTypedData
func HashTypedData(data *C.char) *C.char {
var typed typeddata.TypedData
err := json.Unmarshal([]byte(C.GoString(data)), &typed)
if err != nil {
return C.CString(prepareJSONResponseWithCode(nil, err, codeFailedParseParams))
}
if err := typed.Validate(); err != nil {
return C.CString(prepareJSONResponseWithCode(nil, err, codeFailedParseParams))
}
result, err := statusBackend.HashTypedData(typed)
return C.CString(prepareJSONResponse(result.String(), err))
}
//StartCPUProfile runs pprof for cpu
//export StartCPUProfile
func StartCPUProfile(dataDir *C.char) *C.char {

View File

@ -369,6 +369,20 @@ func SignTypedData(data, password string) string {
return prepareJSONResponse(result.String(), err)
}
// HashTypedData unmarshalls data into TypedData, validates it and hashes it.
func HashTypedData(data string) string {
var typed typeddata.TypedData
err := json.Unmarshal([]byte(data), &typed)
if err != nil {
return prepareJSONResponseWithCode(nil, err, codeFailedParseParams)
}
if err := typed.Validate(); err != nil {
return prepareJSONResponseWithCode(nil, err, codeFailedParseParams)
}
result, err := statusBackend.HashTypedData(typed)
return prepareJSONResponse(result.String(), err)
}
// Recover unmarshals rpc params {signDataString, signedData} and passes
// them onto backend.
func Recover(rpcParams string) string {

View File

@ -19,6 +19,15 @@ var (
int256Type, _ = abi.NewType("int256", nil)
)
// ValidateAndHash generates a hash of TypedData and verifies that chainId in the typed data matches currently selected chain.
func ValidateAndHash(typed TypedData, chain *big.Int) (common.Hash, error) {
if err := typed.ValidateChainID(chain); err != nil {
return common.Hash{}, err
}
return encodeData(typed)
}
// deps runs breadth-first traversal starting from target and collects all
// found composite dependencies types into result slice. target always will be first
// in the result array. all other dependencies are sorted alphabetically.

View File

@ -27,10 +27,7 @@ func encodeData(typed TypedData) (rst common.Hash, err error) {
// Sign TypedData with a given private key. Verify that chainId in the typed data matches currently selected chain.
func Sign(typed TypedData, prv *ecdsa.PrivateKey, chain *big.Int) ([]byte, error) {
if err := typed.ValidateChainID(chain); err != nil {
return nil, err
}
hash, err := encodeData(typed)
hash, err := ValidateAndHash(typed, chain)
if err != nil {
return nil, err
}