Drafted text/validator and and rewritten []byte as hexutil.Bytes

This commit is contained in:
Paul Berg 2018-10-25 14:41:04 +02:00 committed by Martin Holst Swende
parent 0808627c09
commit 950307bc8a
No known key found for this signature in database
GPG key ID: 683B438C05A5DDF0
2 changed files with 79 additions and 46 deletions

View file

@ -222,3 +222,26 @@ t=2018-10-23T19:51:57+0100 lvl=info msg=Configured api=signer audit log=audit.lo
t=2018-10-23T19:52:05+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:55568\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66D76e6A80DC7D46d7EC1b79b15Dfa34c3C6eF21 [chksum ok]" data="{Types:map[Person:[map[name:name type:string] map[name:wallet type:address]] Mail:[map[name:from type:Person] map[name:to type:Person] map[name:contents type:string]] EIP712Domain:[map[type:string name:name] map[name:version type:string] map[name:chainId type:uint256] map[name:verifyingContract type:address]]] PrimaryType:Mail Domain:{Name:Ether Mail Version:1 ChainId:+1 VerifyingContract:0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC Salt:0x} Message:map[contents:Hello, Bob! from:map[name:Alice wallet:0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21] to:map[name:Bob wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB]]}"
t=2018-10-23T20:12:04+0100 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-23T20:12:14+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:55752\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66D76e6A80DC7D46d7EC1b79b15Dfa34c3C6eF21 [chksum ok]" data="{Types:map[EIP712Domain:[map[name:name type:string] map[name:version type:string] map[name:chainId type:uint256] map[name:verifyingContract type:address]] Person:[map[name:name type:string] map[name:wallet type:address]] Mail:[map[name:from type:Person] map[name:to type:Person] map[type:string name:contents]]] PrimaryType:Mail Domain:{Name:Ether Mail Version:1 ChainId:+1 VerifyingContract:0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC Salt:0x} Message:map[from:map[name:Alice wallet:0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21] to:map[name:Bob wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB] contents:Hello, Bob!]}"
t=2018-10-25T13:47:17+0200 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-25T13:47:54+0200 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-25T13:50:09+0200 lvl=info msg=SignData api=signer type=request metadata="{\"remote\":\"127.0.0.1:50039\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21 [chksum INVALID]" data=608af3436e40964b9f2c7214953a3f03685f62b8deadbeef content-type=text/validator
t=2018-10-25T13:51:00+0200 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-25T13:51:07+0200 lvl=info msg=SignData api=signer type=request metadata="{\"remote\":\"127.0.0.1:51792\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21 [chksum INVALID]" data=608af3436e40964b9f2c7214953a3f03685f62b8deadbeef content-type=text/validator
t=2018-10-25T13:57:27+0200 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-25T13:57:30+0200 lvl=info msg=SignData api=signer type=request metadata="{\"remote\":\"127.0.0.1:62734\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21 [chksum INVALID]" data=608af3436e40964b9f2c7214953a3f03685f62b8deadbeef content-type=text/validator
t=2018-10-25T14:01:29+0200 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-25T14:01:30+0200 lvl=info msg=SignData api=signer type=request metadata="{\"remote\":\"127.0.0.1:53871\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21 [chksum INVALID]" data=608adeadbeef content-type=text/validator
t=2018-10-25T14:02:00+0200 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-25T14:02:10+0200 lvl=info msg=SignData api=signer type=request metadata="{\"remote\":\"127.0.0.1:55131\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21 [chksum INVALID]" data=608adeadbeef content-type=text/validator
t=2018-10-25T14:13:15+0200 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-25T14:13:16+0200 lvl=info msg=SignData api=signer type=request metadata="{\"remote\":\"127.0.0.1:58361\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21 [chksum INVALID]" data=608af3436e40964b9f2c7214953a3f03685f62b8deadbeef content-type=text/validator
t=2018-10-25T14:13:53+0200 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-25T14:14:04+0200 lvl=info msg=SignData api=signer type=request metadata="{\"remote\":\"127.0.0.1:59928\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21 [chksum INVALID]" data=608af3436e40964b9f2c7214953a3f03685f62b8deadbeef content-type=text/validator
t=2018-10-25T14:14:39+0200 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-25T14:14:43+0200 lvl=info msg=SignData api=signer type=request metadata="{\"remote\":\"127.0.0.1:61106\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21 [chksum INVALID]" data=608af3436e40964b9f2c7214953a3f03685f62b8deadbeef content-type=text/validator
t=2018-10-25T14:21:56+0200 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-25T14:22:59+0200 lvl=info msg=SignData api=signer type=request metadata="{\"remote\":\"127.0.0.1:59342\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21 [chksum INVALID]" data=60deadbeef content-type=text/validator
t=2018-10-25T14:22:59+0200 lvl=info msg=SignData api=signer type=response data= error="validator address and data undefined"
t=2018-10-25T14:23:05+0200 lvl=info msg=SignData api=signer type=request metadata="{\"remote\":\"127.0.0.1:59342\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21 [chksum INVALID]" data=608af3436e40964b9f2c7214953a3f03685f62b8deadbeef content-type=text/validator
t=2018-10-25T14:23:31+0200 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-25T14:23:55+0200 lvl=info msg=SignData api=signer type=request metadata="{\"remote\":\"127.0.0.1:61180\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21 [chksum INVALID]" data=608af3436e40964b9f2c7214953a3f03685f62b8deadbeef content-type=text/validator

View file

@ -39,6 +39,11 @@ import (
"github.com/ethereum/go-ethereum/rlp"
)
type ValidatorData struct {
Address common.Address
Message hexutil.Bytes
}
type TypedData struct {
Types EIP712Types `json:"types"`
PrimaryType string `json:"primaryType"`
@ -77,7 +82,7 @@ const (
// Note, the produced signature conforms to the secp256k1 curve R, S and V values,
// where the V value will be 27 or 28 for legacy reasons.
func (api *SignerAPI) Sign(ctx context.Context, addr common.MixedcaseAddress, req *SignDataRequest) ([]byte, error) {
func (api *SignerAPI) Sign(ctx context.Context, addr common.MixedcaseAddress, req *SignDataRequest) (hexutil.Bytes, error) {
req.Address = addr
req.Meta = MetadataFromContext(ctx)
@ -110,7 +115,7 @@ func (api *SignerAPI) Sign(ctx context.Context, addr common.MixedcaseAddress, re
//
// Different types of validation occur.
func (api *SignerAPI) SignData(ctx context.Context, contentType string, addr common.MixedcaseAddress, data hexutil.Bytes) (hexutil.Bytes, error) {
var req, err = api.determineSignatureFormat(contentType, data)
var req, err = api.determineSignatureFormat(contentType, addr, data)
if err != nil {
return nil, err
}
@ -125,7 +130,11 @@ func (api *SignerAPI) SignData(ctx context.Context, contentType string, addr com
}
// Determines which signature method should be used based upon the mime type
func (api *SignerAPI) determineSignatureFormat(contentType string, data hexutil.Bytes) (*SignDataRequest, error) {
// In the cases where it matters ensure that the charset is handled. The charset
// resides in the 'params' returned as the second returnvalue from mime.ParseMediaType
// charset, ok := params["charset"]
// As it is now, we accept any charset and just treat it as 'raw'.
func (api *SignerAPI) determineSignatureFormat(contentType string, addr common.MixedcaseAddress, data hexutil.Bytes) (*SignDataRequest, error) {
var req *SignDataRequest
mediaType, _, err := mime.ParseMediaType(contentType)
if err != nil {
@ -135,18 +144,19 @@ func (api *SignerAPI) determineSignatureFormat(contentType string, data hexutil.
switch mediaType {
case TextValidator.Mime:
// Data with an intended validator
sighash, msg := signTextWithValidator(data)
if len(data) < common.AddressLength {
return nil, errors.New("validator address and data undefined")
}
if len(data) == common.AddressLength {
return nil, errors.New("no data to sign")
}
sighash, msg := SignTextValidator(data)
req = &SignDataRequest{Rawdata: data, Message: msg, Hash: sighash, ContentType: mediaType}
break
case TextPlain.Mime:
// Sign calculates an Ethereum ECDSA signature for:
// hash = keccak256("\x19${byteVersion}Ethereum Signed Message:\n${message length}${message}")
// In the cases where it matters ensure that the charset is handled. The charset
// resides in the 'params' returned as the second returnvalue from mime.ParseMediaType
// charset, ok := params["charset"]
// As it is now, we accept any charset and just treat it as 'raw'.
sighash, msg := signTextPlain(data)
sighash, msg := SignTextPlain(data)
req = &SignDataRequest{Rawdata: data, Message: msg, Hash: sighash, ContentType: mediaType}
break
case ApplicationClique.Mime:
@ -155,11 +165,11 @@ func (api *SignerAPI) determineSignatureFormat(contentType string, data hexutil.
if err := rlp.DecodeBytes(data, header); err != nil {
return nil, err
}
sighash, err := signCliqueHeader(header)
sighash, err := SignCliqueHeader(header)
if err != nil {
return nil, err
}
msg := fmt.Sprintf("Clique block %d [0x%x]", header.Number, header.Hash())
msg := fmt.Sprintf("clique block %d [0x%x]", header.Number, header.Hash())
req = &SignDataRequest{Rawdata: data, Message: msg, Hash: sighash, ContentType: mediaType}
break
default:
@ -169,23 +179,15 @@ func (api *SignerAPI) determineSignatureFormat(contentType string, data hexutil.
}
// signTextPlain is a helper function that calculates a hash for the given message that can be
// safely used to calculate a signature from.
//
// The hash is calculated as
// keccak256("\x19${byteVersion}Ethereum Signed Message:\n"${message length}${message}).
//
// This gives context to the signed message and prevents signing of transactions.
func signTextPlain(data []byte) ([]byte, string) {
msg := fmt.Sprintf("\x19\\x%xEthereum Signed Message:\n%d%s", TextPlain.ByteVersion, len(data), data)
return crypto.Keccak256([]byte(msg)), msg
}
// signTextWithValidator signs the given message which can be further recovered
// with the given validator.
func signTextWithValidator(data []byte) ([]byte, string) {
msg := "TODO"
return crypto.Keccak256([]byte(msg)), msg
//
// hash = keccak256("\x19\x00"${address}${data}).
func SignTextValidator(data hexutil.Bytes) (hexutil.Bytes, string) {
address := common.BytesToAddress(data[:common.AddressLength])
message := data[common.AddressLength:len(data)-1]
hash := fmt.Sprintf("\x19\x00:%x%s", address, string(message))
return crypto.Keccak256(hexutil.Bytes(hash)), hash
}
// signCliqueHeader returns the hash which is used as input for the proof-of-authority
@ -195,7 +197,7 @@ func signTextWithValidator(data []byte) ([]byte, string) {
// The method requires the extra data to be at least 65 bytes -- the original implementation
// in clique.go panics if this is the case, thus it's been reimplemented here to avoid the panic
// and simply return an error instead
func signCliqueHeader(header *types.Header) (hexutil.Bytes, error) {
func SignCliqueHeader(header *types.Header) (hexutil.Bytes, error) {
hash := common.Hash{}
if len(header.Extra) < 65 {
return hash.Bytes(), fmt.Errorf("clique header extradata too short, %d < 65", len(header.Extra))
@ -222,6 +224,18 @@ func signCliqueHeader(header *types.Header) (hexutil.Bytes, error) {
return hash.Bytes(), nil
}
// signTextPlain is a helper function that calculates a hash for the given message that can be
// safely used to calculate a signature from. This gives context to the signed message and prevents
// signing of transactions.
//
// hash = keccak256("\x19$Ethereum Signed Message:\n"${message length}${message}).
func SignTextPlain(data hexutil.Bytes) (hexutil.Bytes, string) {
// The letter `E` is \x45 in hex, retrofitting
// https://github.com/ethereum/go-ethereum/pull/2940/commits
hash := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), string(data))
return crypto.Keccak256(hexutil.Bytes(hash)), hash
}
// SignTypedData signs EIP-712 conformant typed data
// hash = keccak256("\x19${byteVersion}${domainSeparator}${hashStruct(message)}")
func (api *SignerAPI) SignTypedData(ctx context.Context, addr common.MixedcaseAddress, typedData TypedData) (hexutil.Bytes, error) {
@ -252,12 +266,11 @@ func (api *SignerAPI) SignTypedData(ctx context.Context, addr common.MixedcaseAd
// hashStruct generates the following encoding for the given domain and message:
// `encode(domainSeparator : 𝔹²⁵⁶, message : 𝕊) = "\x19\x01" ‖ domainSeparator ‖ hashStruct(message)`
func (typedData *TypedData) HashStruct(primaryType string, data EIP712Data) []byte {
func (typedData *TypedData) HashStruct(primaryType string, data EIP712Data) hexutil.Bytes {
return crypto.Keccak256(typedData.EncodeData(primaryType, data))
}
// dependencies returns an array of custom types ordered by their
// hierarchical reference tree
// dependencies returns an array of custom types ordered by their hierarchical reference tree
func (typedData *TypedData) Dependencies(primaryType string, found []string) []string {
includes := func(arr []string, str string) bool {
for _, obj := range arr {
@ -289,7 +302,7 @@ func (typedData *TypedData) Dependencies(primaryType string, found []string) []s
// `name ‖ "(" ‖ member₁ ‖ "," ‖ member₂ ‖ "," ‖ … ‖ memberₙ ")"`
//
// each member is written as `type ‖ " " ‖ name` encodings cascade down and are sorted by name
func (typedData *TypedData) EncodeType(primaryType string) []byte {
func (typedData *TypedData) EncodeType(primaryType string) hexutil.Bytes {
// Get dependencies primary first, then alphabetical
deps := typedData.Dependencies(primaryType, []string{})
for i, dep := range deps {
@ -317,33 +330,33 @@ func (typedData *TypedData) EncodeType(primaryType string) []byte {
return buffer.Bytes()
}
func (typedData *TypedData) TypeHash(primaryType string) []byte {
func (typedData *TypedData) TypeHash(primaryType string) hexutil.Bytes {
return crypto.Keccak256(typedData.EncodeType(primaryType))
}
func bytesValueOf(_interface interface{}) []byte {
bytesVal, ok := _interface.([]byte)
func bytesValueOf(_interface interface{}) hexutil.Bytes {
bytesVal, ok := _interface.(hexutil.Bytes)
if ok {
return bytesVal
}
switch reflect.TypeOf(_interface) {
case reflect.TypeOf(string("")):
return []byte(_interface.(string))
return hexutil.Bytes(_interface.(string))
break
default:
break
}
panic(fmt.Errorf("unrecognized interface %v", _interface))
return []byte{}
return hexutil.Bytes{}
}
// encodeData generates the following encoding:
// `enc(value₁) ‖ enc(value₂) ‖ … ‖ enc(valueₙ)`
//
// each encoded member is 32-byte long
func (typedData *TypedData) EncodeData(primaryType string, data map[string]interface{}) []byte {
func (typedData *TypedData) EncodeData(primaryType string, data map[string]interface{}) hexutil.Bytes {
encTypes := []string{}
encValues := []interface{}{}
@ -359,12 +372,10 @@ func (typedData *TypedData) EncodeData(primaryType string, data map[string]inter
switch encType {
case "address":
primitiveEncType = "uint160"
bytesValue := []byte{}
bytesValue := hexutil.Bytes{}
for i := 0; i < 12; i++ {
bytesValue = append(bytesValue, 0)
}
//foo := common.BytesToAddress([]byte(encValue.(string)))
//fmt.Println(foo)
for _, _byte := range encValue.(common.Address) {
bytesValue = append(bytesValue, _byte)
}
@ -387,11 +398,11 @@ func (typedData *TypedData) EncodeData(primaryType string, data map[string]inter
encTypes = append(encTypes, "bytes32")
sizeStr := strings.TrimPrefix(encType, "bytes")
size, _ := strconv.Atoi(sizeStr)
bytesValue := []byte{}
bytesValue := hexutil.Bytes{}
for i := 0; i < 32-size; i++ {
bytesValue = append(bytesValue, 0)
}
for _, _byte := range encValue.([]byte) {
for _, _byte := range encValue.(hexutil.Bytes) {
bytesValue = append(bytesValue, _byte)
}
primitiveEncValue = bytesValue
@ -461,7 +472,6 @@ func (api *SignerAPI) EcRecover(ctx context.Context, contentType string, data he
// the V value must be be 27 or 28 for legacy reasons.
//
// https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_ecRecover
if len(sig) != 65 {
return common.Address{}, fmt.Errorf("signature must be 65 bytes long")
}
@ -469,7 +479,7 @@ func (api *SignerAPI) EcRecover(ctx context.Context, contentType string, data he
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
hash, _ := signTextPlain(data)
hash, _ := SignTextPlain(data)
rpk, err := crypto.SigToPub(hash, sig)
if err != nil {
return common.Address{}, err
@ -481,7 +491,7 @@ func (api *SignerAPI) EcRecover(ctx context.Context, contentType string, data he
}
// UnmarshalJSON validates the input data
func (typedData *TypedData) UnmarshalJSON(data []byte) error {
func (typedData *TypedData) UnmarshalJSON(data hexutil.Bytes) error {
type input struct {
Types EIP712Types `json:"types"`
PrimaryType string `json:"primaryType"`