Indexing targets with TUF is hard if using delegations
See original GitHub issueThe Importance of a Target Index
First, let’s be clear about this: if you’re looking for the trustworthy target info (hash, length, etc.) for a target whose name you know, TUF has that clearly solved for you: the algorithms are in the specification (item 4.5 here) and the reference implementation provides Updater.get_one_valid_target_info()
implementing them. This works fine with or without using delegations.
However, an important part of many update systems is finding out what is available in the first place. There are reasonable arguments for using TUF to find target listings:
- The security of a target index is important, because incorrect listings pose security risks:
- Removed items: Damage can be done if, when a user wants to know what targets are available on a repository, items are removed (DoS). One could prevent important updates or reduce the visibility of some package.
- (Added items: Note that regardless of how this listing is obtained, the addition of other items cannot be particularly harmful as long as the item 4.5 procedure from the spec is followed (e.g.
Updater.get_one_valid_target_info()
). You can lose cycles looking for them, but that’s about it.)
- TUF has to include target lists anyway, in each role.
Obtaining a Target Index from TUF
Without Delegations
If you do not use delegations, the listing of targets on the repository is easy to obtain and verify. The same protection provided for an individual target is extended for the listing of targets itself, with all of TUF’s guarantees. This is because the list exists in one location, the top-level Targets role, and can always be verified using the information in the Root metadata. Just refreshing top-level metadata automatically gets you a verified index of targets, secured by the Targets role via Root.
With Delegations
If you do use delegations, getting a listing of available targets on the repository is more involved and has more complex security implications.
All Roles
If you don’t already know what role lists the targets you might be interested in, you would have to walk the graph from Targets to obtain and verify all delegated roles to fill your list. (We can and should provide that in a function, to replace the deprecated Updater.all_targets()
, and the algorithm should be added to the specification IMO so that no implementer produces something like the current insecure all_targets()
. Note also that this is not very efficient.) The security of such an all-targets query is not the same as the no-delegations security provided above, where the list is secured by Targets via Root. Instead, using delegations, there’s a generally-weaker Snapshot role guarantee that you are not unknowingly missing role files or updates to role files, and a guarantee from each delegated role file itself that it has not added or excluded targets.
Specified Role(s)
If you do already know what delegated role lists the targets you’re interested in, then you can jump to that role and that role is essentially providing you a listing service. That service then has the following ill-defined security:
- Removed items:
- The Targets role again does not protect you from removed items.
- Your protection from missing items now comes down to the Snapshot role telling you what version of the delegated role you should have, and if you already had that version of the role, you can tell if it has changed (presumably maliciously). There is no longer any well-defined Targets or even delegated-targets role protection for the listing itself. The latter is because there is no well-defined way to verify the delegated targets role itself, since it doesn’t come in the context of walking the delegation graph in search of a specific target. You can establish your own expectations for how the role you directly sought has been signed, but that is not part of the TUF spec, even if it seems intuitive in a particular case; there are edge cases with results that are not defined. (If this is not clear, please ask and I’ll elaborate with an example.)
- Added items:
- Because you should still be walking the delegation graph for each target that remains in the list you receive, you will still have the same protection from maliciously added items in the target listing.
Issue Analytics
- State:
- Created 5 years ago
- Comments:9 (9 by maintainers)
Top GitHub Comments
@trishankatdatadog
I’m finishing up rewriting
all_targets
andtargets_of_role
now (no longer deprecated 🎉).targets_of_role
isn’t really interesting, and I’m content with the way I’ve rewritten it (docstring here).There are two ways I could go about
all_targets
, though.In both models, there will be two steps:
The two options pertain to step one, and the security guarantees provided for inclusion of target names. I can take two approaches:
OR
Since step 2 will protect you from attackers’ adding targets, the distinction is in what credentials are required for an attacker to de-list things. For 1A, a mirror without keys can de-list targets by providing junk metadata, without detection. For 1B, missing roles are noticed, so nobody can be quietly deprived of their ability to list targets.
I’m going the 1B direction. That results in this docstring for
all_targets
. LMK if you actually find 1A more appealing .(and just to document the current situation: ngclient does not expose the metadata to callers and does not implement
all_targets()
ortargets_of_role()
for these reasons).