Trust Pinning
See original GitHub issueProposal
Proposal #: 2 Title: Trust Pinning Author: Evan Cordell, Jake Moshenko, Justin Cappos, Vladimir Diaz, Sebastien Awwad Created: August 18, 2016 Type: Design Status: Draft
Rationale
This is a proposal of a design for pinning trusted keys, which would allow clients to root their trust at a namespace (or lower) delegated set of keys instead of the repository root.
An example use case: I trust django’s published public keys to have signed off on django, but I don’t necessarily trust PyPI to remain uncompromised (and possibly convince me of different public keys for django).
This proposal also addresses the problem of private metadata. In the current TUF spec, there is no way to hide all information about the existence of other metadata in the system. This is a problem in a multi-tenant scenario where knowledge of metametadata could be sensitive (e.g. timing of creating a target, names of targets, etc).
Overview
We introduce a new file, pinned.json
, which permits users to pin root files and associate them with a particular namespace prefix. If an entry matches the current package, the pinned root must be used. If it does not match, there is a fallback to the global root.
This constructs two (or more) trust paths for target files, and allows the user to pick between them. Clients that trust the global root (e.g. PyPI) will trust all packages served by it, and those that wish to root trust with a namespace owner (e.g. django project) can pin to those keys. See this diagram for an example.
Because the pinning mechanism uses roots, the “pinned” keys may be rotated according to the standard root rotation scheme. In that sense, you are pinning the root of a tree of keys which can grow over time, rather than pinning a set of keys that must never change.
Pin File
pinned.json
maps a prefix to a location where the pinned root can be found and an optional url for updating it. This file is not available from a TUF repository. It is either constructed by explicit actions from the client (e.g. “pin this role’s keys”) or by an out-of-band bootstrap (e.g. “here’s our organization’s pinned.json
”).
{
"django": {
"location": "pinned/django", // default pinned directory structure
"url": "https://www.djangoproject.com/release/metadata"
},
"private-requests-beta": {
"location": "pinned/requests"
// no url - can't be updated automatically
},
"private-flask-beta": {
"location": "/usr/local/evan/flask-beta" // metadata can live elsewhere if desired
}
}
Pinned Metadata
Pinned metadata lives in a specific default directory, sharing the same layout as a “normal” repo but nested within a prefix namespace, e.g.
pinned
└── django // prefix
├── root.json
├── snapshot.json
├── targets.json
└── timestamp.json
This can be changed with the location
field of the pinned.json
file, which may be useful if e.g. sharing a network drive.
Complex ACLs can be enforced and/or bootstrapped by sending a user an appropriately generated pinned.json
, noting that any metadata endpoint (root repo, or any pinned repo) can have its own access control mechanism.
Hiding
A private package can be omitted from the primary hierarchy entirely, having its own snapshot
and target
files separate from those provided with root
. The snapshot.json
and target.json
could be signed with the same snapshot and target keys used for the public parts of the repository, or they can be managed and signed by the owner of the private delegated role. Access to these private roles is granted by sending the metadata to the appropriate users (further restricted by ACLs if needed). A url pointing to where the snapshot and timestamp can be found is added to the pinned.json
file in the case of private roles.
Hard Pinning
Hard pinning, in which a specific set of non-changing keys are used, can be accomplished by creating the a pinned metadata repository and not specifying a url. Without a url, nothing can convince a client to use different keys. This may be useful for priming a box for a one-time initial pull (with the assumption that it will be killed rather than updated directly).
Repository structure
With this pinning structure it makes sense to structure namespaces and/or packages with their own roots. Alternately, a user can generate a root for a given package/target delegation locally if it doesn’t exist, by generating keys locally and signing.
Because a delegation is also a target file, a global root can delegate to target files of other repos. This allows a simple way to provide both global and namespaced target files.
Prior Art
It’s worth mentioning that Notary has a pinning implementation currently. Although this proposal differs and has slightly different goals, the Notary format should be compatible with this through a simple transformation.
Issue Analytics
- State:
- Created 7 years ago
- Reactions:1
- Comments:24 (21 by maintainers)
Top GitHub Comments
@endophage
To try to answer your questions succinctly, there are two different reasons for TAPs 4 and 5. Let me try to briefly recap what they are.
TAP 4 is useful for: (1) getting different targets from different repositories (e.g., company-controlled targets first from a private repository, all other targets second from a public repository), or (2) requiring multiple repositories to sign off on the same targets.
To try to answer your concerns, this is exactly what TAP 4 is trying to do. Using a trust pinning file, users can specify as many repositories as they like, and exactly how they trust each repository. We think it is useful to have a standard way for users to do so.
I agree that this is not a common use case. However, requiring multiple repositories (with different roots of trust) to sign off on the same targets is useful in some cases. For example, we plan to use this feature in UPTANE. It improves compromise-resilience, because you do not trust only one repository to have the final say. If two different repositories with different roots of trust (and, thus, different keys) sign the same targets metadata, then chances are better that the targets metadata is trustworthy.
In this sense, multi-repository delegations in TAP 4 is similar, but not identical to multi-role delegations in TAP 3. Multi-role delegations are also useful in improving compromise-resilience, but they still share the same root of trust. If the root keys are compromised, then the multi-role delegations can be overwritten. Not so for multi-repository delegations, because that requires compromising multiple roots of trust (which should be significantly more difficult).
Does this answer your question?
Precisely as you wish, TAP 5 is useful for pinning (in the root metadata file) the keys and URLs used to verify the top-level roles of a repository. There are two features in TAP 5.
First, the root metadata file can be used to control where the metadata files for top-level roles come from. If the URLs for a top-level role is not specified in the root metadata file, then the mirrors in the trust pinning file (TAP 4) would be used to download the metadata file for the top-level role. Otherwise, if the URLs have been specified in the root metadata file, then the mirrors in the trust pinning file will be ignored, and these URLs will be used instead to download the metadata file for the top-level role.
Second, the root metadata file can be used to control the keys used to verify the top-level roles. Thus, as you surmised, instead of using the root metadata file provided by a remote repository, the user can create her own metadata file to pin the keys used by this remote repository. In this way, the user need not automatically agree to revoke and replace the keys used by the remote repository. She can verify them herself before replacing the keys using her own root metadata file.
These two features allow us to implement at least one interesting use case. Suppose that a user trusts only the Django project on the PyPI repository. She is not worried about the timestamp and snapshot keys being compromised on PyPI, because the resulting attacks are not severe. However, where would she get the keys used to verify the Django project on PyPI? In the normal case, she would read the delegation to the Django project off the top-level targets role on PyPI. However, (as a purely hypothetical example), if attackers somehow compromise this top-level targets role, then attackers can distribute incorrect keys for the Django project.
If the user were so inclined, she can avoid this problem by using TAP 5. Instead of using the PyPI root metadata file, she would craft her own root metadata file. Her root metadata file would look as follows:
This is a good question. Per TAP 5, the root metadata file is no longer listed in the snapshot metadata. In order to update the metadata file for a top-level role on a repository, one of the following two rules shall be used. If the URLs for the top-level role have been defined in the root metadata file, then that is where the metadata file shall be fetched. Otherwise, it shall be fetched from the mirrors defined in the trust pinning file.
So, to return to the Django example, this is how metadata files would be downloaded. First, the TUF client would either not update the root metadata file (if so instructed), or it would download the latest root metadata file on a repository of the user’s control. Second, the TUF client would download the timestamp metadata file from PyPI. Third, the TUF client would download the snapshot metadata file from PyPI. Fourth, the TUF client would download the Django delegated targets role metadata file from PyPI. It would verify that the Django metadata file is signed by the keys pinned in her root metadata file, and that its version number nevertheless matches the snapshot metadata signed by PyPI. In effect, this root metadata file allows the user to restrict her trust in PyPI to provide metadata about only the Django project.
Does this make sense? If not, please let us know. I might have botched a thing or two in my explanations. If so, I apologize in advance, and I’m sure @JustinCappos can better explain things.
You can safely ignore TAP 5 for now. It was more a draft from one team member, than something that had been discussed with the intent that I would approve. We will only be adding things that we think are ready to be approved to the repo in the future.
So, TAP 5 will undergo major revisions that will fix this and other issues. We’ll ping you when it has been scrubbed and fixed.
On Wed, Oct 5, 2016 at 6:11 PM, David Lawrence notifications@github.com wrote: