// generateSnapshot generates a new snapshot from the previous one in the store - this assumes all // the other roles except timestamp have already been set on the repo, and will set the generated // snapshot on the repo as well func generateSnapshot(gun string, builder tuf.RepoBuilder, store storage.MetaStore) (*storage.MetaUpdate, error) { var prev *data.SignedSnapshot _, currentJSON, err := store.GetCurrent(gun, data.CanonicalSnapshotRole) if err == nil { prev = new(data.SignedSnapshot) if err = json.Unmarshal(currentJSON, prev); err != nil { logrus.Error("Failed to unmarshal existing snapshot for GUN ", gun) return nil, err } } if _, ok := err.(storage.ErrNotFound); !ok && err != nil { return nil, err } meta, ver, err := builder.GenerateSnapshot(prev) switch err.(type) { case nil: return &storage.MetaUpdate{ Role: data.CanonicalSnapshotRole, Version: ver, Data: meta, }, nil case signed.ErrInsufficientSignatures, signed.ErrNoKeys, signed.ErrRoleThreshold: // If we cannot sign the snapshot, then we don't have keys for the snapshot, // and the client should have submitted a snapshot return nil, validation.ErrBadHierarchy{ Missing: data.CanonicalSnapshotRole, Msg: "no snapshot was included in update and server does not hold current snapshot key for repository"} default: return nil, validation.ErrValidation{Msg: err.Error()} } }
func checkOnlySnapshotConsistentAfterTimestamp(t *testing.T, repo *tuf.Repo, meta map[string][]byte, extraMeta []string, builder tuf.RepoBuilder) { // once timestamp is loaded, we can get the consistent info for snapshot but nothing else require.NoError(t, builder.Load(data.CanonicalTimestampRole, meta[data.CanonicalTimestampRole], 1, false)) for _, checkName := range append(data.BaseRoles, extraMeta...) { ci := builder.GetConsistentInfo(checkName) switch checkName { case data.CanonicalSnapshotRole: cName := utils.ConsistentName(data.CanonicalSnapshotRole, repo.Timestamp.Signed.Meta[data.CanonicalSnapshotRole].Hashes[notary.SHA256]) require.Equal(t, cName, ci.ConsistentName()) require.True(t, ci.ChecksumKnown()) require.True(t, ci.Length() > -1) case data.CanonicalTimestampRole: // timestamp's canonical name is always "timestamp" and its size is always the max // timestamp size require.Equal(t, data.CanonicalTimestampRole, ci.ConsistentName()) require.True(t, ci.ChecksumKnown()) require.Equal(t, notary.MaxTimestampSize, ci.Length()) default: require.Equal(t, checkName, ci.ConsistentName()) require.False(t, ci.ChecksumKnown()) require.Equal(t, int64(-1), ci.Length()) } } }
func loadFromStore(gun, roleName string, builder tuf.RepoBuilder, store storage.MetaStore) error { _, metaJSON, err := store.GetCurrent(gun, roleName) if err != nil { return err } if err := builder.Load(roleName, metaJSON, 1, true); err != nil { return err } return nil }
func loadAndValidateTargets(gun string, builder tuf.RepoBuilder, roles map[string]storage.MetaUpdate, store storage.MetaStore) ([]storage.MetaUpdate, error) { targetsRoles := make(utils.RoleList, 0) for role := range roles { if role == data.CanonicalTargetsRole || data.IsDelegation(role) { targetsRoles = append(targetsRoles, role) } } // N.B. RoleList sorts paths with fewer segments first. // By sorting, we'll always process shallower targets updates before deeper // ones (i.e. we'll load and validate targets before targets/foo). This // helps ensure we only load from storage when necessary in a cleaner way. sort.Sort(targetsRoles) updatesToApply := make([]storage.MetaUpdate, 0, len(targetsRoles)) for _, roleName := range targetsRoles { // don't load parent if current role is "targets", // we must load all ancestor roles, starting from `targets` and working down, // for delegations to validate the full parent chain var parentsToLoad []string ancestorRole := roleName for ancestorRole != data.CanonicalTargetsRole { ancestorRole = path.Dir(ancestorRole) if !builder.IsLoaded(ancestorRole) { parentsToLoad = append(parentsToLoad, ancestorRole) } } for i := len(parentsToLoad) - 1; i >= 0; i-- { if err := loadFromStore(gun, parentsToLoad[i], builder, store); err != nil { // if the parent doesn't exist, just keep going - loading the role will eventually fail // due to it being an invalid role if _, ok := err.(storage.ErrNotFound); !ok { return nil, err } } } if err := builder.Load(roleName, roles[roleName].Data, 1, false); err != nil { logrus.Error("ErrBadTargets: ", err.Error()) return nil, validation.ErrBadTargets{Msg: err.Error()} } updatesToApply = append(updatesToApply, roles[roleName]) } return updatesToApply, nil }
func checkTimestampSnapshotRequired(t *testing.T, meta map[string][]byte, extraMeta []string, builder tuf.RepoBuilder) { // if neither snapshot nor timestamp are loaded, no matter how much other data is loaded, consistent info // is empty except for timestamp: timestamps have no checksums, and the length is always -1 for _, roleToLoad := range []string{data.CanonicalRootRole, data.CanonicalTargetsRole} { require.NoError(t, builder.Load(roleToLoad, meta[roleToLoad], 1, false)) for _, checkName := range append(data.BaseRoles, extraMeta...) { ci := builder.GetConsistentInfo(checkName) require.Equal(t, checkName, ci.ConsistentName()) switch checkName { case data.CanonicalTimestampRole: // timestamp's size is always the max timestamp size require.True(t, ci.ChecksumKnown()) require.Equal(t, notary.MaxTimestampSize, ci.Length()) default: require.False(t, ci.ChecksumKnown()) require.Equal(t, int64(-1), ci.Length()) } } } }
// generateTimestamp generates a new timestamp from the previous one in the store - this assumes all // the other roles have already been set on the repo, and will set the generated timestamp on the repo as well func generateTimestamp(gun string, builder tuf.RepoBuilder, store storage.MetaStore) (*storage.MetaUpdate, error) { var prev *data.SignedTimestamp _, currentJSON, err := store.GetCurrent(gun, data.CanonicalTimestampRole) switch err.(type) { case nil: prev = new(data.SignedTimestamp) if err := json.Unmarshal(currentJSON, prev); err != nil { logrus.Error("Failed to unmarshal existing timestamp for GUN ", gun) return nil, err } case storage.ErrNotFound: break // this is the first timestamp ever for the repo default: return nil, err } meta, ver, err := builder.GenerateTimestamp(prev) switch err.(type) { case nil: return &storage.MetaUpdate{ Role: data.CanonicalTimestampRole, Version: ver, Data: meta, }, nil case signed.ErrInsufficientSignatures, signed.ErrNoKeys: // If we cannot sign the timestamp, then we don't have keys for the timestamp, // and the client screwed up their root return nil, validation.ErrBadRoot{ Msg: fmt.Sprintf("no timestamp keys exist on the server"), } default: return nil, validation.ErrValidation{Msg: err.Error()} } }
func checkOtherRolesConsistentAfterSnapshot(t *testing.T, repo *tuf.Repo, meta map[string][]byte, builder tuf.RepoBuilder) { // once the snapshot is loaded, we can get real consistent info for all loaded roles require.NoError(t, builder.Load(data.CanonicalSnapshotRole, meta[data.CanonicalSnapshotRole], 1, false)) for _, checkName := range data.BaseRoles { ci := builder.GetConsistentInfo(checkName) require.True(t, ci.ChecksumKnown(), "%s's checksum is not known", checkName) switch checkName { case data.CanonicalTimestampRole: // timestamp's canonical name is always "timestamp" and its size is always -1 require.Equal(t, data.CanonicalTimestampRole, ci.ConsistentName()) require.Equal(t, notary.MaxTimestampSize, ci.Length()) default: fileInfo := repo.Snapshot.Signed.Meta if checkName == data.CanonicalSnapshotRole { fileInfo = repo.Timestamp.Signed.Meta } cName := utils.ConsistentName(checkName, fileInfo[checkName].Hashes[notary.SHA256]) require.Equal(t, cName, ci.ConsistentName()) require.True(t, ci.Length() > -1) } } }