versioned root role in the root.json. That way we can figure out which keys were
previously root keys.
Update tuf.Repo.sign to take a list of required roles (at most two, for root
rotations, because only the immediate root after a rotation absolutely needs
to correctly validate against the previous root role and the new root role)
instead of just a single role.
tuf.Repo.sign now ensures that the number of signatures on the metadata satisfy
role requirements for every required role. Then it tries to sign with
whatever optional keys it can, ignoring errors and not requiring that any
particular number of signatures were produced with the optional keys.
Signed-off-by: Ying Li <ying.li@docker.com>
Repo.RotateBaseKeys() can now be used to replace the root keypair
(i.e. primarily the expiring certificate), and Repo.SignRoot will make
signatures using the old keypairs if they are available (but not fail if
they are not; only the keypairs listed in the role as trusted are
mandatory).
To do this, we need to keep even the old, possibly currently untrusted,
keypairs around (to allow rollover from clients which trust these old
keys):
The private keys are simply not deleted from the repo's CryptoService;
this means no change for the current setup with a long-term private key
and periodically expiring certificates, but a true rotation of the
private key will eventually require explicit management of the preserved
long-term private keys (if we are keeping several of them around, but
most are either obsolete and non-preferred or possibly even known to be
compromised, we will want to make sure that we always use the
new/preferred private key for new certificate generation).
The public keys are tricker:
1) We need to keep a list of them; the private keys can be looked up
by their IDs, and that allows extracting the public part as well,
but we need a list of the key IDs. We can't just keep the key IDs
included in the role's list of authorized keys, that would make it
impossible to rotate away from a suspect or known compromsied key.
2) With the X.509 certificate “public keys”, key ID is not actually
sufficient to retrieve the full public key even if we have access to the
private key; we actually need to store the full public key ==
certificate somewhere. And preferably without having tuf.Repo depend on
a certs.Manager, designed to deal with concepts of trust at a higher
level than TUF cares about. Actually, to the extent certs.Manager's
purpose is to manage and verify trust, storing old, possibly suspect or
known compromised certificates would be explicitly contrary to its
mission.
So, this patch keeps around full copies of the certificates in the
root.json “keys” map (not the “roles” map of trusted keys). It means
sending to clients a little data which they don't need but it is
otherwise harmless; and keeping the certificates within the
structured and managed tuf.data.Root format could allow us to build nice
UI (e.g. show me all certificates we still carry and keep signing with, let
me drop two of them now that our company has changed a name and does not
want to advertise the history) if we ever needed to something like this
Signed-off-by: Ying Li <ying.li@docker.com>
So UpdateDelegation, DeleteDelegation, AddTargets, RemoveTargets now
all check for the role existence, not metadata existence. And they
also check the role's signing keys - there's no point in adding if
we can't sign.
Signed-off-by: Ying Li <ying.li@docker.com>
Previously we were always signing it, but we can't do that anymore
because then delegated users won't be able to publish ever (they
probably don't have the target key).
Some other related changes: when role keys are rotated, that role
needs to be marked as dirty now in order to be re-signed and
published.
Signed-off-by: Ying Li <ying.li@docker.com>
Only create it when a target is added to it, or other delegations
are added to it, or when getting a child delegation.
Signed-off-by: Ying Li <ying.li@docker.com>