Repository navigation
Description
At the moment we only identify repositories by their user-facing names, which will become problematic once we want to start dealing with composability (repository links #73) and providing information about repositories to other parties (such as for systemd-report purposes) because repository names are fundamentally a local-only concept with no real way of validating them.
For links it would be much better to only instantiate one client and only do a single refresh per repository, as well as potentially making it easier to handle problems like auth, and for systemd-report consumers it will be incredibly useful to be able to categorise repositories using globally unique identifiers.
The only really obvious choice is to use a cryptographic hash of 1.root.json, because:
- It is transport-agnostic.
- Conceptually it defines the initial repository identity. (We already implicitly treat the signing key set as an identity in Quarry, especially on the publishing side.)
- It doesn't require any extensions to TUF.
- It is not something a malicious repository can manipulate directly without a pre-image attack (other ideas like generating a UUID and putting it in the
1.root.jsonsuffer from malicious repositories being able to publish the same identifier).
Once we do the <meta> stuff for self-describing repositories we will likely want to be able to include the identity hash in the <meta> tag.
However, there are some issues with this proposal:
- We need to now store
1.root.jsonon the client (and transmit it in cases like cross-repo links) so that it can independently derive the identifier. But that is not enough -- a malicious repository could provide anN.root.jsonthat is not actually signed with a valid chain from1.root.jsonto impersonate a repository. So we also need to emulateroot.jsonupgrades up to the currentN.root.jsonon first fetch when figuring out the repository identity to make sure that the signature chain is really valid. Forroot_trust = { type = "bundled", ... }we probably should change the structure so that we keep all historicalroot.jsons to avoid post-key-compromise attacks. - The ideas for using prefix-based matching for AuthN/AuthZ providers (as mentioned in tufclient: authenticate repo requests as this machine #82 (comment)) is kind of dead in the water. I guess we would need to instead restrict it based on which domains we are contacting...?
- We probably should hash the canonicalised JSON rather than just using the original bytes so that we don't end up with fragile setups.
- There is now an incentive for an attacker to compromise the initial
1.root.jsonkeys even after rotation, since a fully broken1.root.jsonmight allow you to impersonate that repository... Maybe this is a serious enough issue to warrant a different design?
Activity
pothos commentedon Nov 9, 2026
Requiring that different repos use different root keys means disallowing as policy instead of convention. Isn't this too strict and could lead to subtle issues?
I wonder if a UUID would help or if a fixed free form name that's not mentioning the domain would be good enough?
cyphar commentedon Nov 11, 2026
Sharing root keys between repositories is an incredibly bad idea -- it means that one repository can masquerade as another and completely lock out the original repository from providing future updates (because clients will reject future updates from the original repository once the bad repository rotates
root.json). Other keys can be shared in principle but you should have at least one key be unique to avoid trivial repo replacement attacks (my preference is the snapshot key).The problem with UUIDs or static "proper" names is that nothing stops you from using the same UUID in another repository as there is nothing that actually strongly links it to the original repository. For reports the identifier would ideally be something that can't just be faked because otherwise you will need to emulate something equivalent to a proper identifier to figure out which repository something came from. (If we don't care about having /static/ identities then just using the latest
root.jsonhash is basically good enough.)Using the current pseudo-URL approach for repo names is /okay/ if we were to implement the
<meta>tag stuff discussed elsewhere because it would make the repository much harder to forge but it would mean that custom mirrors of repositories wouldn't work as easily -- how would you distinguish between a legitimate mirror and a fake one as a client? The keys being good is enough to trust it as a mirror but then it has no static identity.I think there are kinda two options here:
root.jsonkey set. This is what TUF essentially does and is the simplest option, but it means we cannot track the identity of repositories long-term. But maybe that's fine?N.root.jsonandN+1.root.jsonwere the first and second signing events.Maybe we should go with (1) for now and keep around copies of the old
root.jsons just in case we decide to switch to (2) at some point.