the repo if signing was successful. This way, we don't mutate the existing root
in a failed attempt to sign it.
Signed-off-by: Ying Li <ying.li@docker.com>
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>
This is a trivial wrapper around the NotaryRepository functionality. The
UI is simplest possible, a single (notary cert rotate) rotates all
certificates.
This handles the common case (only a single certificate)
perfectly
If there were multiple certificates, rotating all of them
regardless of age does not really hurt; we can easily extend this to
give the user more control (having the user specify a cert ID, for
example) later if necessary.
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
NotaryRepository can now list root certificates, and
generate new versions (as changelists to be applied
on Publish).
This is a pretty mechanical encapsulation of the
root certificate rotation support in Repo.AddBaseKeys
and Repo.RemoveBaseKeys. The only slightly interesting
part is ListRootCert, which requires on-line access
to ensure fresh data, and depends on CertStore doing
some verification for us.
Signed-off-by: Miloslav Trmač <mitr@redhat.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>
The only thing depending on signed.Sign keeping old signatures was two
tests; all real users were modifying the signed data without clearing
the old signatures, and therefore implicitly relying on signing with
the keys which were used for the old signatures as well.
This broke signing an updated root with a new certificate when the old
certificate was no longer available.
It could have been fixed by keeping signed.Sign as is and adding the
clearing to all users, but noting actually needs the appending
semantics, the appending semantics is surprising, and switching to
replacing signatures is less code.
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
Instead of silently ignoring any signing errors, and failing with a
generic ErrInsufficientSignatures, just pass through any signing errors.
As long as the role's threshold is met, some primary keys may be
missing. But if any key does exist (whether primary or optional), failures
using that key are fatal. (This is why signed.Sign needs to know the
role's threshold; making any missing key fatal would break signatures
of roles with 2 valid keys and threshold of 1.)
As a side effect, this improves the “snapshot key is not available”
detection in NotaryRepository.Publish, to trigger only when a key is not
available, not also when it should be available but using it is failing.
Similarly, the ErrInsufficientSignatures error now mentions only the
missing keys, not the successfully used keys now that present but
failing keys are handled by using other error types.
(This removes the second ErrInsufficientSignatures check because it is
no longer necessary, which also fixes the fact that it was broken by
introducing optional keys. The first, now only, ErrInsufficientSignatures
check does correctly distinguish between primary and optional keys.)
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
Ordinarily we don't want to continue operating on signed data if the
role's threshold of signatures cannot be me and the signature is unsuable.
OTOH we want to keep signing root.json with all older keys if they are
available (to allow migration), but in that case a missing key is not a
fatal error.
So, split the keys passed to signed.Sign into primary and optional,
treating all current uses as primary and enforcing the role's threshold
only on primary keys. Also update the single existing test which uses
a missing/unusable key to use the optionalKeys parameter.
Note that only the _presence_ of optionalKeys is optional; if an
optional key exists but signing using it fails, the function will fail.
This temporarily breaks the second ErrInsufficientSignatures check
(optional keys count against the role threshold), but that will be fixed
soon.
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
Tell signed.Sign how many signatures are necessary to sign a role, and
have it fail if it cannot create that many.
For most uses this does not make much of a difference because the
threshold tends to be 1 and signed.Sign was already failing if no key
could be found or if no signature could be created; only >1-threshold
roles now (correctly) fail in additional situations. But the knowledge
of a role’s threshold will be useful in a future commit.
Always use ErrInsufficientSignatures for this failure, whether this is
when loading the keys or actually using them (also fixing
ErrInsufficentSignature documentation to refer to signing and not
verification). ErrNoKeys is no longer returned by signed.Sign.
So, adjust the “snapshot key is not available” logic in
NotaryRepository.Publish accordingly, which also makes it more precise
(actually triggering only when no snapshot key is available).
Now that role's threshold is enforced when signing, update
TestValidateRootInvalidTimestampThreshold to create the second key
necessary to correctly sign the timestamp role.
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
This is a refactoring with no behavior change, but it will allow adding
more parameters to the function in the future.
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
If we update the maximum value for "infinite" in the future, it is possible
that we forget to update this comments and thus leads misunderstanding.
Signed-off-by: Hu Keping <hukeping@huawei.com>
ignores buildtags, for instance, and somehow didn't pick up some code in
the signer.
This calls golint on every go file in the repo and also fixes some linting
issues, which involves renaming two yubikey functions to avoid stuttering.
Signed-off-by: Ying Li <ying.li@docker.com>