Skip to content

Instantly share code, notes, and snippets.

@ecordell
Created July 15, 2016 14:21
Show Gist options
  • Save ecordell/cfba62d2f82ab5a252912b242dd74224 to your computer and use it in GitHub Desktop.
Save ecordell/cfba62d2f82ab5a252912b242dd74224 to your computer and use it in GitHub Desktop.
TUF Public Log

RFC: Public Log

This 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.

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 keeping older certs around in the root metadata. Although this does successfully allow rotation, there are a couple of problems:

  • 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 stored in the root.json file
  • Having a large number of old, possibly compromised keys in the root.json makes the metadata more complex and less (human) parsable, possibly increasing the likelihood of implementation errors.

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 in signatures block.
  • sign new metadata with A.key and put in parentSignatures 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) to namespace.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 the signatures block
  • the namespace owner signs the new metadata with namespace.pub, the old key, and puts that signature in the parentSignatures block
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment