1package tuf 2 3import ( 4 "fmt" 5 6 "github.com/docker/go/canonical/json" 7 "github.com/theupdateframework/notary" 8 9 "github.com/theupdateframework/notary/trustpinning" 10 "github.com/theupdateframework/notary/tuf/data" 11 "github.com/theupdateframework/notary/tuf/signed" 12 "github.com/theupdateframework/notary/tuf/utils" 13) 14 15// ErrBuildDone is returned when any functions are called on RepoBuilder, and it 16// is already finished building 17var ErrBuildDone = fmt.Errorf( 18 "the builder has finished building and cannot accept any more input or produce any more output") 19 20// ErrInvalidBuilderInput is returned when RepoBuilder.Load is called 21// with the wrong type of metadata for the state that it's in 22type ErrInvalidBuilderInput struct{ msg string } 23 24func (e ErrInvalidBuilderInput) Error() string { 25 return e.msg 26} 27 28// ConsistentInfo is the consistent name and size of a role, or just the name 29// of the role and a -1 if no file metadata for the role is known 30type ConsistentInfo struct { 31 RoleName data.RoleName 32 fileMeta data.FileMeta 33} 34 35// ChecksumKnown determines whether or not we know enough to provide a size and 36// consistent name 37func (c ConsistentInfo) ChecksumKnown() bool { 38 // empty hash, no size : this is the zero value 39 return len(c.fileMeta.Hashes) > 0 || c.fileMeta.Length != 0 40} 41 42// ConsistentName returns the consistent name (rolename.sha256) for the role 43// given this consistent information 44func (c ConsistentInfo) ConsistentName() string { 45 return utils.ConsistentName(c.RoleName.String(), c.fileMeta.Hashes[notary.SHA256]) 46} 47 48// Length returns the expected length of the role as per this consistent 49// information - if no checksum information is known, the size is -1. 50func (c ConsistentInfo) Length() int64 { 51 if c.ChecksumKnown() { 52 return c.fileMeta.Length 53 } 54 return -1 55} 56 57// RepoBuilder is an interface for an object which builds a tuf.Repo 58type RepoBuilder interface { 59 Load(roleName data.RoleName, content []byte, minVersion int, allowExpired bool) error 60 LoadRootForUpdate(content []byte, minVersion int, isFinal bool) error 61 GenerateSnapshot(prev *data.SignedSnapshot) ([]byte, int, error) 62 GenerateTimestamp(prev *data.SignedTimestamp) ([]byte, int, error) 63 Finish() (*Repo, *Repo, error) 64 BootstrapNewBuilder() RepoBuilder 65 BootstrapNewBuilderWithNewTrustpin(trustpin trustpinning.TrustPinConfig) RepoBuilder 66 67 // informative functions 68 IsLoaded(roleName data.RoleName) bool 69 GetLoadedVersion(roleName data.RoleName) int 70 GetConsistentInfo(roleName data.RoleName) ConsistentInfo 71} 72 73// finishedBuilder refuses any more input or output 74type finishedBuilder struct{} 75 76func (f finishedBuilder) Load(roleName data.RoleName, content []byte, minVersion int, allowExpired bool) error { 77 return ErrBuildDone 78} 79func (f finishedBuilder) LoadRootForUpdate(content []byte, minVersion int, isFinal bool) error { 80 return ErrBuildDone 81} 82func (f finishedBuilder) GenerateSnapshot(prev *data.SignedSnapshot) ([]byte, int, error) { 83 return nil, 0, ErrBuildDone 84} 85func (f finishedBuilder) GenerateTimestamp(prev *data.SignedTimestamp) ([]byte, int, error) { 86 return nil, 0, ErrBuildDone 87} 88func (f finishedBuilder) Finish() (*Repo, *Repo, error) { return nil, nil, ErrBuildDone } 89func (f finishedBuilder) BootstrapNewBuilder() RepoBuilder { return f } 90func (f finishedBuilder) BootstrapNewBuilderWithNewTrustpin(trustpin trustpinning.TrustPinConfig) RepoBuilder { 91 return f 92} 93func (f finishedBuilder) IsLoaded(roleName data.RoleName) bool { return false } 94func (f finishedBuilder) GetLoadedVersion(roleName data.RoleName) int { return 0 } 95func (f finishedBuilder) GetConsistentInfo(roleName data.RoleName) ConsistentInfo { 96 return ConsistentInfo{RoleName: roleName} 97} 98 99// NewRepoBuilder is the only way to get a pre-built RepoBuilder 100func NewRepoBuilder(gun data.GUN, cs signed.CryptoService, trustpin trustpinning.TrustPinConfig) RepoBuilder { 101 return NewBuilderFromRepo(gun, NewRepo(cs), trustpin) 102} 103 104// NewBuilderFromRepo allows us to bootstrap a builder given existing repo data. 105// YOU PROBABLY SHOULDN'T BE USING THIS OUTSIDE OF TESTING CODE!!! 106func NewBuilderFromRepo(gun data.GUN, repo *Repo, trustpin trustpinning.TrustPinConfig) RepoBuilder { 107 return &repoBuilderWrapper{ 108 RepoBuilder: &repoBuilder{ 109 repo: repo, 110 invalidRoles: NewRepo(nil), 111 gun: gun, 112 trustpin: trustpin, 113 loadedNotChecksummed: make(map[data.RoleName][]byte), 114 }, 115 } 116} 117 118// repoBuilderWrapper embeds a repoBuilder, but once Finish is called, swaps 119// the embed out with a finishedBuilder 120type repoBuilderWrapper struct { 121 RepoBuilder 122} 123 124func (rbw *repoBuilderWrapper) Finish() (*Repo, *Repo, error) { 125 switch rbw.RepoBuilder.(type) { 126 case finishedBuilder: 127 return rbw.RepoBuilder.Finish() 128 default: 129 old := rbw.RepoBuilder 130 rbw.RepoBuilder = finishedBuilder{} 131 return old.Finish() 132 } 133} 134 135// repoBuilder actually builds a tuf.Repo 136type repoBuilder struct { 137 repo *Repo 138 invalidRoles *Repo 139 140 // needed for root trust pininng verification 141 gun data.GUN 142 trustpin trustpinning.TrustPinConfig 143 144 // in case we load root and/or targets before snapshot and timestamp ( 145 // or snapshot and not timestamp), so we know what to verify when the 146 // data with checksums come in 147 loadedNotChecksummed map[data.RoleName][]byte 148 149 // bootstrapped values to validate a new root 150 prevRoot *data.SignedRoot 151 bootstrappedRootChecksum *data.FileMeta 152 153 // for bootstrapping the next builder 154 nextRootChecksum *data.FileMeta 155} 156 157func (rb *repoBuilder) Finish() (*Repo, *Repo, error) { 158 return rb.repo, rb.invalidRoles, nil 159} 160 161func (rb *repoBuilder) BootstrapNewBuilder() RepoBuilder { 162 return &repoBuilderWrapper{RepoBuilder: &repoBuilder{ 163 repo: NewRepo(rb.repo.cryptoService), 164 invalidRoles: NewRepo(nil), 165 gun: rb.gun, 166 loadedNotChecksummed: make(map[data.RoleName][]byte), 167 trustpin: rb.trustpin, 168 169 prevRoot: rb.repo.Root, 170 bootstrappedRootChecksum: rb.nextRootChecksum, 171 }} 172} 173 174func (rb *repoBuilder) BootstrapNewBuilderWithNewTrustpin(trustpin trustpinning.TrustPinConfig) RepoBuilder { 175 return &repoBuilderWrapper{RepoBuilder: &repoBuilder{ 176 repo: NewRepo(rb.repo.cryptoService), 177 gun: rb.gun, 178 loadedNotChecksummed: make(map[data.RoleName][]byte), 179 trustpin: trustpin, 180 181 prevRoot: rb.repo.Root, 182 bootstrappedRootChecksum: rb.nextRootChecksum, 183 }} 184} 185 186// IsLoaded returns whether a particular role has already been loaded 187func (rb *repoBuilder) IsLoaded(roleName data.RoleName) bool { 188 switch roleName { 189 case data.CanonicalRootRole: 190 return rb.repo.Root != nil 191 case data.CanonicalSnapshotRole: 192 return rb.repo.Snapshot != nil 193 case data.CanonicalTimestampRole: 194 return rb.repo.Timestamp != nil 195 default: 196 return rb.repo.Targets[roleName] != nil 197 } 198} 199 200// GetLoadedVersion returns the metadata version, if it is loaded, or 1 (the 201// minimum valid version number) otherwise 202func (rb *repoBuilder) GetLoadedVersion(roleName data.RoleName) int { 203 switch { 204 case roleName == data.CanonicalRootRole && rb.repo.Root != nil: 205 return rb.repo.Root.Signed.Version 206 case roleName == data.CanonicalSnapshotRole && rb.repo.Snapshot != nil: 207 return rb.repo.Snapshot.Signed.Version 208 case roleName == data.CanonicalTimestampRole && rb.repo.Timestamp != nil: 209 return rb.repo.Timestamp.Signed.Version 210 default: 211 if tgts, ok := rb.repo.Targets[roleName]; ok { 212 return tgts.Signed.Version 213 } 214 } 215 216 return 1 217} 218 219// GetConsistentInfo returns the consistent name and size of a role, if it is known, 220// otherwise just the rolename and a -1 for size (both of which are inside a 221// ConsistentInfo object) 222func (rb *repoBuilder) GetConsistentInfo(roleName data.RoleName) ConsistentInfo { 223 info := ConsistentInfo{RoleName: roleName} // starts out with unknown filemeta 224 switch roleName { 225 case data.CanonicalTimestampRole: 226 // we do not want to get a consistent timestamp, but we do want to 227 // limit its size 228 info.fileMeta.Length = notary.MaxTimestampSize 229 case data.CanonicalSnapshotRole: 230 if rb.repo.Timestamp != nil { 231 info.fileMeta = rb.repo.Timestamp.Signed.Meta[roleName.String()] 232 } 233 case data.CanonicalRootRole: 234 switch { 235 case rb.bootstrappedRootChecksum != nil: 236 info.fileMeta = *rb.bootstrappedRootChecksum 237 case rb.repo.Snapshot != nil: 238 info.fileMeta = rb.repo.Snapshot.Signed.Meta[roleName.String()] 239 } 240 default: 241 if rb.repo.Snapshot != nil { 242 info.fileMeta = rb.repo.Snapshot.Signed.Meta[roleName.String()] 243 } 244 } 245 return info 246} 247 248func (rb *repoBuilder) Load(roleName data.RoleName, content []byte, minVersion int, allowExpired bool) error { 249 return rb.loadOptions(roleName, content, minVersion, allowExpired, false, false) 250} 251 252// LoadRootForUpdate adds additional flags for updating the root.json file 253func (rb *repoBuilder) LoadRootForUpdate(content []byte, minVersion int, isFinal bool) error { 254 if err := rb.loadOptions(data.CanonicalRootRole, content, minVersion, !isFinal, !isFinal, true); err != nil { 255 return err 256 } 257 if !isFinal { 258 rb.prevRoot = rb.repo.Root 259 } 260 return nil 261} 262 263// loadOptions adds additional flags that should only be used for updating the root.json 264func (rb *repoBuilder) loadOptions(roleName data.RoleName, content []byte, minVersion int, allowExpired, skipChecksum, allowLoaded bool) error { 265 if !data.ValidRole(roleName) { 266 return ErrInvalidBuilderInput{msg: fmt.Sprintf("%s is an invalid role", roleName)} 267 } 268 269 if !allowLoaded && rb.IsLoaded(roleName) { 270 return ErrInvalidBuilderInput{msg: fmt.Sprintf("%s has already been loaded", roleName)} 271 } 272 273 var err error 274 switch roleName { 275 case data.CanonicalRootRole: 276 break 277 case data.CanonicalTimestampRole, data.CanonicalSnapshotRole, data.CanonicalTargetsRole: 278 err = rb.checkPrereqsLoaded([]data.RoleName{data.CanonicalRootRole}) 279 default: // delegations 280 err = rb.checkPrereqsLoaded([]data.RoleName{data.CanonicalRootRole, data.CanonicalTargetsRole}) 281 } 282 if err != nil { 283 return err 284 } 285 286 switch roleName { 287 case data.CanonicalRootRole: 288 return rb.loadRoot(content, minVersion, allowExpired, skipChecksum) 289 case data.CanonicalSnapshotRole: 290 return rb.loadSnapshot(content, minVersion, allowExpired) 291 case data.CanonicalTimestampRole: 292 return rb.loadTimestamp(content, minVersion, allowExpired) 293 case data.CanonicalTargetsRole: 294 return rb.loadTargets(content, minVersion, allowExpired) 295 default: 296 return rb.loadDelegation(roleName, content, minVersion, allowExpired) 297 } 298} 299 300func (rb *repoBuilder) checkPrereqsLoaded(prereqRoles []data.RoleName) error { 301 for _, req := range prereqRoles { 302 if !rb.IsLoaded(req) { 303 return ErrInvalidBuilderInput{msg: fmt.Sprintf("%s must be loaded first", req)} 304 } 305 } 306 return nil 307} 308 309// GenerateSnapshot generates a new snapshot given a previous (optional) snapshot 310// We can't just load the previous snapshot, because it may have been signed by a different 311// snapshot key (maybe from a previous root version). Note that we need the root role and 312// targets role to be loaded, because we need to generate metadata for both (and we need 313// the root to be loaded so we can get the snapshot role to sign with) 314func (rb *repoBuilder) GenerateSnapshot(prev *data.SignedSnapshot) ([]byte, int, error) { 315 switch { 316 case rb.repo.cryptoService == nil: 317 return nil, 0, ErrInvalidBuilderInput{msg: "cannot generate snapshot without a cryptoservice"} 318 case rb.IsLoaded(data.CanonicalSnapshotRole): 319 return nil, 0, ErrInvalidBuilderInput{msg: "snapshot has already been loaded"} 320 case rb.IsLoaded(data.CanonicalTimestampRole): 321 return nil, 0, ErrInvalidBuilderInput{msg: "cannot generate snapshot if timestamp has already been loaded"} 322 } 323 324 if err := rb.checkPrereqsLoaded([]data.RoleName{data.CanonicalRootRole}); err != nil { 325 return nil, 0, err 326 } 327 328 // If there is no previous snapshot, we need to generate one, and so the targets must 329 // have already been loaded. Otherwise, so long as the previous snapshot structure is 330 // valid (it has a targets meta), we're good. 331 switch prev { 332 case nil: 333 if err := rb.checkPrereqsLoaded([]data.RoleName{data.CanonicalTargetsRole}); err != nil { 334 return nil, 0, err 335 } 336 337 if err := rb.repo.InitSnapshot(); err != nil { 338 rb.repo.Snapshot = nil 339 return nil, 0, err 340 } 341 default: 342 if err := data.IsValidSnapshotStructure(prev.Signed); err != nil { 343 return nil, 0, err 344 } 345 rb.repo.Snapshot = prev 346 } 347 348 sgnd, err := rb.repo.SignSnapshot(data.DefaultExpires(data.CanonicalSnapshotRole)) 349 if err != nil { 350 rb.repo.Snapshot = nil 351 return nil, 0, err 352 } 353 354 sgndJSON, err := json.Marshal(sgnd) 355 if err != nil { 356 rb.repo.Snapshot = nil 357 return nil, 0, err 358 } 359 360 // loadedNotChecksummed should currently contain the root awaiting checksumming, 361 // since it has to have been loaded. Since the snapshot was generated using 362 // the root and targets data (there may not be any) that that have been loaded, 363 // remove all of them from rb.loadedNotChecksummed 364 for tgtName := range rb.repo.Targets { 365 delete(rb.loadedNotChecksummed, data.RoleName(tgtName)) 366 } 367 delete(rb.loadedNotChecksummed, data.CanonicalRootRole) 368 369 // The timestamp can't have been loaded yet, so we want to cache the snapshot 370 // bytes so we can validate the checksum when a timestamp gets generated or 371 // loaded later. 372 rb.loadedNotChecksummed[data.CanonicalSnapshotRole] = sgndJSON 373 374 return sgndJSON, rb.repo.Snapshot.Signed.Version, nil 375} 376 377// GenerateTimestamp generates a new timestamp given a previous (optional) timestamp 378// We can't just load the previous timestamp, because it may have been signed by a different 379// timestamp key (maybe from a previous root version) 380func (rb *repoBuilder) GenerateTimestamp(prev *data.SignedTimestamp) ([]byte, int, error) { 381 switch { 382 case rb.repo.cryptoService == nil: 383 return nil, 0, ErrInvalidBuilderInput{msg: "cannot generate timestamp without a cryptoservice"} 384 case rb.IsLoaded(data.CanonicalTimestampRole): 385 return nil, 0, ErrInvalidBuilderInput{msg: "timestamp has already been loaded"} 386 } 387 388 // SignTimestamp always serializes the loaded snapshot and signs in the data, so we must always 389 // have the snapshot loaded first 390 if err := rb.checkPrereqsLoaded([]data.RoleName{data.CanonicalRootRole, data.CanonicalSnapshotRole}); err != nil { 391 return nil, 0, err 392 } 393 394 switch prev { 395 case nil: 396 if err := rb.repo.InitTimestamp(); err != nil { 397 rb.repo.Timestamp = nil 398 return nil, 0, err 399 } 400 default: 401 if err := data.IsValidTimestampStructure(prev.Signed); err != nil { 402 return nil, 0, err 403 } 404 rb.repo.Timestamp = prev 405 } 406 407 sgnd, err := rb.repo.SignTimestamp(data.DefaultExpires(data.CanonicalTimestampRole)) 408 if err != nil { 409 rb.repo.Timestamp = nil 410 return nil, 0, err 411 } 412 413 sgndJSON, err := json.Marshal(sgnd) 414 if err != nil { 415 rb.repo.Timestamp = nil 416 return nil, 0, err 417 } 418 419 // The snapshot should have been loaded (and not checksummed, since a timestamp 420 // cannot have been loaded), so it is awaiting checksumming. Since this 421 // timestamp was generated using the snapshot awaiting checksumming, we can 422 // remove it from rb.loadedNotChecksummed. There should be no other items 423 // awaiting checksumming now since loading/generating a snapshot should have 424 // cleared out everything else in `loadNotChecksummed`. 425 delete(rb.loadedNotChecksummed, data.CanonicalSnapshotRole) 426 427 return sgndJSON, rb.repo.Timestamp.Signed.Version, nil 428} 429 430// loadRoot loads a root if one has not been loaded 431func (rb *repoBuilder) loadRoot(content []byte, minVersion int, allowExpired, skipChecksum bool) error { 432 roleName := data.CanonicalRootRole 433 434 signedObj, err := rb.bytesToSigned(content, data.CanonicalRootRole, skipChecksum) 435 if err != nil { 436 return err 437 } 438 // ValidateRoot validates against the previous root's role, as well as validates that the root 439 // itself is self-consistent with its own signatures and thresholds. 440 // This assumes that ValidateRoot calls data.RootFromSigned, which validates 441 // the metadata, rather than just unmarshalling signedObject into a SignedRoot object itself. 442 signedRoot, err := trustpinning.ValidateRoot(rb.prevRoot, signedObj, rb.gun, rb.trustpin) 443 if err != nil { 444 return err 445 } 446 447 if err := signed.VerifyVersion(&(signedRoot.Signed.SignedCommon), minVersion); err != nil { 448 return err 449 } 450 451 if !allowExpired { // check must go at the end because all other validation should pass 452 if err := signed.VerifyExpiry(&(signedRoot.Signed.SignedCommon), roleName); err != nil { 453 return err 454 } 455 } 456 457 rootRole, err := signedRoot.BuildBaseRole(data.CanonicalRootRole) 458 if err != nil { // this should never happen since the root has been validated 459 return err 460 } 461 rb.repo.Root = signedRoot 462 rb.repo.originalRootRole = rootRole 463 return nil 464} 465 466func (rb *repoBuilder) loadTimestamp(content []byte, minVersion int, allowExpired bool) error { 467 roleName := data.CanonicalTimestampRole 468 469 timestampRole, err := rb.repo.Root.BuildBaseRole(roleName) 470 if err != nil { // this should never happen, since it's already been validated 471 return err 472 } 473 474 signedObj, err := rb.bytesToSignedAndValidateSigs(timestampRole, content) 475 if err != nil { 476 return err 477 } 478 479 signedTimestamp, err := data.TimestampFromSigned(signedObj) 480 if err != nil { 481 return err 482 } 483 484 if err := signed.VerifyVersion(&(signedTimestamp.Signed.SignedCommon), minVersion); err != nil { 485 return err 486 } 487 488 if !allowExpired { // check must go at the end because all other validation should pass 489 if err := signed.VerifyExpiry(&(signedTimestamp.Signed.SignedCommon), roleName); err != nil { 490 return err 491 } 492 } 493 494 if err := rb.validateChecksumsFromTimestamp(signedTimestamp); err != nil { 495 return err 496 } 497 498 rb.repo.Timestamp = signedTimestamp 499 return nil 500} 501 502func (rb *repoBuilder) loadSnapshot(content []byte, minVersion int, allowExpired bool) error { 503 roleName := data.CanonicalSnapshotRole 504 505 snapshotRole, err := rb.repo.Root.BuildBaseRole(roleName) 506 if err != nil { // this should never happen, since it's already been validated 507 return err 508 } 509 510 signedObj, err := rb.bytesToSignedAndValidateSigs(snapshotRole, content) 511 if err != nil { 512 return err 513 } 514 515 signedSnapshot, err := data.SnapshotFromSigned(signedObj) 516 if err != nil { 517 return err 518 } 519 520 if err := signed.VerifyVersion(&(signedSnapshot.Signed.SignedCommon), minVersion); err != nil { 521 return err 522 } 523 524 if !allowExpired { // check must go at the end because all other validation should pass 525 if err := signed.VerifyExpiry(&(signedSnapshot.Signed.SignedCommon), roleName); err != nil { 526 return err 527 } 528 } 529 530 // at this point, the only thing left to validate is existing checksums - we can use 531 // this snapshot to bootstrap the next builder if needed - and we don't need to do 532 // the 2-value assignment since we've already validated the signedSnapshot, which MUST 533 // have root metadata 534 rootMeta := signedSnapshot.Signed.Meta[data.CanonicalRootRole.String()] 535 rb.nextRootChecksum = &rootMeta 536 537 if err := rb.validateChecksumsFromSnapshot(signedSnapshot); err != nil { 538 return err 539 } 540 541 rb.repo.Snapshot = signedSnapshot 542 return nil 543} 544 545func (rb *repoBuilder) loadTargets(content []byte, minVersion int, allowExpired bool) error { 546 roleName := data.CanonicalTargetsRole 547 548 targetsRole, err := rb.repo.Root.BuildBaseRole(roleName) 549 if err != nil { // this should never happen, since it's already been validated 550 return err 551 } 552 553 signedObj, err := rb.bytesToSignedAndValidateSigs(targetsRole, content) 554 if err != nil { 555 return err 556 } 557 558 signedTargets, err := data.TargetsFromSigned(signedObj, roleName) 559 if err != nil { 560 return err 561 } 562 563 if err := signed.VerifyVersion(&(signedTargets.Signed.SignedCommon), minVersion); err != nil { 564 return err 565 } 566 567 if !allowExpired { // check must go at the end because all other validation should pass 568 if err := signed.VerifyExpiry(&(signedTargets.Signed.SignedCommon), roleName); err != nil { 569 return err 570 } 571 } 572 573 signedTargets.Signatures = signedObj.Signatures 574 rb.repo.Targets[roleName] = signedTargets 575 return nil 576} 577 578func (rb *repoBuilder) loadDelegation(roleName data.RoleName, content []byte, minVersion int, allowExpired bool) error { 579 delegationRole, err := rb.repo.GetDelegationRole(roleName) 580 if err != nil { 581 return err 582 } 583 584 // bytesToSigned checks checksum 585 signedObj, err := rb.bytesToSigned(content, roleName, false) 586 if err != nil { 587 return err 588 } 589 590 signedTargets, err := data.TargetsFromSigned(signedObj, roleName) 591 if err != nil { 592 return err 593 } 594 595 if err := signed.VerifyVersion(&(signedTargets.Signed.SignedCommon), minVersion); err != nil { 596 // don't capture in invalidRoles because the role we received is a rollback 597 return err 598 } 599 600 // verify signature 601 if err := signed.VerifySignatures(signedObj, delegationRole.BaseRole); err != nil { 602 rb.invalidRoles.Targets[roleName] = signedTargets 603 return err 604 } 605 606 if !allowExpired { // check must go at the end because all other validation should pass 607 if err := signed.VerifyExpiry(&(signedTargets.Signed.SignedCommon), roleName); err != nil { 608 rb.invalidRoles.Targets[roleName] = signedTargets 609 return err 610 } 611 } 612 613 signedTargets.Signatures = signedObj.Signatures 614 rb.repo.Targets[roleName] = signedTargets 615 return nil 616} 617 618func (rb *repoBuilder) validateChecksumsFromTimestamp(ts *data.SignedTimestamp) error { 619 sn, ok := rb.loadedNotChecksummed[data.CanonicalSnapshotRole] 620 if ok { 621 // by this point, the SignedTimestamp has been validated so it must have a snapshot hash 622 snMeta := ts.Signed.Meta[data.CanonicalSnapshotRole.String()].Hashes 623 if err := data.CheckHashes(sn, data.CanonicalSnapshotRole.String(), snMeta); err != nil { 624 return err 625 } 626 delete(rb.loadedNotChecksummed, data.CanonicalSnapshotRole) 627 } 628 return nil 629} 630 631func (rb *repoBuilder) validateChecksumsFromSnapshot(sn *data.SignedSnapshot) error { 632 var goodRoles []data.RoleName 633 for roleName, loadedBytes := range rb.loadedNotChecksummed { 634 switch roleName { 635 case data.CanonicalSnapshotRole, data.CanonicalTimestampRole: 636 break 637 default: 638 if err := data.CheckHashes(loadedBytes, roleName.String(), sn.Signed.Meta[roleName.String()].Hashes); err != nil { 639 return err 640 } 641 goodRoles = append(goodRoles, roleName) 642 } 643 } 644 for _, roleName := range goodRoles { 645 delete(rb.loadedNotChecksummed, roleName) 646 } 647 return nil 648} 649 650func (rb *repoBuilder) validateChecksumFor(content []byte, roleName data.RoleName) error { 651 // validate the bootstrap checksum for root, if provided 652 if roleName == data.CanonicalRootRole && rb.bootstrappedRootChecksum != nil { 653 if err := data.CheckHashes(content, roleName.String(), rb.bootstrappedRootChecksum.Hashes); err != nil { 654 return err 655 } 656 } 657 658 // but we also want to cache the root content, so that when the snapshot is 659 // loaded it is validated (to make sure everything in the repo is self-consistent) 660 checksums := rb.getChecksumsFor(roleName) 661 if checksums != nil { // as opposed to empty, in which case hash check should fail 662 if err := data.CheckHashes(content, roleName.String(), *checksums); err != nil { 663 return err 664 } 665 } else if roleName != data.CanonicalTimestampRole { 666 // timestamp is the only role which does not need to be checksummed, but 667 // for everything else, cache the contents in the list of roles that have 668 // not been checksummed by the snapshot/timestamp yet 669 rb.loadedNotChecksummed[roleName] = content 670 } 671 672 return nil 673} 674 675// Checksums the given bytes, and if they validate, convert to a data.Signed object. 676// If a checksums are nil (as opposed to empty), adds the bytes to the list of roles that 677// haven't been checksummed (unless it's a timestamp, which has no checksum reference). 678func (rb *repoBuilder) bytesToSigned(content []byte, roleName data.RoleName, skipChecksum bool) (*data.Signed, error) { 679 if !skipChecksum { 680 if err := rb.validateChecksumFor(content, roleName); err != nil { 681 return nil, err 682 } 683 } 684 685 // unmarshal to signed 686 signedObj := &data.Signed{} 687 if err := json.Unmarshal(content, signedObj); err != nil { 688 return nil, err 689 } 690 691 return signedObj, nil 692} 693 694func (rb *repoBuilder) bytesToSignedAndValidateSigs(role data.BaseRole, content []byte) (*data.Signed, error) { 695 696 signedObj, err := rb.bytesToSigned(content, role.Name, false) 697 if err != nil { 698 return nil, err 699 } 700 701 // verify signature 702 if err := signed.VerifySignatures(signedObj, role); err != nil { 703 return nil, err 704 } 705 706 return signedObj, nil 707} 708 709// If the checksum reference (the loaded timestamp for the snapshot role, and 710// the loaded snapshot for every other role except timestamp and snapshot) is nil, 711// then return nil for the checksums, meaning that the checksum is not yet 712// available. If the checksum reference *is* loaded, then always returns the 713// Hashes object for the given role - if it doesn't exist, returns an empty Hash 714// object (against which any checksum validation would fail). 715func (rb *repoBuilder) getChecksumsFor(role data.RoleName) *data.Hashes { 716 var hashes data.Hashes 717 switch role { 718 case data.CanonicalTimestampRole: 719 return nil 720 case data.CanonicalSnapshotRole: 721 if rb.repo.Timestamp == nil { 722 return nil 723 } 724 hashes = rb.repo.Timestamp.Signed.Meta[data.CanonicalSnapshotRole.String()].Hashes 725 default: 726 if rb.repo.Snapshot == nil { 727 return nil 728 } 729 hashes = rb.repo.Snapshot.Signed.Meta[role.String()].Hashes 730 } 731 return &hashes 732} 733