github.com/bloxroute-labs/bor@v0.1.4/signer/core/signed_data.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // This file is part of go-ethereum. 3 // 4 // go-ethereum is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // go-ethereum is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. 16 // 17 package core 18 19 import ( 20 "bytes" 21 "context" 22 "errors" 23 "fmt" 24 "math/big" 25 "mime" 26 "reflect" 27 "regexp" 28 "sort" 29 "strconv" 30 "strings" 31 "unicode" 32 33 "github.com/maticnetwork/bor/accounts" 34 "github.com/maticnetwork/bor/accounts/abi" 35 "github.com/maticnetwork/bor/common" 36 "github.com/maticnetwork/bor/common/hexutil" 37 "github.com/maticnetwork/bor/common/math" 38 "github.com/maticnetwork/bor/consensus/bor" 39 "github.com/maticnetwork/bor/consensus/clique" 40 "github.com/maticnetwork/bor/core/types" 41 "github.com/maticnetwork/bor/crypto" 42 "github.com/maticnetwork/bor/rlp" 43 ) 44 45 type SigFormat struct { 46 Mime string 47 ByteVersion byte 48 } 49 50 var ( 51 IntendedValidator = SigFormat{ 52 accounts.MimetypeDataWithValidator, 53 0x00, 54 } 55 DataTyped = SigFormat{ 56 accounts.MimetypeTypedData, 57 0x01, 58 } 59 ApplicationClique = SigFormat{ 60 accounts.MimetypeClique, 61 0x02, 62 } 63 ApplicationBor = SigFormat{ 64 accounts.MimetypeBor, 65 0x10, 66 } 67 TextPlain = SigFormat{ 68 accounts.MimetypeTextPlain, 69 0x45, 70 } 71 ) 72 73 type ValidatorData struct { 74 Address common.Address 75 Message hexutil.Bytes 76 } 77 78 type TypedData struct { 79 Types Types `json:"types"` 80 PrimaryType string `json:"primaryType"` 81 Domain TypedDataDomain `json:"domain"` 82 Message TypedDataMessage `json:"message"` 83 } 84 85 type Type struct { 86 Name string `json:"name"` 87 Type string `json:"type"` 88 } 89 90 func (t *Type) isArray() bool { 91 return strings.HasSuffix(t.Type, "[]") 92 } 93 94 // typeName returns the canonical name of the type. If the type is 'Person[]', then 95 // this method returns 'Person' 96 func (t *Type) typeName() string { 97 if strings.HasSuffix(t.Type, "[]") { 98 return strings.TrimSuffix(t.Type, "[]") 99 } 100 return t.Type 101 } 102 103 func (t *Type) isReferenceType() bool { 104 if len(t.Type) == 0 { 105 return false 106 } 107 // Reference types must have a leading uppercase characer 108 return unicode.IsUpper([]rune(t.Type)[0]) 109 } 110 111 type Types map[string][]Type 112 113 type TypePriority struct { 114 Type string 115 Value uint 116 } 117 118 type TypedDataMessage = map[string]interface{} 119 120 type TypedDataDomain struct { 121 Name string `json:"name"` 122 Version string `json:"version"` 123 ChainId *math.HexOrDecimal256 `json:"chainId"` 124 VerifyingContract string `json:"verifyingContract"` 125 Salt string `json:"salt"` 126 } 127 128 var typedDataReferenceTypeRegexp = regexp.MustCompile(`^[A-Z](\w*)(\[\])?$`) 129 130 // sign receives a request and produces a signature 131 // 132 // Note, the produced signature conforms to the secp256k1 curve R, S and V values, 133 // where the V value will be 27 or 28 for legacy reasons, if legacyV==true. 134 func (api *SignerAPI) sign(addr common.MixedcaseAddress, req *SignDataRequest, legacyV bool) (hexutil.Bytes, error) { 135 // We make the request prior to looking up if we actually have the account, to prevent 136 // account-enumeration via the API 137 res, err := api.UI.ApproveSignData(req) 138 if err != nil { 139 return nil, err 140 } 141 if !res.Approved { 142 return nil, ErrRequestDenied 143 } 144 // Look up the wallet containing the requested signer 145 account := accounts.Account{Address: addr.Address()} 146 wallet, err := api.am.Find(account) 147 if err != nil { 148 return nil, err 149 } 150 pw, err := api.lookupOrQueryPassword(account.Address, 151 "Password for signing", 152 fmt.Sprintf("Please enter password for signing data with account %s", account.Address.Hex())) 153 if err != nil { 154 return nil, err 155 } 156 // Sign the data with the wallet 157 signature, err := wallet.SignDataWithPassphrase(account, pw, req.ContentType, req.Rawdata) 158 if err != nil { 159 return nil, err 160 } 161 if legacyV { 162 signature[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper 163 } 164 return signature, nil 165 } 166 167 // SignData signs the hash of the provided data, but does so differently 168 // depending on the content-type specified. 169 // 170 // Different types of validation occur. 171 func (api *SignerAPI) SignData(ctx context.Context, contentType string, addr common.MixedcaseAddress, data interface{}) (hexutil.Bytes, error) { 172 var req, transformV, err = api.determineSignatureFormat(ctx, contentType, addr, data) 173 if err != nil { 174 return nil, err 175 } 176 signature, err := api.sign(addr, req, transformV) 177 if err != nil { 178 api.UI.ShowError(err.Error()) 179 return nil, err 180 } 181 return signature, nil 182 } 183 184 // determineSignatureFormat determines which signature method should be used based upon the mime type 185 // In the cases where it matters ensure that the charset is handled. The charset 186 // resides in the 'params' returned as the second returnvalue from mime.ParseMediaType 187 // charset, ok := params["charset"] 188 // As it is now, we accept any charset and just treat it as 'raw'. 189 // This method returns the mimetype for signing along with the request 190 func (api *SignerAPI) determineSignatureFormat(ctx context.Context, contentType string, addr common.MixedcaseAddress, data interface{}) (*SignDataRequest, bool, error) { 191 var ( 192 req *SignDataRequest 193 useEthereumV = true // Default to use V = 27 or 28, the legacy Ethereum format 194 ) 195 mediaType, _, err := mime.ParseMediaType(contentType) 196 if err != nil { 197 return nil, useEthereumV, err 198 } 199 200 switch mediaType { 201 case IntendedValidator.Mime: 202 // Data with an intended validator 203 validatorData, err := UnmarshalValidatorData(data) 204 if err != nil { 205 return nil, useEthereumV, err 206 } 207 sighash, msg := SignTextValidator(validatorData) 208 messages := []*NameValueType{ 209 { 210 Name: "This is a request to sign data intended for a particular validator (see EIP 191 version 0)", 211 Typ: "description", 212 Value: "", 213 }, 214 { 215 Name: "Intended validator address", 216 Typ: "address", 217 Value: validatorData.Address.String(), 218 }, 219 { 220 Name: "Application-specific data", 221 Typ: "hexdata", 222 Value: validatorData.Message, 223 }, 224 { 225 Name: "Full message for signing", 226 Typ: "hexdata", 227 Value: fmt.Sprintf("0x%x", msg), 228 }, 229 } 230 req = &SignDataRequest{ContentType: mediaType, Rawdata: []byte(msg), Messages: messages, Hash: sighash} 231 case ApplicationClique.Mime: 232 // Clique is the Ethereum PoA standard 233 stringData, ok := data.(string) 234 if !ok { 235 return nil, useEthereumV, fmt.Errorf("input for %v must be an hex-encoded string", ApplicationClique.Mime) 236 } 237 cliqueData, err := hexutil.Decode(stringData) 238 if err != nil { 239 return nil, useEthereumV, err 240 } 241 header := &types.Header{} 242 if err := rlp.DecodeBytes(cliqueData, header); err != nil { 243 return nil, useEthereumV, err 244 } 245 // The incoming clique header is already truncated, sent to us with a extradata already shortened 246 if len(header.Extra) < 65 { 247 // Need to add it back, to get a suitable length for hashing 248 newExtra := make([]byte, len(header.Extra)+65) 249 copy(newExtra, header.Extra) 250 header.Extra = newExtra 251 } 252 // Get back the rlp data, encoded by us 253 sighash, cliqueRlp, err := cliqueHeaderHashAndRlp(header) 254 if err != nil { 255 return nil, useEthereumV, err 256 } 257 messages := []*NameValueType{ 258 { 259 Name: "Clique header", 260 Typ: "clique", 261 Value: fmt.Sprintf("clique header %d [0x%x]", header.Number, header.Hash()), 262 }, 263 } 264 // Clique uses V on the form 0 or 1 265 useEthereumV = false 266 req = &SignDataRequest{ContentType: mediaType, Rawdata: cliqueRlp, Messages: messages, Hash: sighash} 267 case ApplicationBor.Mime: 268 // Bor consensus 269 stringData, ok := data.(string) 270 if !ok { 271 return nil, useEthereumV, fmt.Errorf("input for %v must be an hex-encoded string", ApplicationBor.Mime) 272 } 273 borData, err := hexutil.Decode(stringData) 274 if err != nil { 275 return nil, useEthereumV, err 276 } 277 header := &types.Header{} 278 if err := rlp.DecodeBytes(borData, header); err != nil { 279 return nil, useEthereumV, err 280 } 281 // The incoming clique header is already truncated, sent to us with a extradata already shortened 282 if len(header.Extra) < 65 { 283 // Need to add it back, to get a suitable length for hashing 284 newExtra := make([]byte, len(header.Extra)+65) 285 copy(newExtra, header.Extra) 286 header.Extra = newExtra 287 } 288 // Get back the rlp data, encoded by us 289 sighash, borRlp, err := borHeaderHashAndRlp(header) 290 if err != nil { 291 return nil, useEthereumV, err 292 } 293 messages := []*NameValueType{ 294 { 295 Name: "Bor header", 296 Typ: "bor", 297 Value: fmt.Sprintf("bor header %d [0x%x]", header.Number, header.Hash()), 298 }, 299 } 300 // Bor uses V on the form 0 or 1 301 useEthereumV = false 302 req = &SignDataRequest{ContentType: mediaType, Rawdata: borRlp, Messages: messages, Hash: sighash} 303 default: // also case TextPlain.Mime: 304 // Calculates an Ethereum ECDSA signature for: 305 // hash = keccak256("\x19${byteVersion}Ethereum Signed Message:\n${message length}${message}") 306 // We expect it to be a string 307 if stringData, ok := data.(string); !ok { 308 return nil, useEthereumV, fmt.Errorf("input for text/plain must be an hex-encoded string") 309 } else { 310 if textData, err := hexutil.Decode(stringData); err != nil { 311 return nil, useEthereumV, err 312 } else { 313 sighash, msg := accounts.TextAndHash(textData) 314 messages := []*NameValueType{ 315 { 316 Name: "message", 317 Typ: accounts.MimetypeTextPlain, 318 Value: msg, 319 }, 320 } 321 req = &SignDataRequest{ContentType: mediaType, Rawdata: []byte(msg), Messages: messages, Hash: sighash} 322 } 323 } 324 } 325 req.Address = addr 326 req.Meta = MetadataFromContext(ctx) 327 return req, useEthereumV, nil 328 } 329 330 // SignTextWithValidator signs the given message which can be further recovered 331 // with the given validator. 332 // hash = keccak256("\x19\x00"${address}${data}). 333 func SignTextValidator(validatorData ValidatorData) (hexutil.Bytes, string) { 334 msg := fmt.Sprintf("\x19\x00%s%s", string(validatorData.Address.Bytes()), string(validatorData.Message)) 335 return crypto.Keccak256([]byte(msg)), msg 336 } 337 338 // cliqueHeaderHashAndRlp returns the hash which is used as input for the proof-of-authority 339 // signing. It is the hash of the entire header apart from the 65 byte signature 340 // contained at the end of the extra data. 341 // 342 // The method requires the extra data to be at least 65 bytes -- the original implementation 343 // in clique.go panics if this is the case, thus it's been reimplemented here to avoid the panic 344 // and simply return an error instead 345 func cliqueHeaderHashAndRlp(header *types.Header) (hash, rlp []byte, err error) { 346 if len(header.Extra) < 65 { 347 err = fmt.Errorf("clique header extradata too short, %d < 65", len(header.Extra)) 348 return 349 } 350 rlp = clique.CliqueRLP(header) 351 hash = clique.SealHash(header).Bytes() 352 return hash, rlp, err 353 } 354 355 // borHeaderHashAndRlp returns the hash which is used as input for the proof-of-authority 356 // signing. It is the hash of the entire header apart from the 65 byte signature 357 // contained at the end of the extra data. 358 // 359 // The method requires the extra data to be at least 65 bytes -- the original implementation 360 // in bor.go panics if this is the case, thus it's been reimplemented here to avoid the panic 361 // and simply return an error instead 362 func borHeaderHashAndRlp(header *types.Header) (hash, rlp []byte, err error) { 363 if len(header.Extra) < 65 { 364 err = fmt.Errorf("bor header extradata too short, %d < 65", len(header.Extra)) 365 return 366 } 367 rlp = bor.BorRLP(header) 368 hash = bor.SealHash(header).Bytes() 369 return hash, rlp, err 370 } 371 372 // SignTypedData signs EIP-712 conformant typed data 373 // hash = keccak256("\x19${byteVersion}${domainSeparator}${hashStruct(message)}") 374 func (api *SignerAPI) SignTypedData(ctx context.Context, addr common.MixedcaseAddress, typedData TypedData) (hexutil.Bytes, error) { 375 domainSeparator, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map()) 376 if err != nil { 377 return nil, err 378 } 379 typedDataHash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message) 380 if err != nil { 381 return nil, err 382 } 383 rawData := []byte(fmt.Sprintf("\x19\x01%s%s", string(domainSeparator), string(typedDataHash))) 384 sighash := crypto.Keccak256(rawData) 385 messages, err := typedData.Format() 386 if err != nil { 387 return nil, err 388 } 389 req := &SignDataRequest{ContentType: DataTyped.Mime, Rawdata: rawData, Messages: messages, Hash: sighash} 390 signature, err := api.sign(addr, req, true) 391 if err != nil { 392 api.UI.ShowError(err.Error()) 393 return nil, err 394 } 395 return signature, nil 396 } 397 398 // HashStruct generates a keccak256 hash of the encoding of the provided data 399 func (typedData *TypedData) HashStruct(primaryType string, data TypedDataMessage) (hexutil.Bytes, error) { 400 encodedData, err := typedData.EncodeData(primaryType, data, 1) 401 if err != nil { 402 return nil, err 403 } 404 return crypto.Keccak256(encodedData), nil 405 } 406 407 // Dependencies returns an array of custom types ordered by their hierarchical reference tree 408 func (typedData *TypedData) Dependencies(primaryType string, found []string) []string { 409 includes := func(arr []string, str string) bool { 410 for _, obj := range arr { 411 if obj == str { 412 return true 413 } 414 } 415 return false 416 } 417 418 if includes(found, primaryType) { 419 return found 420 } 421 if typedData.Types[primaryType] == nil { 422 return found 423 } 424 found = append(found, primaryType) 425 for _, field := range typedData.Types[primaryType] { 426 for _, dep := range typedData.Dependencies(field.Type, found) { 427 if !includes(found, dep) { 428 found = append(found, dep) 429 } 430 } 431 } 432 return found 433 } 434 435 // EncodeType generates the following encoding: 436 // `name ‖ "(" ‖ member₁ ‖ "," ‖ member₂ ‖ "," ‖ … ‖ memberₙ ")"` 437 // 438 // each member is written as `type ‖ " " ‖ name` encodings cascade down and are sorted by name 439 func (typedData *TypedData) EncodeType(primaryType string) hexutil.Bytes { 440 // Get dependencies primary first, then alphabetical 441 deps := typedData.Dependencies(primaryType, []string{}) 442 if len(deps) > 0 { 443 slicedDeps := deps[1:] 444 sort.Strings(slicedDeps) 445 deps = append([]string{primaryType}, slicedDeps...) 446 } 447 448 // Format as a string with fields 449 var buffer bytes.Buffer 450 for _, dep := range deps { 451 buffer.WriteString(dep) 452 buffer.WriteString("(") 453 for _, obj := range typedData.Types[dep] { 454 buffer.WriteString(obj.Type) 455 buffer.WriteString(" ") 456 buffer.WriteString(obj.Name) 457 buffer.WriteString(",") 458 } 459 buffer.Truncate(buffer.Len() - 1) 460 buffer.WriteString(")") 461 } 462 return buffer.Bytes() 463 } 464 465 // TypeHash creates the keccak256 hash of the data 466 func (typedData *TypedData) TypeHash(primaryType string) hexutil.Bytes { 467 return crypto.Keccak256(typedData.EncodeType(primaryType)) 468 } 469 470 // EncodeData generates the following encoding: 471 // `enc(value₁) ‖ enc(value₂) ‖ … ‖ enc(valueₙ)` 472 // 473 // each encoded member is 32-byte long 474 func (typedData *TypedData) EncodeData(primaryType string, data map[string]interface{}, depth int) (hexutil.Bytes, error) { 475 if err := typedData.validate(); err != nil { 476 return nil, err 477 } 478 479 buffer := bytes.Buffer{} 480 481 // Verify extra data 482 if len(typedData.Types[primaryType]) < len(data) { 483 return nil, errors.New("there is extra data provided in the message") 484 } 485 486 // Add typehash 487 buffer.Write(typedData.TypeHash(primaryType)) 488 489 // Add field contents. Structs and arrays have special handlers. 490 for _, field := range typedData.Types[primaryType] { 491 encType := field.Type 492 encValue := data[field.Name] 493 if encType[len(encType)-1:] == "]" { 494 arrayValue, ok := encValue.([]interface{}) 495 if !ok { 496 return nil, dataMismatchError(encType, encValue) 497 } 498 499 arrayBuffer := bytes.Buffer{} 500 parsedType := strings.Split(encType, "[")[0] 501 for _, item := range arrayValue { 502 if typedData.Types[parsedType] != nil { 503 mapValue, ok := item.(map[string]interface{}) 504 if !ok { 505 return nil, dataMismatchError(parsedType, item) 506 } 507 encodedData, err := typedData.EncodeData(parsedType, mapValue, depth+1) 508 if err != nil { 509 return nil, err 510 } 511 arrayBuffer.Write(encodedData) 512 } else { 513 bytesValue, err := typedData.EncodePrimitiveValue(parsedType, item, depth) 514 if err != nil { 515 return nil, err 516 } 517 arrayBuffer.Write(bytesValue) 518 } 519 } 520 521 buffer.Write(crypto.Keccak256(arrayBuffer.Bytes())) 522 } else if typedData.Types[field.Type] != nil { 523 mapValue, ok := encValue.(map[string]interface{}) 524 if !ok { 525 return nil, dataMismatchError(encType, encValue) 526 } 527 encodedData, err := typedData.EncodeData(field.Type, mapValue, depth+1) 528 if err != nil { 529 return nil, err 530 } 531 buffer.Write(crypto.Keccak256(encodedData)) 532 } else { 533 byteValue, err := typedData.EncodePrimitiveValue(encType, encValue, depth) 534 if err != nil { 535 return nil, err 536 } 537 buffer.Write(byteValue) 538 } 539 } 540 return buffer.Bytes(), nil 541 } 542 543 func parseInteger(encType string, encValue interface{}) (*big.Int, error) { 544 var ( 545 length = 0 546 signed = strings.HasPrefix(encType, "int") 547 b *big.Int 548 ) 549 if encType == "int" || encType == "uint" { 550 length = 256 551 } else { 552 lengthStr := "" 553 if strings.HasPrefix(encType, "uint") { 554 lengthStr = strings.TrimPrefix(encType, "uint") 555 } else { 556 lengthStr = strings.TrimPrefix(encType, "int") 557 } 558 atoiSize, err := strconv.Atoi(lengthStr) 559 if err != nil { 560 return nil, fmt.Errorf("invalid size on integer: %v", lengthStr) 561 } 562 length = atoiSize 563 } 564 switch v := encValue.(type) { 565 case *math.HexOrDecimal256: 566 b = (*big.Int)(v) 567 case string: 568 var hexIntValue math.HexOrDecimal256 569 if err := hexIntValue.UnmarshalText([]byte(v)); err != nil { 570 return nil, err 571 } 572 b = (*big.Int)(&hexIntValue) 573 case float64: 574 // JSON parses non-strings as float64. Fail if we cannot 575 // convert it losslessly 576 if float64(int64(v)) == v { 577 b = big.NewInt(int64(v)) 578 } else { 579 return nil, fmt.Errorf("invalid float value %v for type %v", v, encType) 580 } 581 } 582 if b == nil { 583 return nil, fmt.Errorf("invalid integer value %v/%v for type %v", encValue, reflect.TypeOf(encValue), encType) 584 } 585 if b.BitLen() > length { 586 return nil, fmt.Errorf("integer larger than '%v'", encType) 587 } 588 if !signed && b.Sign() == -1 { 589 return nil, fmt.Errorf("invalid negative value for unsigned type %v", encType) 590 } 591 return b, nil 592 } 593 594 // EncodePrimitiveValue deals with the primitive values found 595 // while searching through the typed data 596 func (typedData *TypedData) EncodePrimitiveValue(encType string, encValue interface{}, depth int) ([]byte, error) { 597 switch encType { 598 case "address": 599 stringValue, ok := encValue.(string) 600 if !ok || !common.IsHexAddress(stringValue) { 601 return nil, dataMismatchError(encType, encValue) 602 } 603 retval := make([]byte, 32) 604 copy(retval[12:], common.HexToAddress(stringValue).Bytes()) 605 return retval, nil 606 case "bool": 607 boolValue, ok := encValue.(bool) 608 if !ok { 609 return nil, dataMismatchError(encType, encValue) 610 } 611 if boolValue { 612 return math.PaddedBigBytes(common.Big1, 32), nil 613 } 614 return math.PaddedBigBytes(common.Big0, 32), nil 615 case "string": 616 strVal, ok := encValue.(string) 617 if !ok { 618 return nil, dataMismatchError(encType, encValue) 619 } 620 return crypto.Keccak256([]byte(strVal)), nil 621 case "bytes": 622 bytesValue, ok := encValue.([]byte) 623 if !ok { 624 return nil, dataMismatchError(encType, encValue) 625 } 626 return crypto.Keccak256(bytesValue), nil 627 } 628 if strings.HasPrefix(encType, "bytes") { 629 lengthStr := strings.TrimPrefix(encType, "bytes") 630 length, err := strconv.Atoi(lengthStr) 631 if err != nil { 632 return nil, fmt.Errorf("invalid size on bytes: %v", lengthStr) 633 } 634 if length < 0 || length > 32 { 635 return nil, fmt.Errorf("invalid size on bytes: %d", length) 636 } 637 if byteValue, ok := encValue.(hexutil.Bytes); !ok { 638 return nil, dataMismatchError(encType, encValue) 639 } else { 640 return math.PaddedBigBytes(new(big.Int).SetBytes(byteValue), 32), nil 641 } 642 } 643 if strings.HasPrefix(encType, "int") || strings.HasPrefix(encType, "uint") { 644 b, err := parseInteger(encType, encValue) 645 if err != nil { 646 return nil, err 647 } 648 return abi.U256(b), nil 649 } 650 return nil, fmt.Errorf("unrecognized type '%s'", encType) 651 652 } 653 654 // dataMismatchError generates an error for a mismatch between 655 // the provided type and data 656 func dataMismatchError(encType string, encValue interface{}) error { 657 return fmt.Errorf("provided data '%v' doesn't match type '%s'", encValue, encType) 658 } 659 660 // EcRecover recovers the address associated with the given sig. 661 // Only compatible with `text/plain` 662 func (api *SignerAPI) EcRecover(ctx context.Context, data hexutil.Bytes, sig hexutil.Bytes) (common.Address, error) { 663 // Returns the address for the Account that was used to create the signature. 664 // 665 // Note, this function is compatible with eth_sign and personal_sign. As such it recovers 666 // the address of: 667 // hash = keccak256("\x19${byteVersion}Ethereum Signed Message:\n${message length}${message}") 668 // addr = ecrecover(hash, signature) 669 // 670 // Note, the signature must conform to the secp256k1 curve R, S and V values, where 671 // the V value must be be 27 or 28 for legacy reasons. 672 // 673 // https://github.com/maticnetwork/bor/wiki/Management-APIs#personal_ecRecover 674 if len(sig) != 65 { 675 return common.Address{}, fmt.Errorf("signature must be 65 bytes long") 676 } 677 if sig[64] != 27 && sig[64] != 28 { 678 return common.Address{}, fmt.Errorf("invalid Ethereum signature (V is not 27 or 28)") 679 } 680 sig[64] -= 27 // Transform yellow paper V from 27/28 to 0/1 681 hash := accounts.TextHash(data) 682 rpk, err := crypto.SigToPub(hash, sig) 683 if err != nil { 684 return common.Address{}, err 685 } 686 return crypto.PubkeyToAddress(*rpk), nil 687 } 688 689 // UnmarshalValidatorData converts the bytes input to typed data 690 func UnmarshalValidatorData(data interface{}) (ValidatorData, error) { 691 raw, ok := data.(map[string]interface{}) 692 if !ok { 693 return ValidatorData{}, errors.New("validator input is not a map[string]interface{}") 694 } 695 addr, ok := raw["address"].(string) 696 if !ok { 697 return ValidatorData{}, errors.New("validator address is not sent as a string") 698 } 699 addrBytes, err := hexutil.Decode(addr) 700 if err != nil { 701 return ValidatorData{}, err 702 } 703 if !ok || len(addrBytes) == 0 { 704 return ValidatorData{}, errors.New("validator address is undefined") 705 } 706 707 message, ok := raw["message"].(string) 708 if !ok { 709 return ValidatorData{}, errors.New("message is not sent as a string") 710 } 711 messageBytes, err := hexutil.Decode(message) 712 if err != nil { 713 return ValidatorData{}, err 714 } 715 if !ok || len(messageBytes) == 0 { 716 return ValidatorData{}, errors.New("message is undefined") 717 } 718 719 return ValidatorData{ 720 Address: common.BytesToAddress(addrBytes), 721 Message: messageBytes, 722 }, nil 723 } 724 725 // validate makes sure the types are sound 726 func (typedData *TypedData) validate() error { 727 if err := typedData.Types.validate(); err != nil { 728 return err 729 } 730 if err := typedData.Domain.validate(); err != nil { 731 return err 732 } 733 return nil 734 } 735 736 // Map generates a map version of the typed data 737 func (typedData *TypedData) Map() map[string]interface{} { 738 dataMap := map[string]interface{}{ 739 "types": typedData.Types, 740 "domain": typedData.Domain.Map(), 741 "primaryType": typedData.PrimaryType, 742 "message": typedData.Message, 743 } 744 return dataMap 745 } 746 747 // Format returns a representation of typedData, which can be easily displayed by a user-interface 748 // without in-depth knowledge about 712 rules 749 func (typedData *TypedData) Format() ([]*NameValueType, error) { 750 domain, err := typedData.formatData("EIP712Domain", typedData.Domain.Map()) 751 if err != nil { 752 return nil, err 753 } 754 ptype, err := typedData.formatData(typedData.PrimaryType, typedData.Message) 755 if err != nil { 756 return nil, err 757 } 758 var nvts []*NameValueType 759 nvts = append(nvts, &NameValueType{ 760 Name: "EIP712Domain", 761 Value: domain, 762 Typ: "domain", 763 }) 764 nvts = append(nvts, &NameValueType{ 765 Name: typedData.PrimaryType, 766 Value: ptype, 767 Typ: "primary type", 768 }) 769 return nvts, nil 770 } 771 772 func (typedData *TypedData) formatData(primaryType string, data map[string]interface{}) ([]*NameValueType, error) { 773 var output []*NameValueType 774 775 // Add field contents. Structs and arrays have special handlers. 776 for _, field := range typedData.Types[primaryType] { 777 encName := field.Name 778 encValue := data[encName] 779 item := &NameValueType{ 780 Name: encName, 781 Typ: field.Type, 782 } 783 if field.isArray() { 784 arrayValue, _ := encValue.([]interface{}) 785 parsedType := field.typeName() 786 for _, v := range arrayValue { 787 if typedData.Types[parsedType] != nil { 788 mapValue, _ := v.(map[string]interface{}) 789 mapOutput, err := typedData.formatData(parsedType, mapValue) 790 if err != nil { 791 return nil, err 792 } 793 item.Value = mapOutput 794 } else { 795 primitiveOutput, err := formatPrimitiveValue(field.Type, encValue) 796 if err != nil { 797 return nil, err 798 } 799 item.Value = primitiveOutput 800 } 801 } 802 } else if typedData.Types[field.Type] != nil { 803 if mapValue, ok := encValue.(map[string]interface{}); ok { 804 mapOutput, err := typedData.formatData(field.Type, mapValue) 805 if err != nil { 806 return nil, err 807 } 808 item.Value = mapOutput 809 } else { 810 item.Value = "<nil>" 811 } 812 } else { 813 primitiveOutput, err := formatPrimitiveValue(field.Type, encValue) 814 if err != nil { 815 return nil, err 816 } 817 item.Value = primitiveOutput 818 } 819 output = append(output, item) 820 } 821 return output, nil 822 } 823 824 func formatPrimitiveValue(encType string, encValue interface{}) (string, error) { 825 switch encType { 826 case "address": 827 if stringValue, ok := encValue.(string); !ok { 828 return "", fmt.Errorf("could not format value %v as address", encValue) 829 } else { 830 return common.HexToAddress(stringValue).String(), nil 831 } 832 case "bool": 833 if boolValue, ok := encValue.(bool); !ok { 834 return "", fmt.Errorf("could not format value %v as bool", encValue) 835 } else { 836 return fmt.Sprintf("%t", boolValue), nil 837 } 838 case "bytes", "string": 839 return fmt.Sprintf("%s", encValue), nil 840 } 841 if strings.HasPrefix(encType, "bytes") { 842 return fmt.Sprintf("%s", encValue), nil 843 844 } 845 if strings.HasPrefix(encType, "uint") || strings.HasPrefix(encType, "int") { 846 if b, err := parseInteger(encType, encValue); err != nil { 847 return "", err 848 } else { 849 return fmt.Sprintf("%d (0x%x)", b, b), nil 850 } 851 } 852 return "", fmt.Errorf("unhandled type %v", encType) 853 } 854 855 // NameValueType is a very simple struct with Name, Value and Type. It's meant for simple 856 // json structures used to communicate signing-info about typed data with the UI 857 type NameValueType struct { 858 Name string `json:"name"` 859 Value interface{} `json:"value"` 860 Typ string `json:"type"` 861 } 862 863 // Pprint returns a pretty-printed version of nvt 864 func (nvt *NameValueType) Pprint(depth int) string { 865 output := bytes.Buffer{} 866 output.WriteString(strings.Repeat("\u00a0", depth*2)) 867 output.WriteString(fmt.Sprintf("%s [%s]: ", nvt.Name, nvt.Typ)) 868 if nvts, ok := nvt.Value.([]*NameValueType); ok { 869 output.WriteString("\n") 870 for _, next := range nvts { 871 sublevel := next.Pprint(depth + 1) 872 output.WriteString(sublevel) 873 } 874 } else { 875 output.WriteString(fmt.Sprintf("%q\n", nvt.Value)) 876 } 877 return output.String() 878 } 879 880 // Validate checks if the types object is conformant to the specs 881 func (t Types) validate() error { 882 for typeKey, typeArr := range t { 883 if len(typeKey) == 0 { 884 return fmt.Errorf("empty type key") 885 } 886 for i, typeObj := range typeArr { 887 if len(typeObj.Type) == 0 { 888 return fmt.Errorf("type %v:%d: empty Type", typeKey, i) 889 } 890 if len(typeObj.Name) == 0 { 891 return fmt.Errorf("type %v:%d: empty Name", typeKey, i) 892 } 893 if typeKey == typeObj.Type { 894 return fmt.Errorf("type '%s' cannot reference itself", typeObj.Type) 895 } 896 if typeObj.isReferenceType() { 897 if _, exist := t[typeObj.typeName()]; !exist { 898 return fmt.Errorf("reference type '%s' is undefined", typeObj.Type) 899 } 900 if !typedDataReferenceTypeRegexp.MatchString(typeObj.Type) { 901 return fmt.Errorf("unknown reference type '%s", typeObj.Type) 902 } 903 } else if !isPrimitiveTypeValid(typeObj.Type) { 904 return fmt.Errorf("unknown type '%s'", typeObj.Type) 905 } 906 } 907 } 908 return nil 909 } 910 911 // Checks if the primitive value is valid 912 func isPrimitiveTypeValid(primitiveType string) bool { 913 if primitiveType == "address" || 914 primitiveType == "address[]" || 915 primitiveType == "bool" || 916 primitiveType == "bool[]" || 917 primitiveType == "string" || 918 primitiveType == "string[]" { 919 return true 920 } 921 if primitiveType == "bytes" || 922 primitiveType == "bytes[]" || 923 primitiveType == "bytes1" || 924 primitiveType == "bytes1[]" || 925 primitiveType == "bytes2" || 926 primitiveType == "bytes2[]" || 927 primitiveType == "bytes3" || 928 primitiveType == "bytes3[]" || 929 primitiveType == "bytes4" || 930 primitiveType == "bytes4[]" || 931 primitiveType == "bytes5" || 932 primitiveType == "bytes5[]" || 933 primitiveType == "bytes6" || 934 primitiveType == "bytes6[]" || 935 primitiveType == "bytes7" || 936 primitiveType == "bytes7[]" || 937 primitiveType == "bytes8" || 938 primitiveType == "bytes8[]" || 939 primitiveType == "bytes9" || 940 primitiveType == "bytes9[]" || 941 primitiveType == "bytes10" || 942 primitiveType == "bytes10[]" || 943 primitiveType == "bytes11" || 944 primitiveType == "bytes11[]" || 945 primitiveType == "bytes12" || 946 primitiveType == "bytes12[]" || 947 primitiveType == "bytes13" || 948 primitiveType == "bytes13[]" || 949 primitiveType == "bytes14" || 950 primitiveType == "bytes14[]" || 951 primitiveType == "bytes15" || 952 primitiveType == "bytes15[]" || 953 primitiveType == "bytes16" || 954 primitiveType == "bytes16[]" || 955 primitiveType == "bytes17" || 956 primitiveType == "bytes17[]" || 957 primitiveType == "bytes18" || 958 primitiveType == "bytes18[]" || 959 primitiveType == "bytes19" || 960 primitiveType == "bytes19[]" || 961 primitiveType == "bytes20" || 962 primitiveType == "bytes20[]" || 963 primitiveType == "bytes21" || 964 primitiveType == "bytes21[]" || 965 primitiveType == "bytes22" || 966 primitiveType == "bytes22[]" || 967 primitiveType == "bytes23" || 968 primitiveType == "bytes23[]" || 969 primitiveType == "bytes24" || 970 primitiveType == "bytes24[]" || 971 primitiveType == "bytes25" || 972 primitiveType == "bytes25[]" || 973 primitiveType == "bytes26" || 974 primitiveType == "bytes26[]" || 975 primitiveType == "bytes27" || 976 primitiveType == "bytes27[]" || 977 primitiveType == "bytes28" || 978 primitiveType == "bytes28[]" || 979 primitiveType == "bytes29" || 980 primitiveType == "bytes29[]" || 981 primitiveType == "bytes30" || 982 primitiveType == "bytes30[]" || 983 primitiveType == "bytes31" || 984 primitiveType == "bytes31[]" { 985 return true 986 } 987 if primitiveType == "int" || 988 primitiveType == "int[]" || 989 primitiveType == "int8" || 990 primitiveType == "int8[]" || 991 primitiveType == "int16" || 992 primitiveType == "int16[]" || 993 primitiveType == "int32" || 994 primitiveType == "int32[]" || 995 primitiveType == "int64" || 996 primitiveType == "int64[]" || 997 primitiveType == "int128" || 998 primitiveType == "int128[]" || 999 primitiveType == "int256" || 1000 primitiveType == "int256[]" { 1001 return true 1002 } 1003 if primitiveType == "uint" || 1004 primitiveType == "uint[]" || 1005 primitiveType == "uint8" || 1006 primitiveType == "uint8[]" || 1007 primitiveType == "uint16" || 1008 primitiveType == "uint16[]" || 1009 primitiveType == "uint32" || 1010 primitiveType == "uint32[]" || 1011 primitiveType == "uint64" || 1012 primitiveType == "uint64[]" || 1013 primitiveType == "uint128" || 1014 primitiveType == "uint128[]" || 1015 primitiveType == "uint256" || 1016 primitiveType == "uint256[]" { 1017 return true 1018 } 1019 return false 1020 } 1021 1022 // validate checks if the given domain is valid, i.e. contains at least 1023 // the minimum viable keys and values 1024 func (domain *TypedDataDomain) validate() error { 1025 if domain.ChainId == nil { 1026 return errors.New("chainId must be specified according to EIP-155") 1027 } 1028 1029 if len(domain.Name) == 0 && len(domain.Version) == 0 && len(domain.VerifyingContract) == 0 && len(domain.Salt) == 0 { 1030 return errors.New("domain is undefined") 1031 } 1032 1033 return nil 1034 } 1035 1036 // Map is a helper function to generate a map version of the domain 1037 func (domain *TypedDataDomain) Map() map[string]interface{} { 1038 dataMap := map[string]interface{}{} 1039 1040 if domain.ChainId != nil { 1041 dataMap["chainId"] = domain.ChainId 1042 } 1043 1044 if len(domain.Name) > 0 { 1045 dataMap["name"] = domain.Name 1046 } 1047 1048 if len(domain.Version) > 0 { 1049 dataMap["version"] = domain.Version 1050 } 1051 1052 if len(domain.VerifyingContract) > 0 { 1053 dataMap["verifyingContract"] = domain.VerifyingContract 1054 } 1055 1056 if len(domain.Salt) > 0 { 1057 dataMap["salt"] = domain.Salt 1058 } 1059 return dataMap 1060 }