Public Log Proposal
See original GitHub issueThis proposes a change to the TUF spec to support a tamper-evident public log of changes to the root authority in a TUF repository and to allow trust-pinning for delegated roles.
There is further discussion of this idea at https://github.com/docker/notary/issues/458 and https://github.com/docker/notary/issues/835
Motivation
From the TUF spec:
To replace a compromised root key or any other top-level role key, the root role signs a new root.json file that lists the updated trusted keys for the role. When replacing root keys, an application will sign the new root.json file with both the new and old root keys until all clients are known to have obtained the new root.json file (a safe assumption is that this will be a very long time or never).
Currently, rotating the root keys requires signing with old keys indefinitely. Although this does successfully allow rotation, supporting even moderately aggressive rotation schemes (say, having a root threshold of 2/3 available keys and rotating those keys quarterly) results in a large number of old keys that need to be kept around.
Note that this may be less of a concern in TUF as-specified:
Periodically, the software update system using the framework instructs the framework to check each repository for updates.
If clients can be assumed to be constantly polling, there may be an opportunity to remove older keys. But this is not the case for package managers in general - current partial/full implementations in PyPI, RubyGems, and Notary do not have “periodic” polling as one of their properties.
Proposal
The canonical (server) TUF metadata repository should version the root.json file any time root keys change, and should point to a “parent” root.json.
{
"signed": {
"_type": "Root",
// ...
"parent": "123" // sha256 of previous root.json
},
"parentSignatures" {
// current metadata signed with parent keys/threshold
},
"signatures": [
// current metadata signed with current keys/threshold
]
}
When a client gets new metadata with a parent
and parentSignatures
block, it does the following:
- requests the parent metadata file from the server by hash
- verifies the hash
- using the public keys and threshold of the parent, calculates the signatures in
parentSignatures
of the new metadata - repeats the process if the parent also has a
parent
block, until either the hash matches the current local metadata or the pulled metadata doesn’t have a parent block.
Example
Rotating a single root key from A
to B
- switch public key in public portion to point to
B.pub
- calculate hash of original metadata and put it in the
parent
block of the new metadata - sign new metadata with
B.key
and put insignatures
block. - sign new metadata with
A.key
and put inparentSignatures
block. - publish new metadata
- old metadata should still be accessible by hash (not name)
Pinning Delegated Trust
Trust pinning is unspecified in TUF, though it does exist in Notary, which requires pinning an x509 cert or CA.
Using a public log on delegations would allow trust pinning with key rotation. Instead of pinning to a CA or cert, a pubic key (ideally, a set of public keys) would be pinned.
If trust is pinned for a delegation, we require that there is a valid parent chain back to the pinned public keys. This works analagously to the root key rotation case.
An interesting property that falls out of this is that users can decide where to root their trust (the registry or any delegated target owner) without losing the TUF freshness/rotation ability.
We leave the semantics of specifying pinned trust unspecified for now.
Example
Rotating a pinned delegation key
- trust is pinned for a delegation (e.g.
targets/namespace
) tonamespace.pub
- the namespace owner generates a new keypair and new namespace metadata
- the namespace owner calculates the hash of original metadata and puts it in the
parent
block of the new metadata - the namespace owner signs the new metadata with
new_namespace.pub
and puts that signature in thesignatures
block - the namespace owner signs the new metadata with
namespace.pub
, the old key, and puts that signature in theparentSignatures
block
Issue Analytics
- State:
- Created 7 years ago
- Comments:9 (7 by maintainers)
Top GitHub Comments
Agreed. Updated proposal reflecting that:
One thing that jumps out, the breakup of
parentSignatures
andsignatures
is undesirable. Whenever I get a new root, regardless of whether a root rotation has actually happened or not, I must validate it using both the root keys I know, and the root keys it contains. In many instances it will not be the root key that has changed, soparentSignatures
andsignatures
will contain duplicate signatures for the same set of root keys.If a root key rotation has occurred, I can’t see any benefit in breaking up the signatures of the old and new keys as if there is a threshold n with n > 1, I may have rotated only one key, yet
parentSignatures
andsignatures
will now both have to contain n signatures, but with subsets of size n-1 being identical.