Example #1
0
func changeTargetMeta(repo *tuf.Repo, c changelist.Change) error {
	var err error
	switch c.Action() {
	case changelist.ActionCreate:
		logrus.Debug("changelist add: ", c.Path())
		meta := &data.FileMeta{}
		err = json.Unmarshal(c.Content(), meta)
		if err != nil {
			return err
		}
		files := data.Files{c.Path(): *meta}

		err = doWithRoleFallback(c.Scope(), func(role string) error {
			_, e := repo.AddTargets(role, files)
			return e
		})
		if err != nil {
			logrus.Errorf("couldn't add target to %s: %s", c.Scope(), err.Error())
		}

	case changelist.ActionDelete:
		logrus.Debug("changelist remove: ", c.Path())

		err = doWithRoleFallback(c.Scope(), func(role string) error {
			return repo.RemoveTargets(role, c.Path())
		})
		if err != nil {
			logrus.Errorf("couldn't remove target from %s: %s", c.Scope(), err.Error())
		}

	default:
		logrus.Debug("action not yet supported: ", c.Action())
	}
	return err
}
Example #2
0
func changeTargetMeta(repo *tuf.Repo, c changelist.Change) error {
	var err error
	switch c.Action() {
	case changelist.ActionCreate:
		logrus.Debug("changelist add: ", c.Path())
		meta := &data.FileMeta{}
		err = json.Unmarshal(c.Content(), meta)
		if err != nil {
			return err
		}
		files := data.Files{c.Path(): *meta}

		// Attempt to add the target to this role
		if _, err = repo.AddTargets(c.Scope(), files); err != nil {
			logrus.Errorf("couldn't add target to %s: %s", c.Scope(), err.Error())
		}

	case changelist.ActionDelete:
		logrus.Debug("changelist remove: ", c.Path())

		// Attempt to remove the target from this role
		if err = repo.RemoveTargets(c.Scope(), c.Path()); err != nil {
			logrus.Errorf("couldn't remove target from %s: %s", c.Scope(), err.Error())
		}

	default:
		logrus.Debug("action not yet supported: ", c.Action())
	}
	return err
}
Example #3
0
func validateSnapshot(role string, oldSnap *data.SignedSnapshot, snapUpdate storage.MetaUpdate, roles map[string]storage.MetaUpdate, repo *tuf.Repo) error {
	s := &data.Signed{}
	err := json.Unmarshal(snapUpdate.Data, s)
	if err != nil {
		return errors.New("could not parse snapshot")
	}
	// version specifically gets validated when writing to store to
	// better handle race conditions there.
	snapshotRole, err := repo.GetBaseRole(role)
	if err != nil {
		return err
	}
	if err := signed.Verify(s, snapshotRole, 0); err != nil {
		return err
	}

	snap, err := data.SnapshotFromSigned(s)
	if err != nil {
		return errors.New("could not parse snapshot")
	}
	if !data.ValidTUFType(snap.Signed.Type, data.CanonicalSnapshotRole) {
		return errors.New("snapshot has wrong type")
	}
	err = checkSnapshotEntries(role, oldSnap, snap, roles)
	if err != nil {
		return err
	}
	return nil
}
Example #4
0
func witnessTargets(repo *tuf.Repo, invalid *tuf.Repo, role string) error {
	if r, ok := repo.Targets[role]; ok {
		// role is already valid, mark for re-signing/updating
		r.Dirty = true
		return nil
	}

	if roleObj, err := repo.GetDelegationRole(role); err == nil && invalid != nil {
		// A role with a threshold > len(keys) is technically invalid, but we let it build in the builder because
		// we want to be able to download the role (which may still have targets on it), add more keys, and then
		// witness the role, thus bringing it back to valid.  However, if no keys have been added before witnessing,
		// then it is still an invalid role, and can't be witnessed because nothing can bring it back to valid.
		if roleObj.Threshold > len(roleObj.Keys) {
			return data.ErrInvalidRole{
				Role:   role,
				Reason: "role does not specify enough valid signing keys to meet its required threshold",
			}
		}
		if r, ok := invalid.Targets[role]; ok {
			// role is recognized but invalid, move to valid data and mark for re-signing
			repo.Targets[role] = r
			r.Dirty = true
			return nil
		}
	}
	// role isn't recognized, even as invalid
	return data.ErrInvalidRole{
		Role:   role,
		Reason: "this role is not known",
	}
}
Example #5
0
func changeTargetsDelegation(repo *tuf.Repo, c changelist.Change) error {
	switch c.Action() {
	case changelist.ActionCreate:
		td := changelist.TufDelegation{}
		err := json.Unmarshal(c.Content(), &td)
		if err != nil {
			return err
		}
		r, err := repo.GetDelegation(c.Scope())
		if _, ok := err.(data.ErrNoSuchRole); err != nil && !ok {
			// error that wasn't ErrNoSuchRole
			return err
		}
		if err == nil {
			// role existed
			return data.ErrInvalidRole{
				Role:   c.Scope(),
				Reason: "cannot create a role that already exists",
			}
		}
		// role doesn't exist, create brand new
		r, err = td.ToNewRole(c.Scope())
		if err != nil {
			return err
		}
		return repo.UpdateDelegations(r, td.AddKeys)
	case changelist.ActionUpdate:
		td := changelist.TufDelegation{}
		err := json.Unmarshal(c.Content(), &td)
		if err != nil {
			return err
		}
		r, err := repo.GetDelegation(c.Scope())
		if err != nil {
			return err
		}
		// role exists, merge
		if err := r.AddPaths(td.AddPaths); err != nil {
			return err
		}
		if err := r.AddPathHashPrefixes(td.AddPathHashPrefixes); err != nil {
			return err
		}
		r.RemoveKeys(td.RemoveKeys)
		r.RemovePaths(td.RemovePaths)
		r.RemovePathHashPrefixes(td.RemovePathHashPrefixes)
		return repo.UpdateDelegations(r, td.AddKeys)
	case changelist.ActionDelete:
		r := data.Role{Name: c.Scope()}
		return repo.DeleteDelegation(r)
	default:
		return fmt.Errorf("unsupported action against delegations: %s", c.Action())
	}

}
Example #6
0
func loadTargetsFromStore(gun, role string, repo *tuf.Repo, store storage.MetaStore) error {
	tgtJSON, err := store.GetCurrent(gun, role)
	if err != nil {
		return err
	}
	t := &data.SignedTargets{}
	err = json.Unmarshal(tgtJSON, t)
	if err != nil {
		return err
	}
	return repo.SetTargets(role, t)
}
Example #7
0
func loadAndValidateTargets(gun string, repo *tuf.Repo, 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 _, role := range targetsRoles {
		// don't load parent if current role is "targets",
		// we must load all ancestor roles for delegations to validate the full parent chain
		ancestorRole := role
		for ancestorRole != data.CanonicalTargetsRole {
			ancestorRole = path.Dir(ancestorRole)
			if _, ok := repo.Targets[ancestorRole]; !ok {
				err := loadTargetsFromStore(gun, ancestorRole, repo, store)
				if err != nil {
					return nil, err
				}
			}
		}
		var (
			t   *data.SignedTargets
			err error
		)
		if t, err = validateTargets(role, roles, repo); err != nil {
			if _, ok := err.(data.ErrInvalidRole); ok {
				// role wasn't found in its parent. It has been removed
				// or never existed. Drop this role from the update
				// (by not adding it to updatesToApply)
				continue
			}
			logrus.Error("ErrBadTargets: ", err.Error())
			return nil, validation.ErrBadTargets{Msg: err.Error()}
		}
		// this will load keys and roles into the kdb
		err = repo.SetTargets(role, t)
		if err != nil {
			return nil, err
		}
		updatesToApply = append(updatesToApply, roles[role])
	}
	return updatesToApply, nil
}
Example #8
0
// AddTarget generates a fake target and adds it to a repo.
func AddTarget(role string, r *tuf.Repo) (name string, meta data.FileMeta, content []byte, err error) {
	randness := fuzz.Continue{}
	content = RandomByteSlice(1024)
	name = randness.RandString()
	t := data.FileMeta{
		Length: int64(len(content)),
		Hashes: data.Hashes{
			"sha256": utils.DoHash("sha256", content),
			"sha512": utils.DoHash("sha512", content),
		},
	}
	files := data.Files{name: t}
	_, err = r.AddTargets(role, files)
	return
}
Example #9
0
func copyTimestampKey(t *testing.T, fromRepo *tuf.Repo,
	toStore storage.MetaStore, gun string) {

	role, err := fromRepo.GetBaseRole(data.CanonicalTimestampRole)
	assert.NoError(t, err)
	assert.NotNil(t, role, "No timestamp role in the root file")
	assert.Len(t, role.ListKeyIDs(), 1, fmt.Sprintf(
		"Expected 1 timestamp key in timestamp role, got %d", len(role.ListKeyIDs())))

	pubTimestampKey := role.ListKeys()[0]

	err = toStore.SetKey(gun, data.CanonicalTimestampRole, pubTimestampKey.Algorithm(),
		pubTimestampKey.Public())
	assert.NoError(t, err)
}
Example #10
0
func applyRootRoleChange(repo *tuf.Repo, c changelist.Change) error {
	switch c.Action() {
	case changelist.ActionCreate:
		// replaces all keys for a role
		d := &changelist.TufRootData{}
		err := json.Unmarshal(c.Content(), d)
		if err != nil {
			return err
		}
		err = repo.ReplaceBaseKeys(d.RoleName, d.Keys...)
		if err != nil {
			return err
		}
	default:
		logrus.Debug("action not yet supported for root: ", c.Action())
	}
	return nil
}
Example #11
0
// SignAndSerialize calls Sign and then Serialize to get the repo metadata out
func SignAndSerialize(tufRepo *tuf.Repo) (map[string][]byte, error) {
	meta := make(map[string][]byte)

	for delgName := range tufRepo.Targets {
		// we'll sign targets later
		if delgName == data.CanonicalTargetsRole {
			continue
		}

		signedThing, err := tufRepo.SignTargets(delgName, data.DefaultExpires("targets"))
		if err != nil {
			return nil, err
		}
		metaBytes, err := json.MarshalCanonical(signedThing)
		if err != nil {
			return nil, err
		}

		meta[delgName] = metaBytes
	}

	// these need to be generated after the delegations are created and signed so
	// the snapshot will have the delegation metadata
	rs, tgs, ss, ts, err := Sign(tufRepo)
	if err != nil {
		return nil, err
	}

	rf, tgf, sf, tf, err := Serialize(rs, tgs, ss, ts)
	if err != nil {
		return nil, err
	}

	meta[data.CanonicalRootRole] = rf
	meta[data.CanonicalSnapshotRole] = sf
	meta[data.CanonicalTargetsRole] = tgf
	meta[data.CanonicalTimestampRole] = tf

	return meta, nil
}
Example #12
0
func applyTargetsChange(repo *tuf.Repo, c changelist.Change) error {
	var err error
	switch c.Action() {
	case changelist.ActionCreate:
		logrus.Debug("changelist add: ", c.Path())
		meta := &data.FileMeta{}
		err = json.Unmarshal(c.Content(), meta)
		if err != nil {
			return err
		}
		files := data.Files{c.Path(): *meta}
		_, err = repo.AddTargets(c.Scope(), files)
	case changelist.ActionDelete:
		logrus.Debug("changelist remove: ", c.Path())
		err = repo.RemoveTargets(c.Scope(), c.Path())
	default:
		logrus.Debug("action not yet supported: ", c.Action())
	}
	if err != nil {
		return err
	}
	return nil
}
Example #13
0
func validateTargets(role string, roles map[string]storage.MetaUpdate, repo *tuf.Repo) (*data.SignedTargets, error) {
	// TODO: when delegations are being validated, validate parent
	//       role exists for any delegation
	s := &data.Signed{}
	err := json.Unmarshal(roles[role].Data, s)
	if err != nil {
		return nil, fmt.Errorf("could not parse %s", role)
	}
	// version specifically gets validated when writing to store to
	// better handle race conditions there.
	var targetOrDelgRole data.BaseRole
	if role == data.CanonicalTargetsRole {
		targetOrDelgRole, err = repo.GetBaseRole(role)
		if err != nil {
			logrus.Debugf("no %s role loaded", role)
			return nil, err
		}
	} else {
		delgRole, err := repo.GetDelegationRole(role)
		if err != nil {
			logrus.Debugf("no %s delegation role loaded", role)
			return nil, err
		}
		targetOrDelgRole = delgRole.BaseRole
	}
	if err := signed.Verify(s, targetOrDelgRole, 0); err != nil {
		return nil, err
	}
	t, err := data.TargetsFromSigned(s)
	if err != nil {
		return nil, err
	}
	if !data.ValidTUFType(t.Signed.Type, data.CanonicalTargetsRole) {
		return nil, fmt.Errorf("%s has wrong type", role)
	}
	return t, nil
}
Example #14
0
// Sign signs all top level roles in a repo in the appropriate order
func Sign(repo *tuf.Repo) (root, targets, snapshot, timestamp *data.Signed, err error) {
	root, err = repo.SignRoot(data.DefaultExpires("root"))
	if err != nil {
		return nil, nil, nil, nil, err
	}
	targets, err = repo.SignTargets("targets", data.DefaultExpires("targets"))
	if err != nil {
		return nil, nil, nil, nil, err
	}
	snapshot, err = repo.SignSnapshot(data.DefaultExpires("snapshot"))
	if err != nil {
		return nil, nil, nil, nil, err
	}
	timestamp, err = repo.SignTimestamp(data.DefaultExpires("timestamp"))
	if err != nil {
		return nil, nil, nil, nil, err
	}
	return
}
Example #15
0
// Sign signs all top level roles in a repo in the appropriate order
func Sign(repo *tuf.Repo) (root, targets, snapshot, timestamp *data.Signed, err error) {
	root, err = repo.SignRoot(data.DefaultExpires("root"))
	if _, ok := err.(data.ErrInvalidRole); err != nil && !ok {
		return nil, nil, nil, nil, err
	}
	targets, err = repo.SignTargets("targets", data.DefaultExpires("targets"))
	if _, ok := err.(data.ErrInvalidRole); err != nil && !ok {
		return nil, nil, nil, nil, err
	}
	snapshot, err = repo.SignSnapshot(data.DefaultExpires("snapshot"))
	if _, ok := err.(data.ErrInvalidRole); err != nil && !ok {
		return nil, nil, nil, nil, err
	}
	timestamp, err = repo.SignTimestamp(data.DefaultExpires("timestamp"))
	if _, ok := err.(data.ErrInvalidRole); err != nil && !ok {
		return nil, nil, nil, nil, err
	}
	return
}
Example #16
0
// signs and serializes the metadata for a canonical role in a tuf repo to JSON
func serializeCanonicalRole(tufRepo *tuf.Repo, role string) (out []byte, err error) {
	var s *data.Signed
	switch {
	case role == data.CanonicalRootRole:
		s, err = tufRepo.SignRoot(data.DefaultExpires(role))
	case role == data.CanonicalSnapshotRole:
		s, err = tufRepo.SignSnapshot(data.DefaultExpires(role))
	case tufRepo.Targets[role] != nil:
		s, err = tufRepo.SignTargets(
			role, data.DefaultExpires(data.CanonicalTargetsRole))
	default:
		err = fmt.Errorf("%s not supported role to sign on the client", role)
	}

	if err != nil {
		return
	}

	return json.Marshal(s)
}
Example #17
0
func generateSnapshot(gun string, repo *tuf.Repo, store storage.MetaStore) (*storage.MetaUpdate, error) {
	role, err := repo.GetBaseRole(data.CanonicalSnapshotRole)
	if err != nil {
		return nil, validation.ErrBadRoot{Msg: "root did not include snapshot role"}
	}

	algo, keyBytes, err := store.GetKey(gun, data.CanonicalSnapshotRole)
	if err != nil {
		return nil, validation.ErrBadHierarchy{Msg: "could not retrieve snapshot key. client must provide snapshot"}
	}
	foundK := data.NewPublicKey(algo, keyBytes)

	validKey := false
	for _, id := range role.ListKeyIDs() {
		if id == foundK.ID() {
			validKey = true
			break
		}
	}
	if !validKey {
		return nil, validation.ErrBadHierarchy{
			Missing: data.CanonicalSnapshotRole,
			Msg:     "no snapshot was included in update and server does not hold current snapshot key for repository"}
	}

	currentJSON, err := store.GetCurrent(gun, data.CanonicalSnapshotRole)
	if err != nil {
		if _, ok := err.(storage.ErrNotFound); !ok {
			return nil, validation.ErrValidation{Msg: err.Error()}
		}
	}
	var sn *data.SignedSnapshot
	if currentJSON != nil {
		sn = new(data.SignedSnapshot)
		err := json.Unmarshal(currentJSON, sn)
		if err != nil {
			return nil, validation.ErrValidation{Msg: err.Error()}
		}
		err = repo.SetSnapshot(sn)
		if err != nil {
			return nil, validation.ErrValidation{Msg: err.Error()}
		}
	} else {
		// this will only occurr if no snapshot has ever been created for the repository
		err := repo.InitSnapshot()
		if err != nil {
			return nil, validation.ErrBadSnapshot{Msg: err.Error()}
		}
	}
	sgnd, err := repo.SignSnapshot(data.DefaultExpires(data.CanonicalSnapshotRole))
	if err != nil {
		return nil, validation.ErrBadSnapshot{Msg: err.Error()}
	}
	sgndJSON, err := json.Marshal(sgnd)
	if err != nil {
		return nil, validation.ErrBadSnapshot{Msg: err.Error()}
	}
	return &storage.MetaUpdate{
		Role:    data.CanonicalSnapshotRole,
		Version: repo.Snapshot.Signed.Version,
		Data:    sgndJSON,
	}, nil
}
Example #18
0
func changeTargetsDelegation(repo *tuf.Repo, c changelist.Change) error {
	switch c.Action() {
	case changelist.ActionCreate:
		td := changelist.TUFDelegation{}
		err := json.Unmarshal(c.Content(), &td)
		if err != nil {
			return err
		}

		// Try to create brand new role or update one
		// First add the keys, then the paths.  We can only add keys and paths in this scenario
		err = repo.UpdateDelegationKeys(c.Scope(), td.AddKeys, []string{}, td.NewThreshold)
		if err != nil {
			return err
		}
		return repo.UpdateDelegationPaths(c.Scope(), td.AddPaths, []string{}, false)
	case changelist.ActionUpdate:
		td := changelist.TUFDelegation{}
		err := json.Unmarshal(c.Content(), &td)
		if err != nil {
			return err
		}
		delgRole, err := repo.GetDelegationRole(c.Scope())
		if err != nil {
			return err
		}

		// We need to translate the keys from canonical ID to TUF ID for compatibility
		canonicalToTUFID := make(map[string]string)
		for tufID, pubKey := range delgRole.Keys {
			canonicalID, err := utils.CanonicalKeyID(pubKey)
			if err != nil {
				return err
			}
			canonicalToTUFID[canonicalID] = tufID
		}

		removeTUFKeyIDs := []string{}
		for _, canonID := range td.RemoveKeys {
			removeTUFKeyIDs = append(removeTUFKeyIDs, canonicalToTUFID[canonID])
		}

		// If we specify the only keys left delete the role, else just delete specified keys
		if strings.Join(delgRole.ListKeyIDs(), ";") == strings.Join(removeTUFKeyIDs, ";") && len(td.AddKeys) == 0 {
			return repo.DeleteDelegation(c.Scope())
		}
		err = repo.UpdateDelegationKeys(c.Scope(), td.AddKeys, removeTUFKeyIDs, td.NewThreshold)
		if err != nil {
			return err
		}
		return repo.UpdateDelegationPaths(c.Scope(), td.AddPaths, td.RemovePaths, td.ClearAllPaths)
	case changelist.ActionDelete:
		return repo.DeleteDelegation(c.Scope())
	default:
		return fmt.Errorf("unsupported action against delegations: %s", c.Action())
	}

}
Example #19
0
func changeTargetsDelegation(repo *tuf.Repo, c changelist.Change) error {
	switch c.Action() {
	case changelist.ActionCreate:
		td := changelist.TufDelegation{}
		err := json.Unmarshal(c.Content(), &td)
		if err != nil {
			return err
		}
		r, _, err := repo.GetDelegation(c.Scope())
		if _, ok := err.(data.ErrNoSuchRole); err != nil && !ok {
			// error that wasn't ErrNoSuchRole
			return err
		}
		if err == nil {
			// role existed, attempt to merge paths and keys
			if err := r.AddPaths(td.AddPaths); err != nil {
				return err
			}
			return repo.UpdateDelegations(r, td.AddKeys)
		}
		// create brand new role
		r, err = td.ToNewRole(c.Scope())
		if err != nil {
			return err
		}
		return repo.UpdateDelegations(r, td.AddKeys)
	case changelist.ActionUpdate:
		td := changelist.TufDelegation{}
		err := json.Unmarshal(c.Content(), &td)
		if err != nil {
			return err
		}
		r, keys, err := repo.GetDelegation(c.Scope())
		if err != nil {
			return err
		}

		// We need to translate the keys from canonical ID to TUF ID for compatibility
		canonicalToTUFID := make(map[string]string)
		for tufID, pubKey := range keys {
			canonicalID, err := utils.CanonicalKeyID(pubKey)
			if err != nil {
				return err
			}
			canonicalToTUFID[canonicalID] = tufID
		}

		removeTUFKeyIDs := []string{}
		for _, canonID := range td.RemoveKeys {
			removeTUFKeyIDs = append(removeTUFKeyIDs, canonicalToTUFID[canonID])
		}

		// If we specify the only keys left delete the role, else just delete specified keys
		if strings.Join(r.KeyIDs, ";") == strings.Join(removeTUFKeyIDs, ";") && len(td.AddKeys) == 0 {
			r := data.Role{Name: c.Scope()}
			return repo.DeleteDelegation(r)
		}
		// if we aren't deleting and the role exists, merge
		if err := r.AddPaths(td.AddPaths); err != nil {
			return err
		}

		// Clear all paths if we're given the flag, else remove specified paths
		if td.ClearAllPaths {
			r.RemovePaths(r.Paths)
		} else {
			r.RemovePaths(td.RemovePaths)
		}
		r.RemoveKeys(removeTUFKeyIDs)
		return repo.UpdateDelegations(r, td.AddKeys)
	case changelist.ActionDelete:
		r := data.Role{Name: c.Scope()}
		return repo.DeleteDelegation(r)
	default:
		return fmt.Errorf("unsupported action against delegations: %s", c.Action())
	}

}
Example #20
0
func changeTargetsDelegation(repo *tuf.Repo, c changelist.Change) error {
	switch c.Action() {
	case changelist.ActionCreate:
		td := changelist.TufDelegation{}
		err := json.Unmarshal(c.Content(), &td)
		if err != nil {
			return err
		}
		r, err := repo.GetDelegation(c.Scope())
		if _, ok := err.(data.ErrNoSuchRole); err != nil && !ok {
			// error that wasn't ErrNoSuchRole
			return err
		}
		if err == nil {
			// role existed, attempt to merge paths and keys
			if err := r.AddPaths(td.AddPaths); err != nil {
				return err
			}
			return repo.UpdateDelegations(r, td.AddKeys)
		}
		// create brand new role
		r, err = td.ToNewRole(c.Scope())
		if err != nil {
			return err
		}
		return repo.UpdateDelegations(r, td.AddKeys)
	case changelist.ActionUpdate:
		td := changelist.TufDelegation{}
		err := json.Unmarshal(c.Content(), &td)
		if err != nil {
			return err
		}
		r, err := repo.GetDelegation(c.Scope())
		if err != nil {
			return err
		}
		// If we specify the only keys left delete the role, else just delete specified keys
		if strings.Join(r.KeyIDs, ";") == strings.Join(td.RemoveKeys, ";") && len(td.AddKeys) == 0 {
			r := data.Role{Name: c.Scope()}
			return repo.DeleteDelegation(r)
		}
		// if we aren't deleting and the role exists, merge
		if err := r.AddPaths(td.AddPaths); err != nil {
			return err
		}
		if err := r.AddPathHashPrefixes(td.AddPathHashPrefixes); err != nil {
			return err
		}
		r.RemoveKeys(td.RemoveKeys)
		r.RemovePaths(td.RemovePaths)
		r.RemovePathHashPrefixes(td.RemovePathHashPrefixes)
		return repo.UpdateDelegations(r, td.AddKeys)
	case changelist.ActionDelete:
		r := data.Role{Name: c.Scope()}
		return repo.DeleteDelegation(r)
	default:
		return fmt.Errorf("unsupported action against delegations: %s", c.Action())
	}

}