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