github.com/boxboat/in-toto-golang@v0.0.3-0.20210303203820-2fa16ecbe6f6/in_toto/model.go (about) 1 package in_toto 2 3 import ( 4 "crypto/ecdsa" 5 "crypto/rsa" 6 "crypto/x509" 7 "encoding/json" 8 "errors" 9 "fmt" 10 "io/ioutil" 11 "os" 12 "reflect" 13 "regexp" 14 "strconv" 15 "strings" 16 "time" 17 ) 18 19 /* 20 KeyVal contains the actual values of a key, as opposed to key metadata such as 21 a key identifier or key type. For RSA keys, the key value is a pair of public 22 and private keys in PEM format stored as strings. For public keys the Private 23 field may be an empty string. 24 */ 25 type KeyVal struct { 26 Private string `json:"private"` 27 Public string `json:"public"` 28 Certificate string `json:"-"` // We just use this for signing, no need to store it 29 } 30 31 /* 32 Key represents a generic in-toto key that contains key metadata, such as an 33 identifier, supported hash algorithms to create the identifier, the key type 34 and the supported signature scheme, and the actual key value. 35 */ 36 type Key struct { 37 KeyID string `json:"keyid"` 38 KeyIDHashAlgorithms []string `json:"keyid_hash_algorithms"` 39 KeyType string `json:"keytype"` 40 KeyVal KeyVal `json:"keyval"` 41 Scheme string `json:"scheme"` 42 } 43 44 // ErrEmptyKeyField will be thrown if a field in our Key struct is empty. 45 var ErrEmptyKeyField = errors.New("empty field in key") 46 47 // ErrInvalidHexString will be thrown, if a string doesn't match a hex string. 48 var ErrInvalidHexString = errors.New("invalid hex string") 49 50 // ErrSchemeKeyTypeMismatch will be thrown, if the given scheme and key type are not supported together. 51 var ErrSchemeKeyTypeMismatch = errors.New("the scheme and key type are not supported together") 52 53 // ErrUnsupportedKeyIDHashAlgorithms will be thrown, if the specified KeyIDHashAlgorithms is not supported. 54 var ErrUnsupportedKeyIDHashAlgorithms = errors.New("the given keyID hash algorithm is not supported") 55 56 // ErrKeyKeyTypeMismatch will be thrown, if the specified keyType does not match the key 57 var ErrKeyKeyTypeMismatch = errors.New("the given key does not match its key type") 58 59 // ErrNoPublicKey gets returned when the private key value is not empty. 60 var ErrNoPublicKey = errors.New("the given key is not a public key") 61 62 // ErrCurveSizeSchemeMismatch gets returned, when the scheme and curve size are incompatible 63 // for example: curve size = "521" and scheme = "ecdsa-sha2-nistp224" 64 var ErrCurveSizeSchemeMismatch = errors.New("the scheme does not match the curve size") 65 66 /* 67 matchEcdsaScheme checks if the scheme suffix, matches the ecdsa key 68 curve size. We do not need a full regex match here, because 69 our validateKey functions are already checking for a valid scheme string. 70 */ 71 func matchEcdsaScheme(curveSize int, scheme string) error { 72 if !strings.HasSuffix(scheme, strconv.Itoa(curveSize)) { 73 return ErrCurveSizeSchemeMismatch 74 } 75 return nil 76 } 77 78 /* 79 validateHexString is used to validate that a string passed to it contains 80 only valid hexadecimal characters. 81 */ 82 func validateHexString(str string) error { 83 formatCheck, _ := regexp.MatchString("^[a-fA-F0-9]+$", str) 84 if !formatCheck { 85 return fmt.Errorf("%w: %s", ErrInvalidHexString, str) 86 } 87 return nil 88 } 89 90 /* 91 validateKeyVal validates the KeyVal struct. In case of an ed25519 key, 92 it will check for a hex string for private and public key. In any other 93 case, validateKeyVal will try to decode the PEM block. If this succeeds, 94 we have a valid PEM block in our KeyVal struct. On success it will return nil 95 on failure it will return the corresponding error. This can be either 96 an ErrInvalidHexString, an ErrNoPEMBlock or an ErrUnsupportedKeyType 97 if the KeyType is unknown. 98 */ 99 func validateKeyVal(key Key) error { 100 switch key.KeyType { 101 case ed25519KeyType: 102 // We cannot use matchPublicKeyKeyType or matchPrivateKeyKeyType here, 103 // because we retrieve the key not from PEM. Hence we are dealing with 104 // plain ed25519 key bytes. These bytes can't be typechecked like in the 105 // matchKeyKeytype functions. 106 err := validateHexString(key.KeyVal.Public) 107 if err != nil { 108 return err 109 } 110 if key.KeyVal.Private != "" { 111 err := validateHexString(key.KeyVal.Private) 112 if err != nil { 113 return err 114 } 115 } 116 case rsaKeyType, ecdsaKeyType: 117 // We do not need the pemData here, so we can throw it away via '_' 118 _, parsedKey, err := decodeAndParse([]byte(key.KeyVal.Public)) 119 if err != nil { 120 return err 121 } 122 err = matchPublicKeyKeyType(parsedKey, key.KeyType) 123 if err != nil { 124 return err 125 } 126 if key.KeyVal.Private != "" { 127 // We do not need the pemData here, so we can throw it away via '_' 128 _, parsedKey, err := decodeAndParse([]byte(key.KeyVal.Private)) 129 if err != nil { 130 return err 131 } 132 err = matchPrivateKeyKeyType(parsedKey, key.KeyType) 133 if err != nil { 134 return err 135 } 136 } 137 default: 138 return ErrUnsupportedKeyType 139 } 140 return nil 141 } 142 143 /* 144 matchPublicKeyKeyType validates an interface if it can be asserted to a 145 the RSA or ECDSA public key type. We can only check RSA and ECDSA this way, 146 because we are storing them in PEM format. Ed25519 keys are stored as plain 147 ed25519 keys encoded as hex strings, thus we have no metadata for them. 148 This function will return nil on success. If the key type does not match 149 it will return an ErrKeyKeyTypeMismatch. 150 */ 151 func matchPublicKeyKeyType(key interface{}, keyType string) error { 152 switch key.(type) { 153 case *rsa.PublicKey: 154 if keyType != rsaKeyType { 155 return ErrKeyKeyTypeMismatch 156 } 157 case *ecdsa.PublicKey: 158 if keyType != ecdsaKeyType { 159 return ErrKeyKeyTypeMismatch 160 } 161 default: 162 return ErrInvalidKey 163 } 164 return nil 165 } 166 167 /* 168 matchPrivateKeyKeyType validates an interface if it can be asserted to a 169 the RSA or ECDSA private key type. We can only check RSA and ECDSA this way, 170 because we are storing them in PEM format. Ed25519 keys are stored as plain 171 ed25519 keys encoded as hex strings, thus we have no metadata for them. 172 This function will return nil on success. If the key type does not match 173 it will return an ErrKeyKeyTypeMismatch. 174 */ 175 func matchPrivateKeyKeyType(key interface{}, keyType string) error { 176 // we can only check RSA and ECDSA this way, because we are storing them in PEM 177 // format. ed25519 keys are stored as plain ed25519 keys encoded as hex strings 178 // so we have no metadata for them. 179 switch key.(type) { 180 case *rsa.PrivateKey: 181 if keyType != rsaKeyType { 182 return ErrKeyKeyTypeMismatch 183 } 184 case *ecdsa.PrivateKey: 185 if keyType != ecdsaKeyType { 186 return ErrKeyKeyTypeMismatch 187 } 188 default: 189 return ErrInvalidKey 190 } 191 return nil 192 } 193 194 /* 195 matchKeyTypeScheme checks if the specified scheme matches our specified 196 keyType. If the keyType is not supported it will return an 197 ErrUnsupportedKeyType. If the keyType and scheme do not match it will return 198 an ErrSchemeKeyTypeMismatch. If the specified keyType and scheme are 199 compatible matchKeyTypeScheme will return nil. 200 */ 201 func matchKeyTypeScheme(key Key) error { 202 switch key.KeyType { 203 case rsaKeyType: 204 for _, scheme := range getSupportedRSASchemes() { 205 if key.Scheme == scheme { 206 return nil 207 } 208 } 209 case ed25519KeyType: 210 for _, scheme := range getSupportedEd25519Schemes() { 211 if key.Scheme == scheme { 212 return nil 213 } 214 } 215 case ecdsaKeyType: 216 for _, scheme := range getSupportedEcdsaSchemes() { 217 if key.Scheme == scheme { 218 return nil 219 } 220 } 221 default: 222 return fmt.Errorf("%w: %s", ErrUnsupportedKeyType, key.KeyType) 223 } 224 return ErrSchemeKeyTypeMismatch 225 } 226 227 /* 228 validateKey checks the outer key object (everything, except the KeyVal struct). 229 It verifies the keyID for being a hex string and checks for empty fields. 230 On success it will return nil, on error it will return the corresponding error. 231 Either: ErrEmptyKeyField or ErrInvalidHexString. 232 */ 233 func validateKey(key Key) error { 234 err := validateHexString(key.KeyID) 235 if err != nil { 236 return err 237 } 238 // This probably can be done more elegant with reflection 239 // but we care about performance, do we?! 240 if key.KeyType == "" { 241 return fmt.Errorf("%w: keytype", ErrEmptyKeyField) 242 } 243 if key.KeyVal.Public == "" { 244 return fmt.Errorf("%w: keyval.public", ErrEmptyKeyField) 245 } 246 if key.Scheme == "" { 247 return fmt.Errorf("%w: scheme", ErrEmptyKeyField) 248 } 249 err = matchKeyTypeScheme(key) 250 if err != nil { 251 return err 252 } 253 // only check for supported KeyIDHashAlgorithms, if the variable has been set 254 if key.KeyIDHashAlgorithms != nil { 255 supportedKeyIDHashAlgorithms := getSupportedKeyIDHashAlgorithms() 256 if !supportedKeyIDHashAlgorithms.IsSubSet(NewSet(key.KeyIDHashAlgorithms...)) { 257 return fmt.Errorf("%w: %#v, supported are: %#v", ErrUnsupportedKeyIDHashAlgorithms, key.KeyIDHashAlgorithms, getSupportedKeyIDHashAlgorithms()) 258 } 259 } 260 return nil 261 } 262 263 /* 264 validatePublicKey is a wrapper around validateKey. It test if the private key 265 value in the key is empty and then validates the key via calling validateKey. 266 On success it will return nil, on error it will return an ErrNoPublicKey error. 267 */ 268 func validatePublicKey(key Key) error { 269 if key.KeyVal.Private != "" { 270 return ErrNoPublicKey 271 } 272 err := validateKey(key) 273 if err != nil { 274 return err 275 } 276 return nil 277 } 278 279 // TODO: implement this 280 func validateCertificates(cert []string) error { 281 return nil 282 } 283 284 /* 285 Signature represents a generic in-toto signature that contains the identifier 286 of the Key, which was used to create the signature and the signature data. The 287 used signature scheme is found in the corresponding Key. 288 */ 289 type Signature struct { 290 KeyID string `json:"keyid"` 291 Sig string `json:"sig"` 292 Certificate string `json:"cert"` 293 } 294 295 // GetCertificate returns the parsed x509 certificate attached to the signature, 296 // if it exists. 297 func (sig Signature) GetCertificate() (Key, error) { 298 key := Key{} 299 if len(sig.Certificate) == 0 { 300 return key, errors.New("Signature has empty Certificate") 301 } 302 303 err := key.LoadKeyReaderDefaults(strings.NewReader(sig.Certificate)) 304 return key, err 305 } 306 307 /* 308 validateSignature is a function used to check if a passed signature is valid, 309 by inspecting the key ID and the signature itself. 310 */ 311 func validateSignature(signature Signature) error { 312 if err := validateHexString(signature.KeyID); err != nil { 313 return err 314 } 315 if err := validateHexString(signature.Sig); err != nil { 316 return err 317 } 318 return nil 319 } 320 321 /* 322 validateSliceOfSignatures is a helper function used to validate multiple 323 signatures stored in a slice. 324 */ 325 func validateSliceOfSignatures(slice []Signature) error { 326 for _, signature := range slice { 327 if err := validateSignature(signature); err != nil { 328 return err 329 } 330 } 331 return nil 332 } 333 334 /* 335 Link represents the evidence of a supply chain step performed by a functionary. 336 It should be contained in a generic Metablock object, which provides 337 functionality for signing and signature verification, and reading from and 338 writing to disk. 339 */ 340 type Link struct { 341 Type string `json:"_type"` 342 Name string `json:"name"` 343 Materials map[string]interface{} `json:"materials"` 344 Products map[string]interface{} `json:"products"` 345 ByProducts map[string]interface{} `json:"byproducts"` 346 Command []string `json:"command"` 347 Environment map[string]interface{} `json:"environment"` 348 } 349 350 /* 351 validateArtifacts is a general function used to validate products and materials. 352 */ 353 func validateArtifacts(artifacts map[string]interface{}) error { 354 for artifactName, artifact := range artifacts { 355 artifactValue := reflect.ValueOf(artifact).MapRange() 356 for artifactValue.Next() { 357 value := artifactValue.Value().Interface().(string) 358 hashType := artifactValue.Key().Interface().(string) 359 if err := validateHexString(value); err != nil { 360 return fmt.Errorf("in artifact '%s', %s hash value: %s", 361 artifactName, hashType, err.Error()) 362 } 363 } 364 } 365 return nil 366 } 367 368 /* 369 validateLink is a function used to ensure that a passed item of type Link 370 matches the necessary format. 371 */ 372 func validateLink(link Link) error { 373 if link.Type != "link" { 374 return fmt.Errorf("invalid type for link '%s': should be 'link'", 375 link.Name) 376 } 377 378 if err := validateArtifacts(link.Materials); err != nil { 379 return fmt.Errorf("in materials of link '%s': %s", link.Name, 380 err.Error()) 381 } 382 383 if err := validateArtifacts(link.Products); err != nil { 384 return fmt.Errorf("in products of link '%s': %s", link.Name, 385 err.Error()) 386 } 387 388 return nil 389 } 390 391 /* 392 LinkNameFormat represents a format string used to create the filename for a 393 signed Link (wrapped in a Metablock). It consists of the name of the link and 394 the first 8 characters of the signing key id. E.g.: 395 fmt.Sprintf(LinkNameFormat, "package", 396 "2f89b9272acfc8f4a0a0f094d789fdb0ba798b0fe41f2f5f417c12f0085ff498") 397 // returns "package.2f89b9272.link" 398 */ 399 const LinkNameFormat = "%s.%.8s.link" 400 const PreliminaryLinkNameFormat = ".%s.%.8s.link-unfinished" 401 402 /* 403 LinkNameFormatShort is for links that are not signed, e.g.: 404 fmt.Sprintf(LinkNameFormatShort, "unsigned") 405 // returns "unsigned.link" 406 */ 407 const LinkNameFormatShort = "%s.link" 408 const LinkGlobFormat = "%s.????????.link" 409 410 /* 411 SublayoutLinkDirFormat represents the format of the name of the directory for 412 sublayout links during the verification workflow. 413 */ 414 const SublayoutLinkDirFormat = "%s.%.8s" 415 416 /* 417 SupplyChainItem summarizes common fields of the two available supply chain 418 item types, Inspection and Step. 419 */ 420 type SupplyChainItem struct { 421 Name string `json:"name"` 422 ExpectedMaterials [][]string `json:"expected_materials"` 423 ExpectedProducts [][]string `json:"expected_products"` 424 } 425 426 /* 427 validateArtifactRule calls UnpackRule to validate that the passed rule conforms 428 with any of the available rule formats. 429 */ 430 func validateArtifactRule(rule []string) error { 431 if _, err := UnpackRule(rule); err != nil { 432 return err 433 } 434 return nil 435 } 436 437 /* 438 validateSliceOfArtifactRules iterates over passed rules to validate them. 439 */ 440 func validateSliceOfArtifactRules(rules [][]string) error { 441 for _, rule := range rules { 442 if err := validateArtifactRule(rule); err != nil { 443 return err 444 } 445 } 446 return nil 447 } 448 449 /* 450 validateSupplyChainItem is used to validate the common elements found in both 451 steps and inspections. Here, the function primarily ensures that the name of 452 a supply chain item isn't empty. 453 */ 454 func validateSupplyChainItem(item SupplyChainItem) error { 455 if item.Name == "" { 456 return fmt.Errorf("name cannot be empty") 457 } 458 459 if err := validateSliceOfArtifactRules(item.ExpectedMaterials); err != nil { 460 return fmt.Errorf("invalid material rule: %s", err) 461 } 462 if err := validateSliceOfArtifactRules(item.ExpectedProducts); err != nil { 463 return fmt.Errorf("invalid product rule: %s", err) 464 } 465 return nil 466 } 467 468 /* 469 Inspection represents an in-toto supply chain inspection, whose command in the 470 Run field is executed during final product verification, generating unsigned 471 link metadata. Materials and products used/produced by the inspection are 472 constrained by the artifact rules in the inspection's ExpectedMaterials and 473 ExpectedProducts fields. 474 */ 475 type Inspection struct { 476 Type string `json:"_type"` 477 Run []string `json:"run"` 478 SupplyChainItem 479 } 480 481 /* 482 validateInspection ensures that a passed inspection is valid and matches the 483 necessary format of an inspection. 484 */ 485 func validateInspection(inspection Inspection) error { 486 if err := validateSupplyChainItem(inspection.SupplyChainItem); err != nil { 487 return fmt.Errorf("inspection %s", err.Error()) 488 } 489 if inspection.Type != "inspection" { 490 return fmt.Errorf("invalid Type value for inspection '%s': should be "+ 491 "'inspection'", inspection.SupplyChainItem.Name) 492 } 493 return nil 494 } 495 496 /* 497 Step represents an in-toto step of the supply chain performed by a functionary. 498 During final product verification in-toto looks for corresponding Link 499 metadata, which is used as signed evidence that the step was performed 500 according to the supply chain definition. Materials and products used/produced 501 by the step are constrained by the artifact rules in the step's 502 ExpectedMaterials and ExpectedProducts fields. 503 */ 504 type Step struct { 505 Type string `json:"_type"` 506 PubKeys []string `json:"pubkeys"` 507 CertificateConstraints []CertificateConstraint `json:"cert_constraints"` 508 ExpectedCommand []string `json:"expected_command"` 509 Threshold int `json:"threshold"` 510 SupplyChainItem 511 } 512 513 // CheckCertConstraints returns true if the provided certificate matches at least one 514 // of the constraints for this step. 515 func (s Step) CheckCertConstraints(key Key) bool { 516 _, possibleCert, err := decodeAndParse([]byte(key.KeyVal.Certificate)) 517 if err != nil { 518 return false 519 } 520 521 cert, ok := possibleCert.(*x509.Certificate) 522 if !ok { 523 return false 524 } 525 526 for _, constraint := range s.CertificateConstraints { 527 if constraint.Check(cert) { 528 return true 529 } 530 } 531 532 return false 533 } 534 535 /* 536 validateStep ensures that a passed step is valid and matches the 537 necessary format of an step. 538 */ 539 func validateStep(step Step) error { 540 if err := validateSupplyChainItem(step.SupplyChainItem); err != nil { 541 return fmt.Errorf("step %s", err.Error()) 542 } 543 if step.Type != "step" { 544 return fmt.Errorf("invalid Type value for step '%s': should be 'step'", 545 step.SupplyChainItem.Name) 546 } 547 for _, keyID := range step.PubKeys { 548 if err := validateHexString(keyID); err != nil { 549 return err 550 } 551 } 552 return nil 553 } 554 555 /* 556 ISO8601DateSchema defines the format string of a timestamp following the 557 ISO 8601 standard. 558 */ 559 const ISO8601DateSchema = "2006-01-02T15:04:05Z" 560 561 /* 562 Layout represents the definition of a software supply chain. It lists the 563 sequence of steps required in the software supply chain and the functionaries 564 authorized to perform these steps. Functionaries are identified by their 565 public keys. In addition, the layout may list a sequence of inspections that 566 are executed during in-toto supply chain verification. A layout should be 567 contained in a generic Metablock object, which provides functionality for 568 signing and signature verification, and reading from and writing to disk. 569 */ 570 type Layout struct { 571 Type string `json:"_type"` 572 Steps []Step `json:"steps"` 573 Inspect []Inspection `json:"inspect"` 574 Keys map[string]Key `json:"keys"` 575 RootCas []string `json:"rootcas"` 576 IntermediateCas []string `json:"intermediatecas"` 577 Expires string `json:"expires"` 578 Readme string `json:"readme"` 579 } 580 581 // Go does not allow to pass `[]T` (slice with certain type) to a function 582 // that accepts `[]interface{}` (slice with generic type) 583 // We have to manually create the interface slice first, see 584 // https://golang.org/doc/faq#convert_slice_of_interface 585 // TODO: Is there a better way to do polymorphism for steps and inspections? 586 func (l *Layout) stepsAsInterfaceSlice() []interface{} { 587 stepsI := make([]interface{}, len(l.Steps)) 588 for i, v := range l.Steps { 589 stepsI[i] = v 590 } 591 return stepsI 592 } 593 func (l *Layout) inspectAsInterfaceSlice() []interface{} { 594 inspectionsI := make([]interface{}, len(l.Inspect)) 595 for i, v := range l.Inspect { 596 inspectionsI[i] = v 597 } 598 return inspectionsI 599 } 600 601 /* 602 validateLayout is a function used to ensure that a passed item of type Layout 603 matches the necessary format. 604 */ 605 func validateLayout(layout Layout) error { 606 if layout.Type != "layout" { 607 return fmt.Errorf("invalid Type value for layout: should be 'layout'") 608 } 609 610 if _, err := time.Parse(ISO8601DateSchema, layout.Expires); err != nil { 611 return fmt.Errorf("expiry time parsed incorrectly - date either" + 612 " invalid or of incorrect format") 613 } 614 615 for keyID, key := range layout.Keys { 616 if key.KeyID != keyID { 617 return fmt.Errorf("invalid key found") 618 } 619 err := validatePublicKey(key) 620 if err != nil { 621 return err 622 } 623 } 624 625 if err := validateCertificates(layout.RootCas); err != nil { 626 return err 627 } 628 629 if err := validateCertificates(layout.IntermediateCas); err != nil { 630 return err 631 } 632 633 var namesSeen = make(map[string]bool) 634 for _, step := range layout.Steps { 635 if namesSeen[step.Name] { 636 return fmt.Errorf("non unique step or inspection name found") 637 } 638 639 namesSeen[step.Name] = true 640 641 if err := validateStep(step); err != nil { 642 return err 643 } 644 } 645 for _, inspection := range layout.Inspect { 646 if namesSeen[inspection.Name] { 647 return fmt.Errorf("non unique step or inspection name found") 648 } 649 650 namesSeen[inspection.Name] = true 651 } 652 return nil 653 } 654 655 /* 656 Metablock is a generic container for signable in-toto objects such as Layout 657 or Link. It has two fields, one that contains the signable object and one that 658 contains corresponding signatures. Metablock also provides functionality for 659 signing and signature verification, and reading from and writing to disk. 660 */ 661 type Metablock struct { 662 // NOTE: Whenever we want to access an attribute of `Signed` we have to 663 // perform type assertion, e.g. `metablock.Signed.(Layout).Keys` 664 // Maybe there is a better way to store either Layouts or Links in `Signed`? 665 // The notary folks seem to have separate container structs: 666 // https://github.com/theupdateframework/notary/blob/master/tuf/data/root.go#L10-L14 667 // https://github.com/theupdateframework/notary/blob/master/tuf/data/targets.go#L13-L17 668 // I implemented it this way, because there will be several functions that 669 // receive or return a Metablock, where the type of Signed has to be inferred 670 // on runtime, e.g. when iterating over links for a layout, and a link can 671 // turn out to be a layout (sublayout) 672 Signed interface{} `json:"signed"` 673 Signatures []Signature `json:"signatures"` 674 } 675 676 /* 677 checkRequiredJSONFields checks that the passed map (obj) has keys for each of 678 the json tags in the passed struct type (typ), and returns an error otherwise. 679 */ 680 func checkRequiredJSONFields(obj map[string]interface{}, 681 typ reflect.Type) error { 682 683 // Create list of json tags, e.g. `json:"_type"` 684 attributeCount := typ.NumField() 685 allFields := make([]string, 0) 686 for i := 0; i < attributeCount; i++ { 687 allFields = append(allFields, typ.Field(i).Tag.Get("json")) 688 } 689 690 // Assert that there's a key in the passed map for each tag 691 for _, field := range allFields { 692 if _, ok := obj[field]; !ok { 693 return fmt.Errorf("required field %s missing", field) 694 } 695 } 696 return nil 697 } 698 699 /* 700 Load parses JSON formatted metadata at the passed path into the Metablock 701 object on which it was called. It returns an error if it cannot parse 702 a valid JSON formatted Metablock that contains a Link or Layout. 703 */ 704 func (mb *Metablock) Load(path string) (err error) { 705 // Open file and close before returning 706 jsonFile, err := os.Open(path) 707 if err != nil { 708 return err 709 } 710 defer func() { 711 if closeErr := jsonFile.Close(); closeErr != nil { 712 err = closeErr 713 } 714 }() 715 716 // Read entire file 717 jsonBytes, err := ioutil.ReadAll(jsonFile) 718 if err != nil { 719 return err 720 } 721 722 // Unmarshal JSON into a map of raw messages (signed and signatures) 723 // We can't fully unmarshal immediately, because we need to inspect the 724 // type (link or layout) to decide which data structure to use 725 var rawMb map[string]*json.RawMessage 726 if err := json.Unmarshal(jsonBytes, &rawMb); err != nil { 727 return err 728 } 729 730 // Error out on missing `signed` or `signatures` field or if 731 // one of them has a `null` value, which would lead to a nil pointer 732 // dereference in Unmarshal below. 733 if rawMb["signed"] == nil || rawMb["signatures"] == nil { 734 return fmt.Errorf("In-toto metadata requires 'signed' and" + 735 " 'signatures' parts") 736 } 737 738 // Fully unmarshal signatures part 739 if err := json.Unmarshal(*rawMb["signatures"], &mb.Signatures); err != nil { 740 return err 741 } 742 743 // Temporarily copy signed to opaque map to inspect the `_type` of signed 744 // and create link or layout accordingly 745 var signed map[string]interface{} 746 if err := json.Unmarshal(*rawMb["signed"], &signed); err != nil { 747 return err 748 } 749 750 if signed["_type"] == "link" { 751 var link Link 752 if err := checkRequiredJSONFields(signed, reflect.TypeOf(link)); err != nil { 753 return err 754 } 755 756 data, err := rawMb["signed"].MarshalJSON() 757 if err != nil { 758 return err 759 } 760 decoder := json.NewDecoder(strings.NewReader(string(data))) 761 decoder.DisallowUnknownFields() 762 if err := decoder.Decode(&link); err != nil { 763 return err 764 } 765 mb.Signed = link 766 767 } else if signed["_type"] == "layout" { 768 var layout Layout 769 if err := checkRequiredJSONFields(signed, reflect.TypeOf(layout)); err != nil { 770 return err 771 } 772 773 data, err := rawMb["signed"].MarshalJSON() 774 if err != nil { 775 return err 776 } 777 decoder := json.NewDecoder(strings.NewReader(string(data))) 778 decoder.DisallowUnknownFields() 779 if err := decoder.Decode(&layout); err != nil { 780 return err 781 } 782 783 mb.Signed = layout 784 785 } else { 786 return fmt.Errorf("The '_type' field of the 'signed' part of in-toto" + 787 " metadata must be one of 'link' or 'layout'") 788 } 789 790 return nil 791 } 792 793 /* 794 Dump JSON serializes and writes the Metablock on which it was called to the 795 passed path. It returns an error if JSON serialization or writing fails. 796 */ 797 func (mb *Metablock) Dump(path string) error { 798 // JSON encode Metablock formatted with newlines and indentation 799 // TODO: parametrize format 800 jsonBytes, err := json.MarshalIndent(mb, "", " ") 801 if err != nil { 802 return err 803 } 804 805 // Write JSON bytes to the passed path with permissions (-rw-r--r--) 806 err = ioutil.WriteFile(path, jsonBytes, 0644) 807 if err != nil { 808 return err 809 } 810 811 return nil 812 } 813 814 /* 815 GetSignableRepresentation returns the canonical JSON representation of the 816 Signed field of the Metablock on which it was called. If canonicalization 817 fails the first return value is nil and the second return value is the error. 818 */ 819 func (mb *Metablock) GetSignableRepresentation() ([]byte, error) { 820 return EncodeCanonical(mb.Signed) 821 } 822 823 /* 824 VerifySignature verifies the first signature, corresponding to the passed Key, 825 that it finds in the Signatures field of the Metablock on which it was called. 826 It returns an error if Signatures does not contain a Signature corresponding to 827 the passed Key, the object in Signed cannot be canonicalized, or the Signature 828 is invalid. 829 */ 830 func (mb *Metablock) VerifySignature(key Key) error { 831 sig, err := mb.GetSignatureForKeyID(key.KeyID) 832 if err != nil { 833 return err 834 } 835 836 dataCanonical, err := mb.GetSignableRepresentation() 837 if err != nil { 838 return err 839 } 840 841 if err := VerifySignature(key, sig, dataCanonical); err != nil { 842 return err 843 } 844 return nil 845 } 846 847 // GetSignatureForKeyID returns the signature that was created by the provided keyID, if it exists. 848 func (mb *Metablock) GetSignatureForKeyID(keyID string) (Signature, error) { 849 for _, s := range mb.Signatures { 850 if s.KeyID == keyID { 851 return s, nil 852 } 853 } 854 855 return Signature{}, fmt.Errorf("No signature found for key '%s'", keyID) 856 } 857 858 /* 859 validateMetablock ensures that a passed Metablock object is valid. It indirectly 860 validates the Link or Layout that the Metablock object contains. 861 */ 862 func validateMetablock(mb Metablock) error { 863 switch mbSignedType := mb.Signed.(type) { 864 case Layout: 865 if err := validateLayout(mb.Signed.(Layout)); err != nil { 866 return err 867 } 868 case Link: 869 if err := validateLink(mb.Signed.(Link)); err != nil { 870 return err 871 } 872 default: 873 return fmt.Errorf("unknown type '%s', should be 'layout' or 'link'", 874 mbSignedType) 875 } 876 877 if err := validateSliceOfSignatures(mb.Signatures); err != nil { 878 return err 879 } 880 881 return nil 882 } 883 884 /* 885 Sign creates a signature over the signed portion of the metablock using the Key 886 object provided. It then appends the resulting signature to the signatures 887 field as provided. It returns an error if the Signed object cannot be 888 canonicalized, or if the key is invalid or not supported. 889 */ 890 func (mb *Metablock) Sign(key Key) error { 891 892 dataCanonical, err := mb.GetSignableRepresentation() 893 if err != nil { 894 return err 895 } 896 897 newSignature, err := GenerateSignature(dataCanonical, key) 898 if err != nil { 899 return err 900 } 901 902 mb.Signatures = append(mb.Signatures, newSignature) 903 return nil 904 }