mirror of https://github.com/docker/docs.git
commit
2874955337
|
@ -29,10 +29,6 @@ func requireValidFixture(t *testing.T, notaryRepo *NotaryRepository) {
|
|||
for _, targetObj := range notaryRepo.tufRepo.Targets {
|
||||
require.True(t, targetObj.Signed.Expires.After(tenYearsInFuture))
|
||||
}
|
||||
|
||||
for _, cert := range notaryRepo.CertStore.GetCertificates() {
|
||||
require.True(t, cert.NotAfter.After(tenYearsInFuture))
|
||||
}
|
||||
}
|
||||
|
||||
// recursively copies the contents of one directory into another - ignores
|
||||
|
|
|
@ -2,7 +2,6 @@ package client
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
@ -87,7 +86,6 @@ type NotaryRepository struct {
|
|||
CryptoService signed.CryptoService
|
||||
tufRepo *tuf.Repo
|
||||
roundTrip http.RoundTripper
|
||||
CertStore trustmanager.X509Store
|
||||
trustPinning trustpinning.TrustPinConfig
|
||||
}
|
||||
|
||||
|
@ -97,15 +95,6 @@ type NotaryRepository struct {
|
|||
func repositoryFromKeystores(baseDir, gun, baseURL string, rt http.RoundTripper,
|
||||
keyStores []trustmanager.KeyStore, trustPin trustpinning.TrustPinConfig) (*NotaryRepository, error) {
|
||||
|
||||
certPath := filepath.Join(baseDir, notary.TrustedCertsDir)
|
||||
certStore, err := trustmanager.NewX509FilteredFileStore(
|
||||
certPath,
|
||||
trustmanager.FilterCertsExpiredSha1,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cryptoService := cryptoservice.NewCryptoService(keyStores...)
|
||||
|
||||
nRepo := &NotaryRepository{
|
||||
|
@ -115,7 +104,6 @@ func repositoryFromKeystores(baseDir, gun, baseURL string, rt http.RoundTripper,
|
|||
tufRepoPath: filepath.Join(baseDir, tufDir, filepath.FromSlash(gun)),
|
||||
CryptoService: cryptoService,
|
||||
roundTrip: rt,
|
||||
CertStore: certStore,
|
||||
trustPinning: trustPin,
|
||||
}
|
||||
|
||||
|
@ -162,22 +150,22 @@ func NewTarget(targetName string, targetPath string) (*Target, error) {
|
|||
return &Target{Name: targetName, Hashes: meta.Hashes, Length: meta.Length}, nil
|
||||
}
|
||||
|
||||
func rootCertKey(gun string, privKey data.PrivateKey) (*x509.Certificate, data.PublicKey, error) {
|
||||
func rootCertKey(gun string, privKey data.PrivateKey) (data.PublicKey, error) {
|
||||
// Hard-coded policy: the generated certificate expires in 10 years.
|
||||
startTime := time.Now()
|
||||
cert, err := cryptoservice.GenerateCertificate(
|
||||
privKey, gun, startTime, startTime.Add(notary.Year*10))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
x509PublicKey := trustmanager.CertToKey(cert)
|
||||
if x509PublicKey == nil {
|
||||
return nil, nil, fmt.Errorf(
|
||||
return nil, fmt.Errorf(
|
||||
"cannot use regenerated certificate: format %s", cert.PublicKeyAlgorithm)
|
||||
}
|
||||
|
||||
return cert, x509PublicKey, nil
|
||||
return x509PublicKey, nil
|
||||
}
|
||||
|
||||
// Initialize creates a new repository by using rootKey as the root Key for the
|
||||
|
@ -218,11 +206,10 @@ func (r *NotaryRepository) Initialize(rootKeyID string, serverManagedRoles ...st
|
|||
}
|
||||
}
|
||||
|
||||
rootCert, rootKey, err := rootCertKey(r.gun, privKey)
|
||||
rootKey, err := rootCertKey(r.gun, privKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.CertStore.AddCert(rootCert)
|
||||
|
||||
var (
|
||||
rootRole = data.NewBaseRole(
|
||||
|
@ -790,9 +777,6 @@ func (r *NotaryRepository) Update(forWrite bool) error {
|
|||
// Partially populates r.tufRepo with this root metadata (only; use
|
||||
// tufclient.Client.Update to load the rest).
|
||||
//
|
||||
// As another side effect, r.CertManager's list of trusted certificates
|
||||
// is updated with data from the loaded root.json.
|
||||
//
|
||||
// Fails if the remote server is reachable and does not know the repo
|
||||
// (i.e. before the first r.Publish()), in which case the error is
|
||||
// store.ErrMetaNotFound, or if the root metadata (from whichever source is used)
|
||||
|
@ -812,7 +796,7 @@ func (r *NotaryRepository) bootstrapClient(checkInitialized bool) (*tufclient.Cl
|
|||
rootJSON, cachedRootErr := r.fileStore.GetMeta(data.CanonicalRootRole, -1)
|
||||
|
||||
if cachedRootErr == nil {
|
||||
signedRoot, cachedRootErr = r.validateRoot(rootJSON)
|
||||
signedRoot, cachedRootErr = r.validateRoot(rootJSON, false)
|
||||
}
|
||||
|
||||
remote, remoteErr := getRemoteStore(r.baseURL, r.gun, r.roundTrip)
|
||||
|
@ -833,7 +817,7 @@ func (r *NotaryRepository) bootstrapClient(checkInitialized bool) (*tufclient.Cl
|
|||
if cachedRootErr != nil {
|
||||
// we always want to use the downloaded root if there was a cache
|
||||
// error.
|
||||
signedRoot, err = r.validateRoot(tmpJSON)
|
||||
signedRoot, err = r.validateRoot(tmpJSON, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -868,7 +852,7 @@ func (r *NotaryRepository) bootstrapClient(checkInitialized bool) (*tufclient.Cl
|
|||
// signatures of the root based on known keys, not expiry or other metadata.
|
||||
// This is so that an out of date root can be loaded to be used in a rotation
|
||||
// should the TUF update process detect a problem.
|
||||
func (r *NotaryRepository) validateRoot(rootJSON []byte) (*data.SignedRoot, error) {
|
||||
func (r *NotaryRepository) validateRoot(rootJSON []byte, fromRemote bool) (*data.SignedRoot, error) {
|
||||
// can't just unmarshal into SignedRoot because validate root
|
||||
// needs the root.Signed field to still be []byte for signature
|
||||
// validation
|
||||
|
@ -878,12 +862,26 @@ func (r *NotaryRepository) validateRoot(rootJSON []byte) (*data.SignedRoot, erro
|
|||
return nil, err
|
||||
}
|
||||
|
||||
err = trustpinning.ValidateRoot(r.CertStore, root, r.gun, r.trustPinning)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
// If we're downloading a root from a remote source, attempt to load a local root
|
||||
// to ensure that we consider old roots when validating this new one
|
||||
var prevRoot *data.SignedRoot
|
||||
if fromRemote {
|
||||
prevRootJSON, err := r.fileStore.GetMeta(data.CanonicalRootRole, -1)
|
||||
// A previous root exists, so we attempt to use it
|
||||
// If for some reason we can't extract it (ex: it's corrupted), we should error client-side to be conservative
|
||||
if err == nil {
|
||||
prevSignedRoot := &data.Signed{}
|
||||
err = json.Unmarshal(prevRootJSON, prevSignedRoot)
|
||||
if err != nil {
|
||||
return nil, &trustpinning.ErrValidationFail{fmt.Sprintf("unable to unmarshal previously trusted root from disk: %v", err)}
|
||||
}
|
||||
prevRoot, err = data.RootFromSigned(prevSignedRoot)
|
||||
if err != nil {
|
||||
return nil, &trustpinning.ErrValidationFail{fmt.Sprintf("error loading previously trusted root into valid role format: %v", err)}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return data.RootFromSigned(root)
|
||||
return trustpinning.ValidateRoot(prevRoot, root, r.gun, r.trustPinning)
|
||||
}
|
||||
|
||||
// RotateKey removes all existing keys associated with the role, and either
|
||||
|
@ -929,7 +927,7 @@ func (r *NotaryRepository) RotateKey(role string, serverManagesKey bool) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, pubKey, err = rootCertKey(r.gun, privKey)
|
||||
pubKey, err = rootCertKey(r.gun, privKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -964,26 +962,12 @@ func (r *NotaryRepository) rootFileKeyChange(cl changelist.Changelist, role, act
|
|||
return cl.Add(c)
|
||||
}
|
||||
|
||||
// DeleteTrustData removes the trust data stored for this repo in the TUF cache and certificate store on the client side
|
||||
// DeleteTrustData removes the trust data stored for this repo in the TUF cache on the client side
|
||||
func (r *NotaryRepository) DeleteTrustData() error {
|
||||
// Clear TUF files and cache
|
||||
if err := r.fileStore.RemoveAll(); err != nil {
|
||||
return fmt.Errorf("error clearing TUF repo data: %v", err)
|
||||
}
|
||||
r.tufRepo = tuf.NewRepo(nil)
|
||||
// Clear certificates
|
||||
certificates, err := r.CertStore.GetCertificatesByCN(r.gun)
|
||||
if err != nil {
|
||||
// If there were no certificates to delete, we're done
|
||||
if _, ok := err.(*trustmanager.ErrNoCertificatesFound); ok {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("error retrieving certificates for %s: %v", r.gun, err)
|
||||
}
|
||||
for _, cert := range certificates {
|
||||
if err := r.CertStore.RemoveCert(cert); err != nil {
|
||||
return fmt.Errorf("error removing certificate: %v: %v", cert, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,78 +0,0 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/notary/trustpinning"
|
||||
"github.com/docker/notary/tuf/data"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// TestValidateRoot through the process of initializing a repository and makes
|
||||
// sure the repository looks correct on disk.
|
||||
// We test this with both an RSA and ECDSA root key
|
||||
func TestValidateRoot(t *testing.T) {
|
||||
logrus.SetLevel(logrus.ErrorLevel)
|
||||
validateRootSuccessfully(t, data.ECDSAKey)
|
||||
if !testing.Short() {
|
||||
validateRootSuccessfully(t, data.RSAKey)
|
||||
}
|
||||
}
|
||||
|
||||
func validateRootSuccessfully(t *testing.T, rootType string) {
|
||||
gun := "docker.com/notary"
|
||||
|
||||
ts, mux, keys := simpleTestServer(t)
|
||||
defer ts.Close()
|
||||
|
||||
repo, _ := initializeRepo(t, rootType, gun, ts.URL, false)
|
||||
defer os.RemoveAll(repo.baseDir)
|
||||
|
||||
// tests need to manually boostrap timestamp as client doesn't generate it
|
||||
err := repo.tufRepo.InitTimestamp()
|
||||
require.NoError(t, err, "error creating repository: %s", err)
|
||||
|
||||
// Initialize is supposed to have created new certificate for this repository
|
||||
// Lets check for it and store it for later use
|
||||
allCerts := repo.CertStore.GetCertificates()
|
||||
require.Len(t, allCerts, 1)
|
||||
|
||||
fakeServerData(t, repo, mux, keys)
|
||||
|
||||
//
|
||||
// Test TOFUS logic. We remove all certs and expect a new one to be added after ListTargets
|
||||
//
|
||||
err = repo.CertStore.RemoveAll()
|
||||
require.NoError(t, err)
|
||||
require.Len(t, repo.CertStore.GetCertificates(), 0)
|
||||
|
||||
// This list targets is expected to succeed and the certificate store to have the new certificate
|
||||
_, err = repo.ListTargets(data.CanonicalTargetsRole)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, repo.CertStore.GetCertificates(), 1)
|
||||
|
||||
//
|
||||
// Test certificate mismatch logic. We remove all certs, add a different cert to the
|
||||
// same CN, and expect ValidateRoot to fail
|
||||
//
|
||||
|
||||
// First, remove all certs
|
||||
err = repo.CertStore.RemoveAll()
|
||||
require.NoError(t, err)
|
||||
require.Len(t, repo.CertStore.GetCertificates(), 0)
|
||||
|
||||
// Add a previously generated certificate with CN=docker.com/notary
|
||||
err = repo.CertStore.AddCertFromFile(
|
||||
"../fixtures/self-signed_docker.com-notary.crt")
|
||||
require.NoError(t, err)
|
||||
|
||||
// This list targets is expected to fail, since there already exists a certificate
|
||||
// in the store for the dnsName docker.com/notary, so TOFUS doesn't apply
|
||||
_, err = repo.ListTargets(data.CanonicalTargetsRole)
|
||||
require.Error(t, err, "An error was expected")
|
||||
require.Equal(t, err, &trustpinning.ErrValidationFail{
|
||||
Reason: "failed to validate data with current trusted certificates",
|
||||
})
|
||||
}
|
|
@ -425,26 +425,6 @@ func requireRepoHasExpectedKeys(t *testing.T, repo *NotaryRepository,
|
|||
"there should be no timestamp key because the server manages it")
|
||||
}
|
||||
|
||||
// This creates a new certificate store in the repo's base directory and
|
||||
// makes sure the repo has the right certificates
|
||||
func requireRepoHasExpectedCerts(t *testing.T, repo *NotaryRepository) {
|
||||
// The repo should have a certificate store and have created certs using
|
||||
// it, so create a new store, and check that the certs do exist and
|
||||
// are valid
|
||||
trustPath := filepath.Join(repo.baseDir, notary.TrustedCertsDir)
|
||||
certStore, err := trustmanager.NewX509FilteredFileStore(
|
||||
trustPath,
|
||||
trustmanager.FilterCertsExpiredSha1,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
certificates := certStore.GetCertificates()
|
||||
require.Len(t, certificates, 1, "unexpected number of trusted certificates")
|
||||
|
||||
certID, err := trustmanager.FingerprintCert(certificates[0])
|
||||
require.NoError(t, err, "unable to fingerprint the trusted certificate")
|
||||
require.NotEqual(t, certID, "")
|
||||
}
|
||||
|
||||
// Sanity check the TUF metadata files. Verify that it exists for a particular
|
||||
// role, the JSON is well-formed, and the signatures exist.
|
||||
// For the root.json file, also check that the root, snapshot, and
|
||||
|
@ -515,7 +495,6 @@ func testInitRepoMetadata(t *testing.T, rootType string, serverManagesSnapshot b
|
|||
defer os.RemoveAll(repo.baseDir)
|
||||
|
||||
requireRepoHasExpectedKeys(t, repo, rootKeyID, !serverManagesSnapshot)
|
||||
requireRepoHasExpectedCerts(t, repo)
|
||||
requireRepoHasExpectedMetadata(t, repo, data.CanonicalRootRole, true)
|
||||
requireRepoHasExpectedMetadata(t, repo, data.CanonicalTargetsRole, true)
|
||||
requireRepoHasExpectedMetadata(t, repo, data.CanonicalSnapshotRole,
|
||||
|
@ -1932,17 +1911,15 @@ func TestPublishRootCorrupt(t *testing.T) {
|
|||
defer os.RemoveAll(repo.baseDir)
|
||||
testPublishBadMetadata(t, data.CanonicalRootRole, repo, false, false)
|
||||
|
||||
// publish first - publish should still succeed if root corrupt since the
|
||||
// remote root is signed with the same key.
|
||||
// publish first - publish should still fail if the local root is corrupt since
|
||||
// we can't determine whether remote root is signed with the same key.
|
||||
repo, _ = initializeRepo(t, data.ECDSAKey, "docker.com/notary2", ts.URL, false)
|
||||
defer os.RemoveAll(repo.baseDir)
|
||||
testPublishBadMetadata(t, data.CanonicalRootRole, repo, true, true)
|
||||
testPublishBadMetadata(t, data.CanonicalRootRole, repo, true, false)
|
||||
}
|
||||
|
||||
// When publishing snapshot, root, or target, if the repo hasn't been published
|
||||
// before, if the metadata is corrupt, it can't be published. If it has been
|
||||
// published already, then the corrupt metadata can just be re-downloaded, so
|
||||
// publishing is successful.
|
||||
// before, if the metadata is corrupt, it can't be published.
|
||||
func testPublishBadMetadata(t *testing.T, roleName string, repo *NotaryRepository,
|
||||
publishFirst, succeeds bool) {
|
||||
|
||||
|
@ -1959,7 +1936,11 @@ func testPublishBadMetadata(t *testing.T, roleName string, repo *NotaryRepositor
|
|||
require.NoError(t, err)
|
||||
} else {
|
||||
require.Error(t, err)
|
||||
require.IsType(t, ®Json.SyntaxError{}, err)
|
||||
if roleName == data.CanonicalRootRole && publishFirst {
|
||||
require.IsType(t, &trustpinning.ErrValidationFail{}, err)
|
||||
} else {
|
||||
require.IsType(t, ®Json.SyntaxError{}, err)
|
||||
}
|
||||
}
|
||||
|
||||
// make an unreadable file by creating a directory instead of a file
|
||||
|
@ -1971,7 +1952,7 @@ func testPublishBadMetadata(t *testing.T, roleName string, repo *NotaryRepositor
|
|||
defer os.RemoveAll(path)
|
||||
|
||||
err = repo.Publish()
|
||||
if succeeds {
|
||||
if succeeds || publishFirst {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
require.Error(t, err)
|
||||
|
@ -2757,14 +2738,6 @@ func logRepoTrustRoot(t *testing.T, prefix string, repo *NotaryRepository) {
|
|||
for _, k := range root.Signed.Roles[data.CanonicalRootRole].KeyIDs {
|
||||
logrus.Debugf("\t%s", k)
|
||||
}
|
||||
logrus.Debugf("All trusted certs:")
|
||||
certs, err := repo.CertStore.GetCertificatesByCN(repo.gun)
|
||||
require.NoError(t, err)
|
||||
for _, cert := range certs {
|
||||
id, err := trustmanager.FingerprintCert(cert)
|
||||
require.NoError(t, err)
|
||||
logrus.Debugf("\t%s", id)
|
||||
}
|
||||
}
|
||||
|
||||
// ID of the (only) certificate trusted by the root role metadata
|
||||
|
@ -2774,15 +2747,6 @@ func rootRoleCertID(t *testing.T, repo *NotaryRepository) string {
|
|||
return rootKeys[0]
|
||||
}
|
||||
|
||||
func verifyOnlyTrustedCertificate(t *testing.T, repo *NotaryRepository, certID string) {
|
||||
certs, err := repo.CertStore.GetCertificatesByCN(repo.gun)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, certs, 1)
|
||||
id, err := trustmanager.FingerprintCert(certs[0])
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, certID, id)
|
||||
}
|
||||
|
||||
func TestRotateRootKey(t *testing.T) {
|
||||
ts := fullTestServer(t)
|
||||
defer ts.Close()
|
||||
|
@ -2825,27 +2789,12 @@ func TestRotateRootKey(t *testing.T) {
|
|||
require.Error(t, err)
|
||||
addTarget(t, authorRepo, "current", "../fixtures/intermediate-ca.crt")
|
||||
|
||||
// NotaryRepository.Update's handling of certificate rotation is weird:
|
||||
//
|
||||
// On every run, NotaryRepository.bootstrapClient rotates the trusted certificates
|
||||
// based on CACHED root data.
|
||||
// Then the client calls Repo.Update, which fetches a new timestmap,
|
||||
// notices an updated root.json, validates it and stores it into the cache.
|
||||
//
|
||||
// So, the locally trusted certificates are rotated only on the SECOND call of
|
||||
// NotaryRepository.Update after the rotation is pushed to the server.
|
||||
//
|
||||
// This would be nice to fix eventually (breaking down the NotaryRepository.Update
|
||||
// / Repo.Update separation which causes this), but for now, just ensure that the
|
||||
// second update does result in updated certificates.
|
||||
|
||||
// Publish the target, which does an update and pulls down the latest metadata, and
|
||||
// should update the cert store now
|
||||
// should update the trusted root
|
||||
logRepoTrustRoot(t, "pre-publish", authorRepo)
|
||||
err = authorRepo.Publish()
|
||||
require.NoError(t, err)
|
||||
logRepoTrustRoot(t, "post-publish", authorRepo)
|
||||
verifyOnlyTrustedCertificate(t, authorRepo, newRootCertID)
|
||||
|
||||
// Verify the user can use the rotated repo, and see the added target.
|
||||
_, err = userRepo.GetTargetByName("current")
|
||||
|
@ -2859,7 +2808,6 @@ func TestRotateRootKey(t *testing.T) {
|
|||
_, err = freshUserRepo.GetTargetByName("current")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, newRootCertID, rootRoleCertID(t, freshUserRepo))
|
||||
verifyOnlyTrustedCertificate(t, freshUserRepo, newRootCertID)
|
||||
logRepoTrustRoot(t, "fresh client", freshUserRepo)
|
||||
|
||||
// Verify that the user initialized with the original certificate eventually
|
||||
|
@ -3307,7 +3255,6 @@ func TestDeleteRepo(t *testing.T) {
|
|||
|
||||
// Assert initialization was successful before we delete
|
||||
requireRepoHasExpectedKeys(t, repo, rootKeyID, true)
|
||||
requireRepoHasExpectedCerts(t, repo)
|
||||
requireRepoHasExpectedMetadata(t, repo, data.CanonicalRootRole, true)
|
||||
requireRepoHasExpectedMetadata(t, repo, data.CanonicalTargetsRole, true)
|
||||
requireRepoHasExpectedMetadata(t, repo, data.CanonicalSnapshotRole, true)
|
||||
|
@ -3322,12 +3269,6 @@ func TestDeleteRepo(t *testing.T) {
|
|||
requireRepoHasExpectedMetadata(t, repo, data.CanonicalSnapshotRole, false)
|
||||
requireRepoHasExpectedMetadata(t, repo, data.CanonicalTimestampRole, false)
|
||||
|
||||
// Assert no certs for this repo exist locally
|
||||
_, err = repo.CertStore.GetCertificatesByCN(gun)
|
||||
require.Error(t, err)
|
||||
require.IsType(t, &trustmanager.ErrNoCertificatesFound{}, err)
|
||||
require.NotNil(t, err)
|
||||
|
||||
// Assert keys for this repo exist locally
|
||||
requireRepoHasExpectedKeys(t, repo, rootKeyID, true)
|
||||
}
|
||||
|
@ -3352,7 +3293,6 @@ func TestDeleteRepoBadFilestore(t *testing.T) {
|
|||
|
||||
// Assert initialization was successful before we delete
|
||||
requireRepoHasExpectedKeys(t, repo, rootKeyID, true)
|
||||
requireRepoHasExpectedCerts(t, repo)
|
||||
requireRepoHasExpectedMetadata(t, repo, data.CanonicalRootRole, true)
|
||||
requireRepoHasExpectedMetadata(t, repo, data.CanonicalTargetsRole, true)
|
||||
requireRepoHasExpectedMetadata(t, repo, data.CanonicalSnapshotRole, true)
|
||||
|
@ -3365,50 +3305,6 @@ func TestDeleteRepoBadFilestore(t *testing.T) {
|
|||
require.Error(t, err)
|
||||
}
|
||||
|
||||
// TestDeleteRepoNoCerts tests that local repo data is deleted successfully without an error even when we do not have certificates
|
||||
func TestDeleteRepoNoCerts(t *testing.T) {
|
||||
gun := "docker.com/notary"
|
||||
|
||||
ts, _, _ := simpleTestServer(t)
|
||||
defer ts.Close()
|
||||
|
||||
repo, rootKeyID := initializeRepo(t, data.ECDSAKey, gun, ts.URL, false)
|
||||
defer os.RemoveAll(repo.baseDir)
|
||||
|
||||
// Assert initialization was successful before we delete
|
||||
requireRepoHasExpectedKeys(t, repo, rootKeyID, true)
|
||||
requireRepoHasExpectedCerts(t, repo)
|
||||
requireRepoHasExpectedMetadata(t, repo, data.CanonicalRootRole, true)
|
||||
requireRepoHasExpectedMetadata(t, repo, data.CanonicalTargetsRole, true)
|
||||
requireRepoHasExpectedMetadata(t, repo, data.CanonicalSnapshotRole, true)
|
||||
|
||||
// Delete the certificate store contents and require it has been fully deleted
|
||||
repo.CertStore.RemoveAll()
|
||||
_, err := repo.CertStore.GetCertificatesByCN(gun)
|
||||
require.Error(t, err)
|
||||
require.IsType(t, &trustmanager.ErrNoCertificatesFound{}, err)
|
||||
require.NotNil(t, err)
|
||||
|
||||
// Delete all client trust data for repo
|
||||
err = repo.DeleteTrustData()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Assert no metadata for this repo exists locally
|
||||
requireRepoHasExpectedMetadata(t, repo, data.CanonicalRootRole, false)
|
||||
requireRepoHasExpectedMetadata(t, repo, data.CanonicalTargetsRole, false)
|
||||
requireRepoHasExpectedMetadata(t, repo, data.CanonicalSnapshotRole, false)
|
||||
requireRepoHasExpectedMetadata(t, repo, data.CanonicalTimestampRole, false)
|
||||
|
||||
// Assert no certs for this repo exist locally
|
||||
_, err = repo.CertStore.GetCertificatesByCN(gun)
|
||||
require.Error(t, err)
|
||||
require.IsType(t, &trustmanager.ErrNoCertificatesFound{}, err)
|
||||
require.NotNil(t, err)
|
||||
|
||||
// Assert keys for this repo exist locally
|
||||
requireRepoHasExpectedKeys(t, repo, rootKeyID, true)
|
||||
}
|
||||
|
||||
// Test that we get a correct list of roles with keys and signatures
|
||||
func TestListRoles(t *testing.T) {
|
||||
ts := fullTestServer(t)
|
||||
|
|
|
@ -208,8 +208,9 @@ var waysToMessUpLocalMetadata = []swizzleExpectations{
|
|||
// actively sabotage and break their own local repo (particularly the root.json)
|
||||
}
|
||||
|
||||
// If a repo has corrupt metadata (in that the hash doesn't match the snapshot) or
|
||||
// missing metadata, an update will replace all of it
|
||||
// If a repo has missing metadata, an update will replace all of it
|
||||
// If a repo has corrupt metadata for root, the update will fail
|
||||
// For other roles, corrupt metadata will be replaced
|
||||
func TestUpdateReplacesCorruptOrMissingMetadata(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping test in short mode")
|
||||
|
@ -230,18 +231,29 @@ func TestUpdateReplacesCorruptOrMissingMetadata(t *testing.T) {
|
|||
repoSwizzler := testutils.NewMetadataSwizzler("docker.com/notary", serverMeta, cs)
|
||||
repoSwizzler.MetadataCache = repo.fileStore
|
||||
|
||||
origMeta := testutils.CopyRepoMetadata(serverMeta)
|
||||
|
||||
for _, role := range repoSwizzler.Roles {
|
||||
for _, expt := range waysToMessUpLocalMetadata {
|
||||
text, messItUp := expt.desc, expt.swizzle
|
||||
for _, forWrite := range []bool{true, false} {
|
||||
require.NoError(t, messItUp(repoSwizzler, role), "could not fuzz %s (%s)", role, text)
|
||||
err := repo.Update(forWrite)
|
||||
require.NoError(t, err)
|
||||
for r, expected := range serverMeta {
|
||||
actual, err := repo.fileStore.GetMeta(r, -1)
|
||||
require.NoError(t, err, "problem getting repo metadata for %s", role)
|
||||
require.True(t, bytes.Equal(expected, actual),
|
||||
"%s for %s: expected to recover after update", text, role)
|
||||
// if this is a root role, we should error if it's corrupted data
|
||||
if role == data.CanonicalRootRole && expt.desc == "invalid JSON" {
|
||||
require.Error(t, err)
|
||||
// revert our original metadata
|
||||
for role := range origMeta {
|
||||
require.NoError(t, repo.fileStore.SetMeta(role, origMeta[role]))
|
||||
}
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
for r, expected := range serverMeta {
|
||||
actual, err := repo.fileStore.GetMeta(r, -1)
|
||||
require.NoError(t, err, "problem getting repo metadata for %s", role)
|
||||
require.True(t, bytes.Equal(expected, actual),
|
||||
"%s for %s: expected to recover after update", text, role)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -291,8 +303,12 @@ func TestUpdateFailsIfServerRootKeyChangedWithoutMultiSign(t *testing.T) {
|
|||
if _, ok := err.(store.ErrMetaNotFound); ok { // one of the ways to mess up is to delete metadata
|
||||
|
||||
err = repo.Update(forWrite)
|
||||
require.Error(t, err) // the new server has a different root key, update fails
|
||||
|
||||
// the new server has a different root key, but we don't have any way of pinning against a previous root
|
||||
require.NoError(t, err)
|
||||
// revert our original metadata
|
||||
for role := range origMeta {
|
||||
require.NoError(t, repo.fileStore.SetMeta(role, origMeta[role]))
|
||||
}
|
||||
} else {
|
||||
|
||||
require.NoError(t, err)
|
||||
|
@ -312,7 +328,7 @@ func TestUpdateFailsIfServerRootKeyChangedWithoutMultiSign(t *testing.T) {
|
|||
expected = messedUpMeta
|
||||
}
|
||||
require.True(t, bytes.Equal(expected, actual),
|
||||
"%s for %s: expected to not have updated", text, role)
|
||||
"%s for %s: expected to not have updated -- swizzle method %s", text, role, expt.desc)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -770,15 +786,15 @@ func testUpdateRemoteFileChecksumWrong(t *testing.T, opts updateOpts, errExpecte
|
|||
// this does not include delete, which is tested separately so we can try to get
|
||||
// 404s and 503s
|
||||
var waysToMessUpServer = []swizzleExpectations{
|
||||
{desc: "invalid JSON", expectErrs: []interface{}{&json.SyntaxError{}},
|
||||
{desc: "invalid JSON", expectErrs: []interface{}{&trustpinning.ErrValidationFail{}, &json.SyntaxError{}},
|
||||
swizzle: (*testutils.MetadataSwizzler).SetInvalidJSON},
|
||||
|
||||
{desc: "an invalid Signed", expectErrs: []interface{}{&json.UnmarshalTypeError{}},
|
||||
{desc: "an invalid Signed", expectErrs: []interface{}{&trustpinning.ErrValidationFail{}, &json.UnmarshalTypeError{}},
|
||||
swizzle: (*testutils.MetadataSwizzler).SetInvalidSigned},
|
||||
|
||||
{desc: "an invalid SignedMeta",
|
||||
// it depends which field gets unmarshalled first
|
||||
expectErrs: []interface{}{&json.UnmarshalTypeError{}, &time.ParseError{}},
|
||||
expectErrs: []interface{}{&trustpinning.ErrValidationFail{}, &json.UnmarshalTypeError{}, &time.ParseError{}},
|
||||
swizzle: (*testutils.MetadataSwizzler).SetInvalidSignedMeta},
|
||||
|
||||
// for the errors below, when we bootstrap root, we get cert.ErrValidationFail failures
|
||||
|
@ -789,11 +805,11 @@ var waysToMessUpServer = []swizzleExpectations{
|
|||
swizzle: (*testutils.MetadataSwizzler).SetInvalidMetadataType},
|
||||
|
||||
{desc: "invalid signatures", expectErrs: []interface{}{
|
||||
&trustpinning.ErrValidationFail{}, signed.ErrRoleThreshold{}},
|
||||
&trustpinning.ErrValidationFail{}, signed.ErrRoleThreshold{}, &trustpinning.ErrRootRotationFail{}},
|
||||
swizzle: (*testutils.MetadataSwizzler).InvalidateMetadataSignatures},
|
||||
|
||||
{desc: "meta signed by wrong key", expectErrs: []interface{}{
|
||||
&trustpinning.ErrValidationFail{}, signed.ErrRoleThreshold{}},
|
||||
&trustpinning.ErrValidationFail{}, signed.ErrRoleThreshold{}, &trustpinning.ErrRootRotationFail{}},
|
||||
swizzle: (*testutils.MetadataSwizzler).SignMetadataWithInvalidKey},
|
||||
|
||||
{desc: "expired metadata", expectErrs: []interface{}{
|
||||
|
@ -1197,12 +1213,6 @@ func TestUpdateLocalAndRemoteRootCorrupt(t *testing.T) {
|
|||
}
|
||||
for _, localExpt := range waysToMessUpLocalMetadata {
|
||||
for _, serverExpt := range waysToMessUpServer {
|
||||
if localExpt.desc == "expired metadata" && serverExpt.desc == "lower metadata version" {
|
||||
// TODO: bug right now where if the local metadata is invalid, we just download a
|
||||
// new version - we verify the signatures and everything, but don't check the version
|
||||
// against the previous if we can
|
||||
continue
|
||||
}
|
||||
testUpdateLocalAndRemoteRootCorrupt(t, true, localExpt, serverExpt)
|
||||
testUpdateLocalAndRemoteRootCorrupt(t, false, localExpt, serverExpt)
|
||||
}
|
||||
|
@ -1364,7 +1374,7 @@ func TestValidateRootRotationWithOldRole(t *testing.T) {
|
|||
keyIDs := make([]string, len(threeKeys))
|
||||
for i := 0; i < len(threeKeys); i++ {
|
||||
threeKeys[i], err = testutils.CreateKey(
|
||||
serverSwizzler.CryptoService, "docker.com/notary", data.CanonicalRootRole)
|
||||
serverSwizzler.CryptoService, "docker.com/notary", data.CanonicalRootRole, data.ECDSAKey)
|
||||
require.NoError(t, err)
|
||||
keyIDs[i] = threeKeys[i].ID()
|
||||
signedRoot.Signed.Keys[keyIDs[i]] = threeKeys[i]
|
||||
|
@ -1382,7 +1392,7 @@ func TestValidateRootRotationWithOldRole(t *testing.T) {
|
|||
// --- threshold back to 1
|
||||
|
||||
replacementKey, err := testutils.CreateKey(
|
||||
serverSwizzler.CryptoService, "docker.com/notary", data.CanonicalRootRole)
|
||||
serverSwizzler.CryptoService, "docker.com/notary", data.CanonicalRootRole, data.ECDSAKey)
|
||||
require.NoError(t, err)
|
||||
signedRoot.Signed.Version++
|
||||
signedRoot.Signed.Keys[replacementKey.ID()] = replacementKey
|
||||
|
@ -1406,7 +1416,7 @@ func TestValidateRootRotationWithOldRole(t *testing.T) {
|
|||
// --- latest root role)
|
||||
signedRoot.Signed.Version++
|
||||
snapKey, err := testutils.CreateKey(
|
||||
serverSwizzler.CryptoService, "docker.com/notary", data.CanonicalSnapshotRole)
|
||||
serverSwizzler.CryptoService, "docker.com/notary", data.CanonicalSnapshotRole, data.ECDSAKey)
|
||||
require.NoError(t, err)
|
||||
signedRoot.Signed.Keys[snapKey.ID()] = snapKey
|
||||
signedRoot.Signed.Roles[data.CanonicalSnapshotRole].KeyIDs = []string{snapKey.ID()}
|
||||
|
|
|
@ -1,194 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/docker/notary"
|
||||
notaryclient "github.com/docker/notary/client"
|
||||
"github.com/docker/notary/passphrase"
|
||||
"github.com/docker/notary/trustmanager"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var cmdCertTemplate = usageTemplate{
|
||||
Use: "cert",
|
||||
Short: "Operates on certificates.",
|
||||
Long: `Operations on certificates.`,
|
||||
}
|
||||
|
||||
var cmdCertListTemplate = usageTemplate{
|
||||
Use: "list",
|
||||
Short: "Lists certificates.",
|
||||
Long: "Lists root certificates known to notary.",
|
||||
}
|
||||
|
||||
var cmdCertRemoveTemplate = usageTemplate{
|
||||
Use: "remove [ certID ]",
|
||||
Short: "Removes the certificate with the given cert ID.",
|
||||
Long: "Remove the certificate with the given cert ID from the local host.",
|
||||
}
|
||||
|
||||
type certCommander struct {
|
||||
// these need to be set
|
||||
configGetter func() (*viper.Viper, error)
|
||||
retriever passphrase.Retriever
|
||||
|
||||
// these are for command line parsing - no need to set
|
||||
certRemoveGUN string
|
||||
certRemoveYes bool
|
||||
}
|
||||
|
||||
func (c *certCommander) GetCommand() *cobra.Command {
|
||||
cmd := cmdCertTemplate.ToCommand(nil)
|
||||
cmd.AddCommand(cmdCertListTemplate.ToCommand(c.certList))
|
||||
|
||||
cmdCertRemove := cmdCertRemoveTemplate.ToCommand(c.certRemove)
|
||||
cmdCertRemove.Flags().StringVarP(
|
||||
&c.certRemoveGUN, "gun", "g", "", "Globally unique name to delete certificates for")
|
||||
cmdCertRemove.Flags().BoolVarP(
|
||||
&c.certRemoveYes, "yes", "y", false, "Answer yes to the removal question (no confirmation)")
|
||||
|
||||
cmd.AddCommand(cmdCertRemove)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// certRemove deletes a certificate given a cert ID or a gun
|
||||
// If given a gun, certRemove will also remove local TUF data
|
||||
func (c *certCommander) certRemove(cmd *cobra.Command, args []string) error {
|
||||
// If the user hasn't provided -g with a gun, or a cert ID, show usage
|
||||
// If the user provided -g and a cert ID, also show usage
|
||||
if (len(args) < 1 && c.certRemoveGUN == "") || (len(args) > 0 && c.certRemoveGUN != "") {
|
||||
cmd.Usage()
|
||||
return fmt.Errorf("Must specify the cert ID or the GUN of the certificates to remove")
|
||||
}
|
||||
config, err := c.configGetter()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
trustDir := config.GetString("trust_dir")
|
||||
certPath := filepath.Join(trustDir, notary.TrustedCertsDir)
|
||||
certStore, err := trustmanager.NewX509FilteredFileStore(
|
||||
certPath,
|
||||
trustmanager.FilterCertsExpiredSha1,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to create a new truststore with directory: %s", trustDir)
|
||||
}
|
||||
|
||||
var certsToRemove []*x509.Certificate
|
||||
var certFoundByID *x509.Certificate
|
||||
var removeTrustData bool
|
||||
|
||||
// If there is no GUN, we expect a cert ID
|
||||
if c.certRemoveGUN == "" {
|
||||
certID := args[0]
|
||||
// Attempt to find this certificate
|
||||
certFoundByID, err = certStore.GetCertificateByCertID(certID)
|
||||
if err != nil {
|
||||
// This is an invalid ID, the user might have forgotten a character
|
||||
if len(certID) != notary.Sha256HexSize {
|
||||
return fmt.Errorf("Unable to retrieve certificate with invalid certificate ID provided: %s", certID)
|
||||
}
|
||||
return fmt.Errorf("Unable to retrieve certificate with cert ID: %s", certID)
|
||||
}
|
||||
// the GUN is the CN from the certificate
|
||||
c.certRemoveGUN = certFoundByID.Subject.CommonName
|
||||
certsToRemove = []*x509.Certificate{certFoundByID}
|
||||
}
|
||||
|
||||
toRemove, err := certStore.GetCertificatesByCN(c.certRemoveGUN)
|
||||
// We could not find any certificates matching the user's query, so propagate the error
|
||||
if err != nil {
|
||||
return fmt.Errorf("%v", err)
|
||||
}
|
||||
|
||||
// If we specified a GUN or if the ID we specified is the only certificate with its CN, remove all GUN certs and trust data too
|
||||
if certFoundByID == nil || len(toRemove) == 1 {
|
||||
removeTrustData = true
|
||||
certsToRemove = toRemove
|
||||
}
|
||||
|
||||
// List all the certificates about to be removed
|
||||
cmd.Printf("The following certificates will be removed:\n\n")
|
||||
for _, cert := range certsToRemove {
|
||||
// This error can't occur because we're getting certs off of an
|
||||
// x509 store that indexes by ID.
|
||||
certID, _ := trustmanager.FingerprintCert(cert)
|
||||
cmd.Printf("%s - %s\n", cert.Subject.CommonName, certID)
|
||||
}
|
||||
// If we were given a GUN or the last ID for a GUN, inform the user that we'll also delete all TUF data
|
||||
if removeTrustData {
|
||||
cmd.Printf("\nAll local trust data will be removed for %s\n", c.certRemoveGUN)
|
||||
}
|
||||
cmd.Println("\nAre you sure you want to remove these certificates? (yes/no)")
|
||||
|
||||
// Ask for confirmation before removing certificates, unless -y is provided
|
||||
if !c.certRemoveYes {
|
||||
confirmed := askConfirm(os.Stdin)
|
||||
if !confirmed {
|
||||
return fmt.Errorf("Aborting action.")
|
||||
}
|
||||
}
|
||||
|
||||
if removeTrustData {
|
||||
// Remove all TUF data, so call RemoveTrustData on a NotaryRepository with the GUN
|
||||
// no online operations are performed so the transport argument is nil
|
||||
trustPin, err := getTrustPinning(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nRepo, err := notaryclient.NewNotaryRepository(
|
||||
trustDir, c.certRemoveGUN, getRemoteTrustServer(config), nil, c.retriever, trustPin)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not establish trust data for GUN %s", c.certRemoveGUN)
|
||||
}
|
||||
// DeleteTrustData will pick up all of the same certificates by GUN (CN) and remove them
|
||||
err = nRepo.DeleteTrustData()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to delete trust data for %s", c.certRemoveGUN)
|
||||
}
|
||||
} else {
|
||||
for _, cert := range certsToRemove {
|
||||
err = certStore.RemoveCert(cert)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to remove cert %s", cert)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *certCommander) certList(cmd *cobra.Command, args []string) error {
|
||||
if len(args) > 0 {
|
||||
cmd.Usage()
|
||||
return fmt.Errorf("")
|
||||
}
|
||||
config, err := c.configGetter()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
trustDir := config.GetString("trust_dir")
|
||||
certPath := filepath.Join(trustDir, notary.TrustedCertsDir)
|
||||
// Load all individual (non-CA) certificates that aren't expired and don't use SHA1
|
||||
certStore, err := trustmanager.NewX509FilteredFileStore(
|
||||
certPath,
|
||||
trustmanager.FilterCertsExpiredSha1,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to create a new truststore with directory: %s", trustDir)
|
||||
}
|
||||
|
||||
trustedCerts := certStore.GetCertificates()
|
||||
|
||||
cmd.Println("")
|
||||
prettyPrintCerts(trustedCerts, cmd.Out())
|
||||
cmd.Println("")
|
||||
return nil
|
||||
}
|
|
@ -1346,98 +1346,6 @@ func TestClientKeyImportExportAllRoles(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func assertNumCerts(t *testing.T, tempDir string, expectedNum int) []string {
|
||||
output, err := runCommand(t, tempDir, "cert", "list")
|
||||
require.NoError(t, err)
|
||||
lines := splitLines(strings.TrimSpace(output))
|
||||
|
||||
if expectedNum == 0 {
|
||||
require.Len(t, lines, 1)
|
||||
require.Equal(t, "No trusted root certificates present.", lines[0])
|
||||
return []string{}
|
||||
}
|
||||
|
||||
require.Len(t, lines, expectedNum+2)
|
||||
return lines[2:]
|
||||
}
|
||||
|
||||
// TestClientCertInteraction
|
||||
func TestClientCertInteraction(t *testing.T) {
|
||||
// -- setup --
|
||||
setUp(t)
|
||||
|
||||
tempDir := tempDirWithConfig(t, "{}")
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
server := setupServer()
|
||||
defer server.Close()
|
||||
|
||||
// -- tests --
|
||||
_, err := runCommand(t, tempDir, "-s", server.URL, "init", "gun1")
|
||||
require.NoError(t, err)
|
||||
_, err = runCommand(t, tempDir, "-s", server.URL, "init", "gun2")
|
||||
require.NoError(t, err)
|
||||
certs := assertNumCerts(t, tempDir, 2)
|
||||
// root is always on disk, because even if there's a yubikey a backup is created
|
||||
assertNumKeys(t, tempDir, 1, 4, true)
|
||||
|
||||
// remove certs for one gun
|
||||
_, err = runCommand(t, tempDir, "cert", "remove", "-g", "gun1", "-y")
|
||||
require.NoError(t, err)
|
||||
certs = assertNumCerts(t, tempDir, 1)
|
||||
// assert that when we remove cert by gun, we do not remove repo signing keys
|
||||
// (root is always on disk, because even if there's a yubikey a backup is created)
|
||||
assertNumKeys(t, tempDir, 1, 4, true)
|
||||
// assert that when we remove cert by gun, we also remove TUF metadata
|
||||
_, err = os.Stat(filepath.Join(tempDir, "tuf", "gun1"))
|
||||
require.Error(t, err)
|
||||
|
||||
// remove a single cert
|
||||
certID := strings.Fields(certs[0])[1]
|
||||
// passing an empty gun here because the string for the previous gun has
|
||||
// has already been stored (a drawback of running these commands without)
|
||||
// shelling out
|
||||
_, err = runCommand(t, tempDir, "cert", "remove", certID, "-y", "-g", "")
|
||||
require.NoError(t, err)
|
||||
assertNumCerts(t, tempDir, 0)
|
||||
// assert that when we remove the last cert ID for a gun, we also remove TUF metadata
|
||||
_, err = os.Stat(filepath.Join(tempDir, "tuf", "gun2"))
|
||||
require.Error(t, err)
|
||||
|
||||
// Setup certificate with nonexistent repo GUN
|
||||
// Check that we can only remove one certificate when specifying one ID
|
||||
startTime := time.Now()
|
||||
privKey, err := trustmanager.GenerateECDSAKey(rand.Reader)
|
||||
require.NoError(t, err)
|
||||
noGunCert, err := cryptoservice.GenerateCertificate(
|
||||
privKey, "nonexistent", startTime, startTime.AddDate(10, 0, 0))
|
||||
require.NoError(t, err)
|
||||
certStore, err := trustmanager.NewX509FileStore(filepath.Join(tempDir, "trusted_certificates"))
|
||||
require.NoError(t, err)
|
||||
err = certStore.AddCert(noGunCert)
|
||||
require.NoError(t, err)
|
||||
|
||||
certs = assertNumCerts(t, tempDir, 1)
|
||||
certID = strings.Fields(certs[0])[1]
|
||||
|
||||
privKey, err = trustmanager.GenerateECDSAKey(rand.Reader)
|
||||
require.NoError(t, err)
|
||||
noGunCert2, err := cryptoservice.GenerateCertificate(
|
||||
privKey, "nonexistent", startTime, startTime.AddDate(10, 0, 0))
|
||||
require.NoError(t, err)
|
||||
err = certStore.AddCert(noGunCert2)
|
||||
require.NoError(t, err)
|
||||
|
||||
certs = assertNumCerts(t, tempDir, 2)
|
||||
|
||||
// passing an empty gun to overwrite previously stored gun
|
||||
_, err = runCommand(t, tempDir, "cert", "remove", certID, "-y", "-g", "")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Since another cert with the same GUN exists, we didn't remove everything
|
||||
assertNumCerts(t, tempDir, 1)
|
||||
}
|
||||
|
||||
// Tests default root key generation
|
||||
func TestDefaultRootKeyGeneration(t *testing.T) {
|
||||
// -- setup --
|
||||
|
|
|
@ -180,11 +180,6 @@ func (n *notaryCommander) GetCommand() *cobra.Command {
|
|||
retriever: n.getRetriever(),
|
||||
}
|
||||
|
||||
cmdCertGenerator := &certCommander{
|
||||
configGetter: n.parseConfig,
|
||||
retriever: n.getRetriever(),
|
||||
}
|
||||
|
||||
cmdTufGenerator := &tufCommander{
|
||||
configGetter: n.parseConfig,
|
||||
retriever: n.getRetriever(),
|
||||
|
@ -192,7 +187,6 @@ func (n *notaryCommander) GetCommand() *cobra.Command {
|
|||
|
||||
notaryCmd.AddCommand(cmdKeyGenerator.GetCommand())
|
||||
notaryCmd.AddCommand(cmdDelegationGenerator.GetCommand())
|
||||
notaryCmd.AddCommand(cmdCertGenerator.GetCommand())
|
||||
|
||||
cmdTufGenerator.AddToCommand(¬aryCmd)
|
||||
|
||||
|
|
|
@ -164,8 +164,6 @@ var exampleValidCommands = []string{
|
|||
"key import backup.pem",
|
||||
"key remove e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"key passwd e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"cert list",
|
||||
"cert remove e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"delegation list repo",
|
||||
"delegation add repo targets/releases path/to/pem/file.pem",
|
||||
"delegation remove repo targets/releases",
|
||||
|
@ -205,8 +203,8 @@ func TestInsufficientArgumentsReturnsErrorAndPrintsUsage(t *testing.T) {
|
|||
cmd.SetOutput(b)
|
||||
|
||||
arglist := strings.Fields(args)
|
||||
if args == "key list" || args == "cert list" || args == "key generate rsa" {
|
||||
// in these case, "key" or "cert" or "key generate" are valid commands, so add an arg to them instead
|
||||
if args == "key list" || args == "key generate rsa" {
|
||||
// in these case, "key" or "key generate" are valid commands, so add an arg to them instead
|
||||
arglist = append(arglist, "extraArg")
|
||||
} else {
|
||||
arglist = arglist[:len(arglist)-1]
|
||||
|
@ -240,8 +238,8 @@ func TestBareCommandPrintsUsageAndNoError(t *testing.T) {
|
|||
// usage is printed
|
||||
require.Contains(t, b.String(), "Usage:", "expected usage when running `notary`")
|
||||
|
||||
// notary key, notary cert, and notary delegation
|
||||
for _, bareCommand := range []string{"key", "cert", "delegation"} {
|
||||
// notary key and notary delegation
|
||||
for _, bareCommand := range []string{"key", "delegation"} {
|
||||
b := new(bytes.Buffer)
|
||||
cmd := NewNotaryCommand()
|
||||
cmd.SetOutput(b)
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/notary/client"
|
||||
"github.com/docker/notary/trustmanager"
|
||||
|
@ -204,52 +201,3 @@ func prettyPrintPaths(paths []string) string {
|
|||
}
|
||||
return strings.Join(prettyPaths, "\n")
|
||||
}
|
||||
|
||||
// --- pretty printing certs ---
|
||||
|
||||
// cert by repo name then expiry time. Don't bother sorting by fingerprint.
|
||||
type certSorter []*x509.Certificate
|
||||
|
||||
func (t certSorter) Len() int { return len(t) }
|
||||
func (t certSorter) Swap(i, j int) { t[i], t[j] = t[j], t[i] }
|
||||
func (t certSorter) Less(i, j int) bool {
|
||||
if t[i].Subject.CommonName < t[j].Subject.CommonName {
|
||||
return true
|
||||
} else if t[i].Subject.CommonName > t[j].Subject.CommonName {
|
||||
return false
|
||||
}
|
||||
|
||||
return t[i].NotAfter.Before(t[j].NotAfter)
|
||||
}
|
||||
|
||||
// Given a list of Ceritifcates in order of listing preference, pretty-prints
|
||||
// the cert common name, fingerprint, and expiry
|
||||
func prettyPrintCerts(certs []*x509.Certificate, writer io.Writer) {
|
||||
if len(certs) == 0 {
|
||||
writer.Write([]byte("\nNo trusted root certificates present.\n\n"))
|
||||
return
|
||||
}
|
||||
|
||||
sort.Stable(certSorter(certs))
|
||||
|
||||
table := getTable([]string{
|
||||
"GUN", "Fingerprint of Trusted Root Certificate", "Expires In"}, writer)
|
||||
|
||||
for _, c := range certs {
|
||||
days := math.Floor(c.NotAfter.Sub(time.Now()).Hours() / 24)
|
||||
expiryString := "< 1 day"
|
||||
if days == 1 {
|
||||
expiryString = "1 day"
|
||||
} else if days > 1 {
|
||||
expiryString = fmt.Sprintf("%d days", int(days))
|
||||
}
|
||||
|
||||
certID, err := trustmanager.FingerprintCert(c)
|
||||
if err != nil {
|
||||
fatalf("Could not fingerprint certificate: %v", err)
|
||||
}
|
||||
|
||||
table.Append([]string{c.Subject.CommonName, certID, expiryString})
|
||||
}
|
||||
table.Render()
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ package main
|
|||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
@ -11,10 +10,8 @@ import (
|
|||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/notary/client"
|
||||
"github.com/docker/notary/cryptoservice"
|
||||
"github.com/docker/notary/passphrase"
|
||||
"github.com/docker/notary/trustmanager"
|
||||
"github.com/docker/notary/tuf/data"
|
||||
|
@ -201,19 +198,6 @@ func TestPrettyPrintSortedTargets(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// --- tests for pretty printing certs ---
|
||||
|
||||
func generateCertificate(t *testing.T, gun string, expireInHours int64) *x509.Certificate {
|
||||
ecdsaPrivKey, err := trustmanager.GenerateECDSAKey(rand.Reader)
|
||||
require.NoError(t, err)
|
||||
|
||||
startTime := time.Now()
|
||||
endTime := startTime.Add(time.Hour * time.Duration(expireInHours))
|
||||
cert, err := cryptoservice.GenerateCertificate(ecdsaPrivKey, gun, startTime, endTime)
|
||||
require.NoError(t, err)
|
||||
return cert
|
||||
}
|
||||
|
||||
// --- tests for pretty printing roles ---
|
||||
|
||||
// If there are no roles, no table is printed, only a line saying that there
|
||||
|
@ -268,53 +252,3 @@ func TestPrettyPrintSortedRoles(t *testing.T) {
|
|||
require.Equal(t, expected[i], splitted)
|
||||
}
|
||||
}
|
||||
|
||||
// If there are no certs in the cert store store, a message that there are no
|
||||
// certs should be displayed.
|
||||
func TestPrettyPrintZeroCerts(t *testing.T) {
|
||||
var b bytes.Buffer
|
||||
prettyPrintCerts([]*x509.Certificate{}, &b)
|
||||
text, err := ioutil.ReadAll(&b)
|
||||
require.NoError(t, err)
|
||||
|
||||
lines := strings.Split(strings.TrimSpace(string(text)), "\n")
|
||||
require.Len(t, lines, 1)
|
||||
require.Equal(t, "No trusted root certificates present.", lines[0])
|
||||
}
|
||||
|
||||
// Certificates are pretty-printed in table form sorted by gun and then expiry
|
||||
func TestPrettyPrintSortedCerts(t *testing.T) {
|
||||
unsorted := []*x509.Certificate{
|
||||
generateCertificate(t, "xylitol", 77), // 3 days 5 hours
|
||||
generateCertificate(t, "xylitol", 12), // less than 1 day
|
||||
generateCertificate(t, "cheesecake", 25), // a little more than 1 day
|
||||
generateCertificate(t, "baklava", 239), // almost 10 days
|
||||
}
|
||||
|
||||
var b bytes.Buffer
|
||||
prettyPrintCerts(unsorted, &b)
|
||||
text, err := ioutil.ReadAll(&b)
|
||||
require.NoError(t, err)
|
||||
|
||||
expected := [][]string{
|
||||
{"baklava", "9 days"},
|
||||
{"cheesecake", "1 day"},
|
||||
{"xylitol", "< 1 day"},
|
||||
{"xylitol", "3 days"},
|
||||
}
|
||||
|
||||
lines := strings.Split(strings.TrimSpace(string(text)), "\n")
|
||||
require.Len(t, lines, len(expected)+2)
|
||||
|
||||
// starts with headers
|
||||
require.True(t, reflect.DeepEqual(strings.Fields(lines[0]), strings.Fields(
|
||||
"GUN FINGERPRINT OF TRUSTED ROOT CERTIFICATE EXPIRES IN")))
|
||||
require.Equal(t, "----", lines[1][:4])
|
||||
|
||||
for i, line := range lines[2:] {
|
||||
splitted := strings.Fields(line)
|
||||
require.True(t, len(splitted) >= 3)
|
||||
require.Equal(t, expected[i][0], splitted[0])
|
||||
require.Equal(t, expected[i][1], strings.Join(splitted[2:], " "))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,272 +0,0 @@
|
|||
package trustmanager
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
// X509FileStore implements X509Store that persists on disk
|
||||
type X509FileStore struct {
|
||||
validate Validator
|
||||
fileMap map[CertID]string
|
||||
fingerprintMap map[CertID]*x509.Certificate
|
||||
nameMap map[string][]CertID
|
||||
fileStore Storage
|
||||
}
|
||||
|
||||
// NewX509FileStore returns a new X509FileStore.
|
||||
func NewX509FileStore(directory string) (*X509FileStore, error) {
|
||||
validate := ValidatorFunc(func(cert *x509.Certificate) bool { return true })
|
||||
return newX509FileStore(directory, validate)
|
||||
}
|
||||
|
||||
// NewX509FilteredFileStore returns a new X509FileStore that validates certificates
|
||||
// that are added.
|
||||
func NewX509FilteredFileStore(directory string, validate func(*x509.Certificate) bool) (*X509FileStore, error) {
|
||||
return newX509FileStore(directory, validate)
|
||||
}
|
||||
|
||||
func newX509FileStore(directory string, validate func(*x509.Certificate) bool) (*X509FileStore, error) {
|
||||
fileStore, err := NewSimpleFileStore(directory, certExtension)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s := &X509FileStore{
|
||||
validate: ValidatorFunc(validate),
|
||||
fileMap: make(map[CertID]string),
|
||||
fingerprintMap: make(map[CertID]*x509.Certificate),
|
||||
nameMap: make(map[string][]CertID),
|
||||
fileStore: fileStore,
|
||||
}
|
||||
|
||||
err = loadCertsFromDir(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// AddCert creates a filename for a given cert and adds a certificate with that name
|
||||
func (s *X509FileStore) AddCert(cert *x509.Certificate) error {
|
||||
if cert == nil {
|
||||
return errors.New("adding nil Certificate to X509Store")
|
||||
}
|
||||
|
||||
// Check if this certificate meets our validation criteria
|
||||
if !s.validate.Validate(cert) {
|
||||
return &ErrCertValidation{}
|
||||
}
|
||||
// Attempt to write the certificate to the file
|
||||
if err := s.addNamedCert(cert); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// addNamedCert allows adding a certificate while controlling the filename it gets
|
||||
// stored under. If the file does not exist on disk, saves it.
|
||||
func (s *X509FileStore) addNamedCert(cert *x509.Certificate) error {
|
||||
fileName, certID, err := fileName(cert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logrus.Debug("Adding cert with certID: ", certID)
|
||||
// Validate if we already added this certificate before
|
||||
if _, ok := s.fingerprintMap[certID]; ok {
|
||||
return &ErrCertExists{}
|
||||
}
|
||||
|
||||
// Convert certificate to PEM
|
||||
certBytes := CertToPEM(cert)
|
||||
|
||||
// Save the file to disk if not already there.
|
||||
if _, err = s.fileStore.Get(fileName); os.IsNotExist(err) {
|
||||
if err := s.fileStore.Add(fileName, certBytes); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// We wrote the certificate succcessfully, add it to our in-memory storage
|
||||
s.fingerprintMap[certID] = cert
|
||||
s.fileMap[certID] = fileName
|
||||
|
||||
name := string(cert.Subject.CommonName)
|
||||
s.nameMap[name] = append(s.nameMap[name], certID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveCert removes a certificate from a X509FileStore.
|
||||
func (s *X509FileStore) RemoveCert(cert *x509.Certificate) error {
|
||||
if cert == nil {
|
||||
return errors.New("removing nil Certificate from X509Store")
|
||||
}
|
||||
|
||||
certID, err := fingerprintCert(cert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
delete(s.fingerprintMap, certID)
|
||||
filename := s.fileMap[certID]
|
||||
delete(s.fileMap, certID)
|
||||
|
||||
name := string(cert.Subject.CommonName)
|
||||
|
||||
// Filter the fingerprint out of this name entry
|
||||
fpList := s.nameMap[name]
|
||||
newfpList := fpList[:0]
|
||||
for _, x := range fpList {
|
||||
if x != certID {
|
||||
newfpList = append(newfpList, x)
|
||||
}
|
||||
}
|
||||
|
||||
s.nameMap[name] = newfpList
|
||||
|
||||
if err := s.fileStore.Remove(filename); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveAll removes all the certificates from the store
|
||||
func (s *X509FileStore) RemoveAll() error {
|
||||
for _, filename := range s.fileMap {
|
||||
if err := s.fileStore.Remove(filename); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
s.fileMap = make(map[CertID]string)
|
||||
s.fingerprintMap = make(map[CertID]*x509.Certificate)
|
||||
s.nameMap = make(map[string][]CertID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddCertFromPEM adds the first certificate that it finds in the byte[], returning
|
||||
// an error if no Certificates are found
|
||||
func (s X509FileStore) AddCertFromPEM(pemBytes []byte) error {
|
||||
cert, err := LoadCertFromPEM(pemBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.AddCert(cert)
|
||||
}
|
||||
|
||||
// AddCertFromFile tries to adds a X509 certificate to the store given a filename
|
||||
func (s *X509FileStore) AddCertFromFile(filename string) error {
|
||||
cert, err := LoadCertFromFile(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.AddCert(cert)
|
||||
}
|
||||
|
||||
// GetCertificates returns an array with all of the current X509 Certificates.
|
||||
func (s *X509FileStore) GetCertificates() []*x509.Certificate {
|
||||
certs := make([]*x509.Certificate, len(s.fingerprintMap))
|
||||
i := 0
|
||||
for _, v := range s.fingerprintMap {
|
||||
certs[i] = v
|
||||
i++
|
||||
}
|
||||
return certs
|
||||
}
|
||||
|
||||
// GetCertificatePool returns an x509 CertPool loaded with all the certificates
|
||||
// in the store.
|
||||
func (s *X509FileStore) GetCertificatePool() *x509.CertPool {
|
||||
pool := x509.NewCertPool()
|
||||
|
||||
for _, v := range s.fingerprintMap {
|
||||
pool.AddCert(v)
|
||||
}
|
||||
return pool
|
||||
}
|
||||
|
||||
// GetCertificateByCertID returns the certificate that matches a certain certID
|
||||
func (s *X509FileStore) GetCertificateByCertID(certID string) (*x509.Certificate, error) {
|
||||
return s.getCertificateByCertID(CertID(certID))
|
||||
}
|
||||
|
||||
// getCertificateByCertID returns the certificate that matches a certain certID
|
||||
func (s *X509FileStore) getCertificateByCertID(certID CertID) (*x509.Certificate, error) {
|
||||
// If it does not look like a hex encoded sha256 hash, error
|
||||
if len(certID) != 64 {
|
||||
return nil, errors.New("invalid Subject Key Identifier")
|
||||
}
|
||||
|
||||
// Check to see if this subject key identifier exists
|
||||
if cert, ok := s.fingerprintMap[CertID(certID)]; ok {
|
||||
return cert, nil
|
||||
|
||||
}
|
||||
return nil, &ErrNoCertificatesFound{query: string(certID)}
|
||||
}
|
||||
|
||||
// GetCertificatesByCN returns all the certificates that match a specific
|
||||
// CommonName
|
||||
func (s *X509FileStore) GetCertificatesByCN(cn string) ([]*x509.Certificate, error) {
|
||||
var certs []*x509.Certificate
|
||||
if ids, ok := s.nameMap[cn]; ok {
|
||||
for _, v := range ids {
|
||||
cert, err := s.getCertificateByCertID(v)
|
||||
if err != nil {
|
||||
// This error should never happen. This would mean that we have
|
||||
// an inconsistent X509FileStore
|
||||
return nil, &ErrBadCertificateStore{}
|
||||
}
|
||||
certs = append(certs, cert)
|
||||
}
|
||||
}
|
||||
if len(certs) == 0 {
|
||||
return nil, &ErrNoCertificatesFound{query: cn}
|
||||
}
|
||||
|
||||
return certs, nil
|
||||
}
|
||||
|
||||
// GetVerifyOptions returns VerifyOptions with the certificates within the KeyStore
|
||||
// as part of the roots list. This never allows the use of system roots, returning
|
||||
// an error if there are no root CAs.
|
||||
func (s *X509FileStore) GetVerifyOptions(dnsName string) (x509.VerifyOptions, error) {
|
||||
// If we have no Certificates loaded return error (we don't want to revert to using
|
||||
// system CAs).
|
||||
if len(s.fingerprintMap) == 0 {
|
||||
return x509.VerifyOptions{}, errors.New("no root CAs available")
|
||||
}
|
||||
|
||||
opts := x509.VerifyOptions{
|
||||
DNSName: dnsName,
|
||||
Roots: s.GetCertificatePool(),
|
||||
}
|
||||
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
// Empty returns true if there are no certificates in the X509FileStore, false
|
||||
// otherwise.
|
||||
func (s *X509FileStore) Empty() bool {
|
||||
return len(s.fingerprintMap) == 0
|
||||
}
|
||||
|
||||
func fileName(cert *x509.Certificate) (string, CertID, error) {
|
||||
certID, err := fingerprintCert(cert)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
return path.Join(cert.Subject.CommonName, string(certID)), certID, nil
|
||||
}
|
|
@ -1,428 +0,0 @@
|
|||
package trustmanager
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNewX509FileStore(t *testing.T) {
|
||||
tempDir, err := ioutil.TempDir("", "cert-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tempDir)
|
||||
store, err := NewX509FileStore(tempDir)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create a new X509FileStore: %v", store)
|
||||
}
|
||||
}
|
||||
|
||||
// NewX509FileStore loads any existing certs from the directory, and does
|
||||
// not overwrite any of the.
|
||||
func TestNewX509FileStoreLoadsExistingCerts(t *testing.T) {
|
||||
tempDir, err := ioutil.TempDir("", "cert-test")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
certBytes, err := ioutil.ReadFile("../fixtures/root-ca.crt")
|
||||
require.NoError(t, err)
|
||||
out, err := os.Create(filepath.Join(tempDir, "root-ca.crt"))
|
||||
require.NoError(t, err)
|
||||
|
||||
// to distinguish it from the canonical format
|
||||
distinguishingBytes := []byte{'\n', '\n', '\n', '\n', '\n', '\n'}
|
||||
nBytes, err := out.Write(distinguishingBytes)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, distinguishingBytes, nBytes)
|
||||
|
||||
nBytes, err = out.Write(certBytes)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, certBytes, nBytes)
|
||||
|
||||
err = out.Close()
|
||||
require.NoError(t, err)
|
||||
|
||||
store, err := NewX509FileStore(tempDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedCert, err := LoadCertFromFile("../fixtures/root-ca.crt")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, []*x509.Certificate{expectedCert}, store.GetCertificates())
|
||||
|
||||
outBytes, err := ioutil.ReadFile(filepath.Join(tempDir, "root-ca.crt"))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, distinguishingBytes, outBytes[:6], "original file overwritten")
|
||||
require.Equal(t, certBytes, outBytes[6:], "original file overwritten")
|
||||
}
|
||||
|
||||
func TestAddCertX509FileStore(t *testing.T) {
|
||||
// Read certificate from file
|
||||
b, err := ioutil.ReadFile("../fixtures/root-ca.crt")
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't load fixture: %v", err)
|
||||
}
|
||||
// Decode PEM block
|
||||
var block *pem.Block
|
||||
block, _ = pem.Decode(b)
|
||||
|
||||
// Load X509 Certificate
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't parse certificate: %v", err)
|
||||
}
|
||||
tempDir, err := ioutil.TempDir("", "cert-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Create a Store and add the certificate to it
|
||||
store, _ := NewX509FileStore(tempDir)
|
||||
err = store.AddCert(cert)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load certificate: %v", err)
|
||||
}
|
||||
// Retrieve all the certificates
|
||||
certs := store.GetCertificates()
|
||||
// Check to see if certificate is present and total number of certs is correct
|
||||
numCerts := len(certs)
|
||||
if numCerts != 1 {
|
||||
t.Fatalf("unexpected number of certificates in store: %d", numCerts)
|
||||
}
|
||||
if certs[0] != cert {
|
||||
t.Fatalf("expected certificates to be the same")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddCertFromFileX509FileStore(t *testing.T) {
|
||||
tempDir, err := ioutil.TempDir("", "cert-test")
|
||||
require.NoError(t, err, "failed to create temporary directory")
|
||||
|
||||
store, err := NewX509FileStore(tempDir)
|
||||
require.NoError(t, err, "failed to load x509 filestore")
|
||||
|
||||
err = store.AddCertFromFile("../fixtures/root-ca.crt")
|
||||
require.NoError(t, err, "failed to add certificate from file")
|
||||
require.Len(t, store.GetCertificates(), 1)
|
||||
|
||||
// Now load the x509 filestore with the same path and expect the same result
|
||||
newStore, err := NewX509FileStore(tempDir)
|
||||
require.NoError(t, err, "failed to load x509 filestore")
|
||||
require.Len(t, newStore.GetCertificates(), 1)
|
||||
|
||||
// Test that adding the same certificate returns an error
|
||||
err = newStore.AddCert(newStore.GetCertificates()[0])
|
||||
require.Error(t, err, "expected error when adding certificate twice")
|
||||
require.Equal(t, err, &ErrCertExists{})
|
||||
}
|
||||
|
||||
// TestNewX509FileStoreEmpty verifies the behavior of the Empty function
|
||||
func TestNewX509FileStoreEmpty(t *testing.T) {
|
||||
tempDir, err := ioutil.TempDir("", "cert-test")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
store, err := NewX509FileStore(tempDir)
|
||||
require.NoError(t, err)
|
||||
require.True(t, store.Empty())
|
||||
|
||||
err = store.AddCertFromFile("../fixtures/root-ca.crt")
|
||||
require.NoError(t, err)
|
||||
require.False(t, store.Empty())
|
||||
}
|
||||
|
||||
func TestAddCertFromPEMX509FileStore(t *testing.T) {
|
||||
b, err := ioutil.ReadFile("../fixtures/root-ca.crt")
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't load fixture: %v", err)
|
||||
}
|
||||
|
||||
tempDir, err := ioutil.TempDir("", "cert-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
store, _ := NewX509FileStore(tempDir)
|
||||
err = store.AddCertFromPEM(b)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load certificate from PEM: %v", err)
|
||||
}
|
||||
numCerts := len(store.GetCertificates())
|
||||
if numCerts != 1 {
|
||||
t.Fatalf("unexpected number of certificates in store: %d", numCerts)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveCertX509FileStore(t *testing.T) {
|
||||
b, err := ioutil.ReadFile("../fixtures/root-ca.crt")
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't load fixture: %v", err)
|
||||
}
|
||||
var block *pem.Block
|
||||
block, _ = pem.Decode(b)
|
||||
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't parse certificate: %v", err)
|
||||
}
|
||||
|
||||
tempDir, err := ioutil.TempDir("", "cert-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
store, _ := NewX509FileStore(tempDir)
|
||||
err = store.AddCert(cert)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load certificate: %v", err)
|
||||
}
|
||||
|
||||
// Number of certificates should be 1 since we added the cert
|
||||
numCerts := len(store.GetCertificates())
|
||||
if numCerts != 1 {
|
||||
t.Fatalf("unexpected number of certificates in store: %d", numCerts)
|
||||
}
|
||||
|
||||
// Remove the cert from the store
|
||||
err = store.RemoveCert(cert)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to remove certificate: %v", err)
|
||||
}
|
||||
// Number of certificates should be 0 since we added and removed the cert
|
||||
numCerts = len(store.GetCertificates())
|
||||
if numCerts != 0 {
|
||||
t.Fatalf("unexpected number of certificates in store: %d", numCerts)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveAllX509FileStore(t *testing.T) {
|
||||
tempDir, err := ioutil.TempDir("", "cert-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Add three certificates to store
|
||||
store, _ := NewX509FileStore(tempDir)
|
||||
certFiles := [3]string{"../fixtures/root-ca.crt",
|
||||
"../fixtures/intermediate-ca.crt",
|
||||
"../fixtures/secure.example.com.crt"}
|
||||
for _, file := range certFiles {
|
||||
b, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't load fixture: %v", err)
|
||||
}
|
||||
var block *pem.Block
|
||||
block, _ = pem.Decode(b)
|
||||
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't parse certificate: %v", err)
|
||||
}
|
||||
err = store.AddCert(cert)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load certificate: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Number of certificates should be 3 since we added the cert
|
||||
numCerts := len(store.GetCertificates())
|
||||
if numCerts != 3 {
|
||||
t.Fatalf("unexpected number of certificates in store: %d", numCerts)
|
||||
}
|
||||
|
||||
// Remove the cert from the store
|
||||
err = store.RemoveAll()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to remove all certificates: %v", err)
|
||||
}
|
||||
// Number of certificates should be 0 since we added and removed the cert
|
||||
numCerts = len(store.GetCertificates())
|
||||
if numCerts != 0 {
|
||||
t.Fatalf("unexpected number of certificates in store: %d", numCerts)
|
||||
}
|
||||
}
|
||||
func TestInexistentGetCertificateByKeyIDX509FileStore(t *testing.T) {
|
||||
tempDir, err := ioutil.TempDir("", "cert-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
store, _ := NewX509FileStore(tempDir)
|
||||
err = store.AddCertFromFile("../fixtures/root-ca.crt")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load certificate from file: %v", err)
|
||||
}
|
||||
|
||||
_, err = store.GetCertificateByCertID("4d06afd30b8bed131d2a84c97d00b37f422021598bfae34285ce98e77b708b5a")
|
||||
if err == nil {
|
||||
t.Fatalf("no error returned for inexistent certificate")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCertificateByKeyIDX509FileStore(t *testing.T) {
|
||||
b, err := ioutil.ReadFile("../fixtures/root-ca.crt")
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't load fixture: %v", err)
|
||||
}
|
||||
var block *pem.Block
|
||||
block, _ = pem.Decode(b)
|
||||
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't parse certificate: %v", err)
|
||||
}
|
||||
|
||||
tempDir, err := ioutil.TempDir("", "cert-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
store, _ := NewX509FileStore(tempDir)
|
||||
err = store.AddCert(cert)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load certificate from PEM: %v", err)
|
||||
}
|
||||
|
||||
keyID, err := FingerprintCert(cert)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to fingerprint the certificate: %v", err)
|
||||
}
|
||||
|
||||
// Tries to retrieve cert by Subject Key IDs
|
||||
_, err = store.GetCertificateByCertID(keyID)
|
||||
if err != nil {
|
||||
t.Fatalf("expected certificate in store: %s", keyID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetVerifyOpsErrorsWithoutCertsX509FileStore(t *testing.T) {
|
||||
tempDir, err := ioutil.TempDir("", "cert-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Create empty Store
|
||||
store, _ := NewX509FileStore(tempDir)
|
||||
|
||||
// Try to get VerifyOptions without certs added
|
||||
_, err = store.GetVerifyOptions("example.com")
|
||||
if err == nil {
|
||||
t.Fatalf("expecting an error when getting empty VerifyOptions")
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyLeafCertFromIntermediateX509FileStore(t *testing.T) {
|
||||
tempDir, err := ioutil.TempDir("", "cert-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Create a store and add a root
|
||||
store, _ := NewX509FileStore(tempDir)
|
||||
err = store.AddCertFromFile("../fixtures/intermediate-ca.crt")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load certificate from file: %v", err)
|
||||
}
|
||||
|
||||
// Get the VerifyOptions from our Store
|
||||
opts, err := store.GetVerifyOptions("secure.example.com")
|
||||
|
||||
// Get leaf certificate
|
||||
b, err := ioutil.ReadFile("../fixtures/secure.example.com.crt")
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't load fixture: %v", err)
|
||||
}
|
||||
var block *pem.Block
|
||||
block, _ = pem.Decode(b)
|
||||
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't parse certificate: %v", err)
|
||||
}
|
||||
|
||||
// Try to find a valid chain for cert
|
||||
_, err = cert.Verify(opts)
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't find a valid chain for this certificate: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyIntermediateFromRootX509FileStore(t *testing.T) {
|
||||
tempDir, err := ioutil.TempDir("", "cert-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Create a store and add a root
|
||||
store, _ := NewX509FileStore(tempDir)
|
||||
err = store.AddCertFromFile("../fixtures/root-ca.crt")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load certificate from file: %v", err)
|
||||
}
|
||||
|
||||
// Get the VerifyOptions from our Store
|
||||
opts, err := store.GetVerifyOptions("Notary Testing CA")
|
||||
|
||||
// Get leaf certificate
|
||||
b, err := ioutil.ReadFile("../fixtures/intermediate-ca.crt")
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't load fixture: %v", err)
|
||||
}
|
||||
var block *pem.Block
|
||||
block, _ = pem.Decode(b)
|
||||
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't parse certificate: %v", err)
|
||||
}
|
||||
|
||||
// Try to find a valid chain for cert
|
||||
_, err = cert.Verify(opts)
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't find a valid chain for this certificate: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewX509FilteredFileStore(t *testing.T) {
|
||||
tempDir, err := ioutil.TempDir("", "cert-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
store, err := NewX509FilteredFileStore(tempDir, func(cert *x509.Certificate) bool {
|
||||
return cert.IsCA
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create new X509FilteredFileStore: %v", err)
|
||||
}
|
||||
// AddCert should succeed because this is a CA being added
|
||||
err = store.AddCertFromFile("../fixtures/root-ca.crt")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load certificate from file: %v", err)
|
||||
}
|
||||
numCerts := len(store.GetCertificates())
|
||||
if numCerts != 1 {
|
||||
t.Fatalf("unexpected number of certificates in store: %d", numCerts)
|
||||
}
|
||||
|
||||
// AddCert should fail because this is a leaf cert being added
|
||||
err = store.AddCertFromFile("../fixtures/secure.example.com.crt")
|
||||
if err == nil {
|
||||
t.Fatalf("was expecting non-CA certificate to be rejected")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCertificatePoolX509FileStore(t *testing.T) {
|
||||
tempDir, err := ioutil.TempDir("", "cert-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Create a store and add a root
|
||||
store, _ := NewX509FileStore(tempDir)
|
||||
err = store.AddCertFromFile("../fixtures/root-ca.crt")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load certificate from file: %v", err)
|
||||
}
|
||||
|
||||
pool := store.GetCertificatePool()
|
||||
numCerts := len(pool.Subjects())
|
||||
if numCerts != 1 {
|
||||
t.Fatalf("unexpected number of certificates in pool: %d", numCerts)
|
||||
}
|
||||
}
|
|
@ -1,203 +0,0 @@
|
|||
package trustmanager
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
// X509MemStore implements X509Store as an in-memory object with no persistence
|
||||
type X509MemStore struct {
|
||||
validate Validator
|
||||
fingerprintMap map[CertID]*x509.Certificate
|
||||
nameMap map[string][]CertID
|
||||
}
|
||||
|
||||
// NewX509MemStore returns a new X509MemStore.
|
||||
func NewX509MemStore() *X509MemStore {
|
||||
validate := ValidatorFunc(func(cert *x509.Certificate) bool { return true })
|
||||
|
||||
return &X509MemStore{
|
||||
validate: validate,
|
||||
fingerprintMap: make(map[CertID]*x509.Certificate),
|
||||
nameMap: make(map[string][]CertID),
|
||||
}
|
||||
}
|
||||
|
||||
// NewX509FilteredMemStore returns a new X509Memstore that validates certificates
|
||||
// that are added.
|
||||
func NewX509FilteredMemStore(validate func(*x509.Certificate) bool) *X509MemStore {
|
||||
s := &X509MemStore{
|
||||
|
||||
validate: ValidatorFunc(validate),
|
||||
fingerprintMap: make(map[CertID]*x509.Certificate),
|
||||
nameMap: make(map[string][]CertID),
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// AddCert adds a certificate to the store
|
||||
func (s *X509MemStore) AddCert(cert *x509.Certificate) error {
|
||||
if cert == nil {
|
||||
return errors.New("adding nil Certificate to X509Store")
|
||||
}
|
||||
|
||||
if !s.validate.Validate(cert) {
|
||||
return &ErrCertValidation{}
|
||||
}
|
||||
|
||||
certID, err := fingerprintCert(cert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logrus.Debug("Adding cert with certID: ", certID)
|
||||
|
||||
// In this store we overwrite the certificate if it already exists
|
||||
s.fingerprintMap[certID] = cert
|
||||
name := string(cert.RawSubject)
|
||||
s.nameMap[name] = append(s.nameMap[name], certID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveCert removes a certificate from a X509MemStore.
|
||||
func (s *X509MemStore) RemoveCert(cert *x509.Certificate) error {
|
||||
if cert == nil {
|
||||
return errors.New("removing nil Certificate to X509Store")
|
||||
}
|
||||
|
||||
certID, err := fingerprintCert(cert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
delete(s.fingerprintMap, certID)
|
||||
name := string(cert.RawSubject)
|
||||
|
||||
// Filter the fingerprint out of this name entry
|
||||
fpList := s.nameMap[name]
|
||||
newfpList := fpList[:0]
|
||||
for _, x := range fpList {
|
||||
if x != certID {
|
||||
newfpList = append(newfpList, x)
|
||||
}
|
||||
}
|
||||
|
||||
s.nameMap[name] = newfpList
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveAll removes all the certificates from the store
|
||||
func (s *X509MemStore) RemoveAll() error {
|
||||
|
||||
for _, cert := range s.fingerprintMap {
|
||||
if err := s.RemoveCert(cert); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddCertFromPEM adds a certificate to the store from a PEM blob
|
||||
func (s *X509MemStore) AddCertFromPEM(pemBytes []byte) error {
|
||||
cert, err := LoadCertFromPEM(pemBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.AddCert(cert)
|
||||
}
|
||||
|
||||
// AddCertFromFile tries to adds a X509 certificate to the store given a filename
|
||||
func (s *X509MemStore) AddCertFromFile(originFilname string) error {
|
||||
cert, err := LoadCertFromFile(originFilname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.AddCert(cert)
|
||||
}
|
||||
|
||||
// GetCertificates returns an array with all of the current X509 Certificates.
|
||||
func (s *X509MemStore) GetCertificates() []*x509.Certificate {
|
||||
certs := make([]*x509.Certificate, len(s.fingerprintMap))
|
||||
i := 0
|
||||
for _, v := range s.fingerprintMap {
|
||||
certs[i] = v
|
||||
i++
|
||||
}
|
||||
return certs
|
||||
}
|
||||
|
||||
// GetCertificatePool returns an x509 CertPool loaded with all the certificates
|
||||
// in the store.
|
||||
func (s *X509MemStore) GetCertificatePool() *x509.CertPool {
|
||||
pool := x509.NewCertPool()
|
||||
|
||||
for _, v := range s.fingerprintMap {
|
||||
pool.AddCert(v)
|
||||
}
|
||||
return pool
|
||||
}
|
||||
|
||||
// GetCertificateByCertID returns the certificate that matches a certain certID
|
||||
func (s *X509MemStore) GetCertificateByCertID(certID string) (*x509.Certificate, error) {
|
||||
return s.getCertificateByCertID(CertID(certID))
|
||||
}
|
||||
|
||||
// getCertificateByCertID returns the certificate that matches a certain certID or error
|
||||
func (s *X509MemStore) getCertificateByCertID(certID CertID) (*x509.Certificate, error) {
|
||||
// If it does not look like a hex encoded sha256 hash, error
|
||||
if len(certID) != 64 {
|
||||
return nil, errors.New("invalid Subject Key Identifier")
|
||||
}
|
||||
|
||||
// Check to see if this subject key identifier exists
|
||||
if cert, ok := s.fingerprintMap[CertID(certID)]; ok {
|
||||
return cert, nil
|
||||
|
||||
}
|
||||
return nil, &ErrNoCertificatesFound{query: string(certID)}
|
||||
}
|
||||
|
||||
// GetCertificatesByCN returns all the certificates that match a specific
|
||||
// CommonName
|
||||
func (s *X509MemStore) GetCertificatesByCN(cn string) ([]*x509.Certificate, error) {
|
||||
var certs []*x509.Certificate
|
||||
if ids, ok := s.nameMap[cn]; ok {
|
||||
for _, v := range ids {
|
||||
cert, err := s.getCertificateByCertID(v)
|
||||
if err != nil {
|
||||
// This error should never happen. This would mean that we have
|
||||
// an inconsistent X509MemStore
|
||||
return nil, err
|
||||
}
|
||||
certs = append(certs, cert)
|
||||
}
|
||||
}
|
||||
if len(certs) == 0 {
|
||||
return nil, &ErrNoCertificatesFound{query: cn}
|
||||
}
|
||||
|
||||
return certs, nil
|
||||
}
|
||||
|
||||
// GetVerifyOptions returns VerifyOptions with the certificates within the KeyStore
|
||||
// as part of the roots list. This never allows the use of system roots, returning
|
||||
// an error if there are no root CAs.
|
||||
func (s *X509MemStore) GetVerifyOptions(dnsName string) (x509.VerifyOptions, error) {
|
||||
// If we have no Certificates loaded return error (we don't want to revert to using
|
||||
// system CAs).
|
||||
if len(s.fingerprintMap) == 0 {
|
||||
return x509.VerifyOptions{}, errors.New("no root CAs available")
|
||||
}
|
||||
|
||||
opts := x509.VerifyOptions{
|
||||
DNSName: dnsName,
|
||||
Roots: s.GetCertificatePool(),
|
||||
}
|
||||
|
||||
return opts, nil
|
||||
}
|
|
@ -1,302 +0,0 @@
|
|||
package trustmanager
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAddCert(t *testing.T) {
|
||||
// Read certificate from file
|
||||
b, err := ioutil.ReadFile("../fixtures/root-ca.crt")
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't load fixture: %v", err)
|
||||
}
|
||||
// Decode PEM block
|
||||
var block *pem.Block
|
||||
block, _ = pem.Decode(b)
|
||||
|
||||
// Load X509 Certificate
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't parse certificate: %v", err)
|
||||
}
|
||||
// Create a Store and add the certificate to it
|
||||
store := NewX509MemStore()
|
||||
err = store.AddCert(cert)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load certificate: %v", err)
|
||||
}
|
||||
// Retrieve all the certificates
|
||||
certs := store.GetCertificates()
|
||||
// Check to see if certificate is present and total number of certs is correct
|
||||
numCerts := len(certs)
|
||||
if numCerts != 1 {
|
||||
t.Fatalf("unexpected number of certificates in store: %d", numCerts)
|
||||
}
|
||||
if certs[0] != cert {
|
||||
t.Fatalf("expected certificates to be the same")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddCertFromFile(t *testing.T) {
|
||||
store := NewX509MemStore()
|
||||
err := store.AddCertFromFile("../fixtures/root-ca.crt")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load certificate from file: %v", err)
|
||||
}
|
||||
numCerts := len(store.GetCertificates())
|
||||
if numCerts != 1 {
|
||||
t.Fatalf("unexpected number of certificates in store: %d", numCerts)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddCertFromPEM(t *testing.T) {
|
||||
b, err := ioutil.ReadFile("../fixtures/root-ca.crt")
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't load fixture: %v", err)
|
||||
}
|
||||
|
||||
store := NewX509MemStore()
|
||||
err = store.AddCertFromPEM(b)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load certificate from PEM: %v", err)
|
||||
}
|
||||
numCerts := len(store.GetCertificates())
|
||||
if numCerts != 1 {
|
||||
t.Fatalf("unexpected number of certificates in store: %d", numCerts)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveCert(t *testing.T) {
|
||||
b, err := ioutil.ReadFile("../fixtures/root-ca.crt")
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't load fixture: %v", err)
|
||||
}
|
||||
var block *pem.Block
|
||||
block, _ = pem.Decode(b)
|
||||
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't parse certificate: %v", err)
|
||||
}
|
||||
|
||||
store := NewX509MemStore()
|
||||
err = store.AddCert(cert)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load certificate: %v", err)
|
||||
}
|
||||
|
||||
// Number of certificates should be 1 since we added the cert
|
||||
numCerts := len(store.GetCertificates())
|
||||
if numCerts != 1 {
|
||||
t.Fatalf("unexpected number of certificates in store: %d", numCerts)
|
||||
}
|
||||
|
||||
// Remove the cert from the store
|
||||
err = store.RemoveCert(cert)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to remove certificate: %v", err)
|
||||
}
|
||||
// Number of certificates should be 0 since we added and removed the cert
|
||||
numCerts = len(store.GetCertificates())
|
||||
if numCerts != 0 {
|
||||
t.Fatalf("unexpected number of certificates in store: %d", numCerts)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveAllX509MemStore(t *testing.T) {
|
||||
// Add three certificates to store
|
||||
store := NewX509MemStore()
|
||||
certFiles := [3]string{"../fixtures/root-ca.crt",
|
||||
"../fixtures/intermediate-ca.crt",
|
||||
"../fixtures/secure.example.com.crt"}
|
||||
for _, file := range certFiles {
|
||||
b, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't load fixture: %v", err)
|
||||
}
|
||||
var block *pem.Block
|
||||
block, _ = pem.Decode(b)
|
||||
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't parse certificate: %v", err)
|
||||
}
|
||||
err = store.AddCert(cert)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load certificate: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Number of certificates should be 3 since we added the cert
|
||||
numCerts := len(store.GetCertificates())
|
||||
if numCerts != 3 {
|
||||
t.Fatalf("unexpected number of certificates in store: %d", numCerts)
|
||||
}
|
||||
|
||||
// Remove the cert from the store
|
||||
err := store.RemoveAll()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to remove all certificates: %v", err)
|
||||
}
|
||||
// Number of certificates should be 0 since we added and removed the cert
|
||||
numCerts = len(store.GetCertificates())
|
||||
if numCerts != 0 {
|
||||
t.Fatalf("unexpected number of certificates in store: %d", numCerts)
|
||||
}
|
||||
}
|
||||
func TestInexistentGetCertificateByCertID(t *testing.T) {
|
||||
store := NewX509MemStore()
|
||||
err := store.AddCertFromFile("../fixtures/root-ca.crt")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load certificate from file: %v", err)
|
||||
}
|
||||
|
||||
_, err = store.GetCertificateByCertID("4d06afd30b8bed131d2a84c97d00b37f422021598bfae34285ce98e77b708b5a")
|
||||
if err == nil {
|
||||
t.Fatalf("no error returned for inexistent certificate")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCertificateByKeyID(t *testing.T) {
|
||||
b, err := ioutil.ReadFile("../fixtures/root-ca.crt")
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't load fixture: %v", err)
|
||||
}
|
||||
var block *pem.Block
|
||||
block, _ = pem.Decode(b)
|
||||
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't parse certificate: %v", err)
|
||||
}
|
||||
|
||||
store := NewX509MemStore()
|
||||
err = store.AddCert(cert)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load certificate from PEM: %v", err)
|
||||
}
|
||||
|
||||
certID, err := FingerprintCert(cert)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to fingerprint the certificate: %v", err)
|
||||
}
|
||||
|
||||
// Tries to retrieve cert by Subject Key IDs
|
||||
_, err = store.GetCertificateByCertID(certID)
|
||||
if err != nil {
|
||||
t.Fatalf("expected certificate in store: %s", certID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetVerifyOpsErrorsWithoutCerts(t *testing.T) {
|
||||
// Create empty Store
|
||||
store := NewX509MemStore()
|
||||
|
||||
// Try to get VerifyOptions without certs added
|
||||
_, err := store.GetVerifyOptions("example.com")
|
||||
if err == nil {
|
||||
t.Fatalf("expecting an error when getting empty VerifyOptions")
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyLeafCertFromIntermediate(t *testing.T) {
|
||||
// Create a store and add a root
|
||||
store := NewX509MemStore()
|
||||
err := store.AddCertFromFile("../fixtures/intermediate-ca.crt")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load certificate from file: %v", err)
|
||||
}
|
||||
|
||||
// Get the VerifyOptions from our Store
|
||||
opts, err := store.GetVerifyOptions("secure.example.com")
|
||||
|
||||
// Get leaf certificate
|
||||
b, err := ioutil.ReadFile("../fixtures/secure.example.com.crt")
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't load fixture: %v", err)
|
||||
}
|
||||
var block *pem.Block
|
||||
block, _ = pem.Decode(b)
|
||||
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't parse certificate: %v", err)
|
||||
}
|
||||
|
||||
// Try to find a valid chain for cert
|
||||
_, err = cert.Verify(opts)
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't find a valid chain for this certificate: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyIntermediateFromRoot(t *testing.T) {
|
||||
// Create a store and add a root
|
||||
store := NewX509MemStore()
|
||||
err := store.AddCertFromFile("../fixtures/root-ca.crt")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load certificate from file: %v", err)
|
||||
}
|
||||
|
||||
// Get the VerifyOptions from our Store
|
||||
opts, err := store.GetVerifyOptions("Notary Testing CA")
|
||||
|
||||
// Get leaf certificate
|
||||
b, err := ioutil.ReadFile("../fixtures/intermediate-ca.crt")
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't load fixture: %v", err)
|
||||
}
|
||||
var block *pem.Block
|
||||
block, _ = pem.Decode(b)
|
||||
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't parse certificate: %v", err)
|
||||
}
|
||||
|
||||
// Try to find a valid chain for cert
|
||||
_, err = cert.Verify(opts)
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't find a valid chain for this certificate: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewX509FilteredMemStore(t *testing.T) {
|
||||
store := NewX509FilteredMemStore(func(cert *x509.Certificate) bool {
|
||||
return cert.IsCA
|
||||
})
|
||||
|
||||
// AddCert should succeed because this is a CA being added
|
||||
err := store.AddCertFromFile("../fixtures/root-ca.crt")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load certificate from file: %v", err)
|
||||
}
|
||||
numCerts := len(store.GetCertificates())
|
||||
if numCerts != 1 {
|
||||
t.Fatalf("unexpected number of certificates in store: %d", numCerts)
|
||||
}
|
||||
|
||||
// AddCert should fail because this is a leaf cert being added
|
||||
err = store.AddCertFromFile("../fixtures/secure.example.com.crt")
|
||||
if err == nil {
|
||||
t.Fatalf("was expecting non-CA certificate to be rejected")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCertificatePool(t *testing.T) {
|
||||
// Create a store and add a root
|
||||
store := NewX509MemStore()
|
||||
err := store.AddCertFromFile("../fixtures/root-ca.crt")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load certificate from file: %v", err)
|
||||
}
|
||||
|
||||
pool := store.GetCertificatePool()
|
||||
numCerts := len(pool.Subjects())
|
||||
if numCerts != 1 {
|
||||
t.Fatalf("unexpected number of certificates in pool: %d", numCerts)
|
||||
}
|
||||
}
|
|
@ -1,144 +0,0 @@
|
|||
package trustmanager
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const certExtension string = "crt"
|
||||
|
||||
// ErrNoCertificatesFound is returned when no certificates are found for a
|
||||
// GetCertificatesBy*
|
||||
type ErrNoCertificatesFound struct {
|
||||
query string
|
||||
}
|
||||
|
||||
// ErrNoCertificatesFound is returned when no certificates are found for a
|
||||
// GetCertificatesBy*
|
||||
func (err ErrNoCertificatesFound) Error() string {
|
||||
return fmt.Sprintf("error, no certificates found in the keystore match: %s", err.query)
|
||||
}
|
||||
|
||||
// ErrCertValidation is returned when a certificate doesn't pass the store specific
|
||||
// validations
|
||||
type ErrCertValidation struct {
|
||||
}
|
||||
|
||||
// ErrCertValidation is returned when a certificate doesn't pass the store specific
|
||||
// validations
|
||||
func (err ErrCertValidation) Error() string {
|
||||
return fmt.Sprintf("store-specific certificate validations failed")
|
||||
}
|
||||
|
||||
// ErrCertExists is returned when a Certificate already exists in the key store
|
||||
type ErrCertExists struct {
|
||||
}
|
||||
|
||||
// ErrCertExists is returned when a Certificate already exists in the key store
|
||||
func (err ErrCertExists) Error() string {
|
||||
return fmt.Sprintf("certificate already in the store")
|
||||
}
|
||||
|
||||
// ErrBadCertificateStore is returned when there is an internal inconsistency
|
||||
// in our x509 store
|
||||
type ErrBadCertificateStore struct {
|
||||
}
|
||||
|
||||
// ErrBadCertificateStore is returned when there is an internal inconsistency
|
||||
// in our x509 store
|
||||
func (err ErrBadCertificateStore) Error() string {
|
||||
return fmt.Sprintf("inconsistent certificate store")
|
||||
}
|
||||
|
||||
// X509Store is the interface for all X509Stores
|
||||
type X509Store interface {
|
||||
AddCert(cert *x509.Certificate) error
|
||||
AddCertFromPEM(pemCerts []byte) error
|
||||
AddCertFromFile(filename string) error
|
||||
RemoveCert(cert *x509.Certificate) error
|
||||
RemoveAll() error
|
||||
GetCertificateByCertID(certID string) (*x509.Certificate, error)
|
||||
GetCertificatesByCN(cn string) ([]*x509.Certificate, error)
|
||||
GetCertificates() []*x509.Certificate
|
||||
GetCertificatePool() *x509.CertPool
|
||||
GetVerifyOptions(dnsName string) (x509.VerifyOptions, error)
|
||||
}
|
||||
|
||||
// CertID represent the ID used to identify certificates
|
||||
type CertID string
|
||||
|
||||
// Validator is a convenience type to create validating function that filters
|
||||
// certificates that get added to the store
|
||||
type Validator interface {
|
||||
Validate(cert *x509.Certificate) bool
|
||||
}
|
||||
|
||||
// ValidatorFunc is a convenience type to create functions that implement
|
||||
// the Validator interface
|
||||
type ValidatorFunc func(cert *x509.Certificate) bool
|
||||
|
||||
// Validate implements the Validator interface to allow for any func() bool method
|
||||
// to be passed as a Validator
|
||||
func (vf ValidatorFunc) Validate(cert *x509.Certificate) bool {
|
||||
return vf(cert)
|
||||
}
|
||||
|
||||
// Verify operates on an X509Store and validates the existence of a chain of trust
|
||||
// between a leafCertificate and a CA present inside of the X509 Store.
|
||||
// It requires at least two certificates in certList, a leaf Certificate and an
|
||||
// intermediate CA certificate.
|
||||
func Verify(s X509Store, dnsName string, certList []*x509.Certificate) error {
|
||||
// If we have no Certificates loaded return error (we don't want to revert to using
|
||||
// system CAs).
|
||||
if len(s.GetCertificates()) == 0 {
|
||||
return errors.New("no root CAs available")
|
||||
}
|
||||
|
||||
// At a minimum we should be provided a leaf cert and an intermediate.
|
||||
if len(certList) < 2 {
|
||||
return errors.New("certificate and at least one intermediate needed")
|
||||
}
|
||||
|
||||
// Get the VerifyOptions from the keystore for a base dnsName
|
||||
opts, err := s.GetVerifyOptions(dnsName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create a Certificate Pool for our intermediate certificates
|
||||
intPool := x509.NewCertPool()
|
||||
var leafCert *x509.Certificate
|
||||
|
||||
// Iterate through all the certificates
|
||||
for _, c := range certList {
|
||||
// If the cert is a CA, we add it to the intermediates pool. If not, we call
|
||||
// it the leaf cert
|
||||
if c.IsCA {
|
||||
intPool.AddCert(c)
|
||||
continue
|
||||
}
|
||||
// Certificate is not a CA, it must be our leaf certificate.
|
||||
// If we already found one, bail with error
|
||||
if leafCert != nil {
|
||||
return errors.New("more than one leaf certificate found")
|
||||
}
|
||||
leafCert = c
|
||||
}
|
||||
|
||||
// We exited the loop with no leaf certificates
|
||||
if leafCert == nil {
|
||||
return errors.New("no leaf certificates found")
|
||||
}
|
||||
|
||||
// We have one leaf certificate and at least one intermediate. Lets add this
|
||||
// Cert Pool as the Intermediates list on our VerifyOptions
|
||||
opts.Intermediates = intPool
|
||||
|
||||
// Finally, let's call Verify on our leafCert with our fully configured options
|
||||
chains, err := leafCert.Verify(opts)
|
||||
if len(chains) == 0 || err != nil {
|
||||
return fmt.Errorf("certificate verification failed: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,138 +0,0 @@
|
|||
package trustmanager
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestVerifyLeafSuccessfully(t *testing.T) {
|
||||
// Get root certificate
|
||||
rootCA, err := LoadCertFromFile("../fixtures/root-ca.crt")
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't load fixture: %v", err)
|
||||
}
|
||||
|
||||
// Get intermediate certificate
|
||||
intermediateCA, err := LoadCertFromFile("../fixtures/intermediate-ca.crt")
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't load fixture: %v", err)
|
||||
}
|
||||
|
||||
// Get leaf certificate
|
||||
leafCert, err := LoadCertFromFile("../fixtures/secure.example.com.crt")
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't load fixture: %v", err)
|
||||
}
|
||||
|
||||
// Create a store and add the CA root
|
||||
store := NewX509MemStore()
|
||||
err = store.AddCert(rootCA)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load certificate from file: %v", err)
|
||||
}
|
||||
|
||||
// Get our certList with Leaf Cert and Intermediate
|
||||
certList := []*x509.Certificate{leafCert, intermediateCA}
|
||||
|
||||
// Try to find a valid chain for cert
|
||||
err = Verify(store, "secure.example.com", certList)
|
||||
if err != nil {
|
||||
t.Fatalf("expected to find a valid chain for this certificate: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyLeafSuccessfullyWithMultipleIntermediates(t *testing.T) {
|
||||
// Get root certificate
|
||||
rootCA, err := LoadCertFromFile("../fixtures/root-ca.crt")
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't load fixture: %v", err)
|
||||
}
|
||||
|
||||
// Get intermediate certificate
|
||||
intermediateCA, err := LoadCertFromFile("../fixtures/intermediate-ca.crt")
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't load fixture: %v", err)
|
||||
}
|
||||
|
||||
// Get leaf certificate
|
||||
leafCert, err := LoadCertFromFile("../fixtures/secure.example.com.crt")
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't load fixture: %v", err)
|
||||
}
|
||||
|
||||
// Create a store and add the CA root
|
||||
store := NewX509MemStore()
|
||||
err = store.AddCert(rootCA)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load certificate from file: %v", err)
|
||||
}
|
||||
|
||||
// Get our certList with Leaf Cert and Intermediate
|
||||
certList := []*x509.Certificate{leafCert, intermediateCA, intermediateCA, rootCA}
|
||||
|
||||
// Try to find a valid chain for cert
|
||||
err = Verify(store, "secure.example.com", certList)
|
||||
if err != nil {
|
||||
t.Fatalf("expected to find a valid chain for this certificate: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyLeafWithNoIntermediate(t *testing.T) {
|
||||
// Get root certificate
|
||||
rootCA, err := LoadCertFromFile("../fixtures/root-ca.crt")
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't load fixture: %v", err)
|
||||
}
|
||||
|
||||
// Get leaf certificate
|
||||
leafCert, err := LoadCertFromFile("../fixtures/secure.example.com.crt")
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't load fixture: %v", err)
|
||||
}
|
||||
|
||||
// Create a store and add the CA root
|
||||
store := NewX509MemStore()
|
||||
err = store.AddCert(rootCA)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load certificate from file: %v", err)
|
||||
}
|
||||
|
||||
// Get our certList with Leaf Cert and Intermediate
|
||||
certList := []*x509.Certificate{leafCert, leafCert}
|
||||
|
||||
// Try to find a valid chain for cert
|
||||
err = Verify(store, "secure.example.com", certList)
|
||||
if err == nil {
|
||||
t.Fatalf("expected error due to more than one leaf certificate")
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyLeafWithNoLeaf(t *testing.T) {
|
||||
// Get root certificate
|
||||
rootCA, err := LoadCertFromFile("../fixtures/root-ca.crt")
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't load fixture: %v", err)
|
||||
}
|
||||
|
||||
// Get intermediate certificate
|
||||
intermediateCA, err := LoadCertFromFile("../fixtures/intermediate-ca.crt")
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't load fixture: %v", err)
|
||||
}
|
||||
|
||||
// Create a store and add the CA root
|
||||
store := NewX509MemStore()
|
||||
err = store.AddCert(rootCA)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load certificate from file: %v", err)
|
||||
}
|
||||
|
||||
// Get our certList with Leaf Cert and Intermediate
|
||||
certList := []*x509.Certificate{intermediateCA, intermediateCA}
|
||||
|
||||
// Try to find a valid chain for cert
|
||||
err = Verify(store, "secure.example.com", certList)
|
||||
if err == nil {
|
||||
t.Fatalf("expected error due to no leaves provided")
|
||||
}
|
||||
}
|
|
@ -14,8 +14,6 @@ import (
|
|||
"io"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
|
@ -24,40 +22,6 @@ import (
|
|||
"github.com/docker/notary/tuf/data"
|
||||
)
|
||||
|
||||
// GetCertFromURL tries to get a X509 certificate given a HTTPS URL
|
||||
func GetCertFromURL(urlStr string) (*x509.Certificate, error) {
|
||||
url, err := url.Parse(urlStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Check if we are adding via HTTPS
|
||||
if url.Scheme != "https" {
|
||||
return nil, errors.New("only HTTPS URLs allowed")
|
||||
}
|
||||
|
||||
// Download the certificate and write to directory
|
||||
resp, err := http.Get(url.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Copy the content to certBytes
|
||||
defer resp.Body.Close()
|
||||
certBytes, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Try to extract the first valid PEM certificate from the bytes
|
||||
cert, err := LoadCertFromPEM(certBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
// CertToPEM is a utility function returns a PEM encoded x509 Certificate
|
||||
func CertToPEM(cert *x509.Certificate) []byte {
|
||||
pemCert := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw})
|
||||
|
@ -100,60 +64,6 @@ func LoadCertFromPEM(pemBytes []byte) (*x509.Certificate, error) {
|
|||
return nil, errors.New("no certificates found in PEM data")
|
||||
}
|
||||
|
||||
// FingerprintCert returns a TUF compliant fingerprint for a X509 Certificate
|
||||
func FingerprintCert(cert *x509.Certificate) (string, error) {
|
||||
certID, err := fingerprintCert(cert)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(certID), nil
|
||||
}
|
||||
|
||||
func fingerprintCert(cert *x509.Certificate) (CertID, error) {
|
||||
block := pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}
|
||||
pemdata := pem.EncodeToMemory(&block)
|
||||
|
||||
var tufKey data.PublicKey
|
||||
switch cert.PublicKeyAlgorithm {
|
||||
case x509.RSA:
|
||||
tufKey = data.NewRSAx509PublicKey(pemdata)
|
||||
case x509.ECDSA:
|
||||
tufKey = data.NewECDSAx509PublicKey(pemdata)
|
||||
default:
|
||||
return "", fmt.Errorf("got Unknown key type while fingerprinting certificate")
|
||||
}
|
||||
|
||||
return CertID(tufKey.ID()), nil
|
||||
}
|
||||
|
||||
// loadCertsFromDir receives a store AddCertFromFile for each certificate found
|
||||
func loadCertsFromDir(s *X509FileStore) error {
|
||||
for _, f := range s.fileStore.ListFiles() {
|
||||
// ListFiles returns relative paths
|
||||
data, err := s.fileStore.Get(f)
|
||||
if err != nil {
|
||||
// the filestore told us it had a file that it then couldn't serve.
|
||||
// this is a serious problem so error immediately
|
||||
return err
|
||||
}
|
||||
err = s.AddCertFromPEM(data)
|
||||
if err != nil {
|
||||
if _, ok := err.(*ErrCertValidation); ok {
|
||||
logrus.Debugf("ignoring certificate, did not pass validation: %s", f)
|
||||
continue
|
||||
}
|
||||
if _, ok := err.(*ErrCertExists); ok {
|
||||
logrus.Debugf("ignoring certificate, already exists in the store: %s", f)
|
||||
continue
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadCertFromFile loads the first certificate from the file provided. The
|
||||
// data is expected to be PEM Encoded and contain one of more certificates
|
||||
// with PEM type "CERTIFICATE"
|
||||
|
@ -533,37 +443,39 @@ func CertToKey(cert *x509.Certificate) data.PublicKey {
|
|||
|
||||
// CertsToKeys transforms each of the input certificate chains into its corresponding
|
||||
// PublicKey
|
||||
func CertsToKeys(leafCerts []*x509.Certificate, intCerts map[string][]*x509.Certificate) map[string]data.PublicKey {
|
||||
func CertsToKeys(leafCerts map[string]*x509.Certificate, intCerts map[string][]*x509.Certificate) map[string]data.PublicKey {
|
||||
keys := make(map[string]data.PublicKey)
|
||||
for _, leafCert := range leafCerts {
|
||||
certBundle := []*x509.Certificate{leafCert}
|
||||
certID, err := FingerprintCert(leafCert)
|
||||
if err != nil {
|
||||
continue
|
||||
for id, leafCert := range leafCerts {
|
||||
if key, err := CertBundleToKey(leafCert, intCerts[id]); err == nil {
|
||||
keys[key.ID()] = key
|
||||
}
|
||||
if intCertsForLeaves, ok := intCerts[certID]; ok {
|
||||
certBundle = append(certBundle, intCertsForLeaves...)
|
||||
}
|
||||
certChainPEM, err := CertChainToPEM(certBundle)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
var newKey data.PublicKey
|
||||
// Use the leaf cert's public key algorithm for typing
|
||||
switch leafCert.PublicKeyAlgorithm {
|
||||
case x509.RSA:
|
||||
newKey = data.NewRSAx509PublicKey(certChainPEM)
|
||||
case x509.ECDSA:
|
||||
newKey = data.NewECDSAx509PublicKey(certChainPEM)
|
||||
default:
|
||||
logrus.Debugf("Unknown key type parsed from certificate: %v", leafCert.PublicKeyAlgorithm)
|
||||
continue
|
||||
}
|
||||
keys[newKey.ID()] = newKey
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
// CertBundleToKey creates a TUF key from a leaf certs and a list of
|
||||
// intermediates
|
||||
func CertBundleToKey(leafCert *x509.Certificate, intCerts []*x509.Certificate) (data.PublicKey, error) {
|
||||
certBundle := []*x509.Certificate{leafCert}
|
||||
certBundle = append(certBundle, intCerts...)
|
||||
certChainPEM, err := CertChainToPEM(certBundle)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var newKey data.PublicKey
|
||||
// Use the leaf cert's public key algorithm for typing
|
||||
switch leafCert.PublicKeyAlgorithm {
|
||||
case x509.RSA:
|
||||
newKey = data.NewRSAx509PublicKey(certChainPEM)
|
||||
case x509.ECDSA:
|
||||
newKey = data.NewECDSAx509PublicKey(certChainPEM)
|
||||
default:
|
||||
logrus.Debugf("Unknown key type parsed from certificate: %v", leafCert.PublicKeyAlgorithm)
|
||||
return nil, x509.ErrUnsupportedAlgorithm
|
||||
}
|
||||
return newKey, nil
|
||||
}
|
||||
|
||||
// NewCertificate returns an X509 Certificate following a template, given a GUN and validity interval.
|
||||
func NewCertificate(gun string, startTime, endTime time.Time) (*x509.Certificate, error) {
|
||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||
|
@ -610,14 +522,3 @@ func X509PublicKeyID(certPubKey data.PublicKey) (string, error) {
|
|||
|
||||
return key.ID(), nil
|
||||
}
|
||||
|
||||
// FilterCertsExpiredSha1 can be used as the filter function to cert store
|
||||
// initializers to filter out all expired or SHA-1 certificate that we
|
||||
// shouldn't load.
|
||||
func FilterCertsExpiredSha1(cert *x509.Certificate) bool {
|
||||
return !cert.IsCA &&
|
||||
time.Now().Before(cert.NotAfter) &&
|
||||
cert.SignatureAlgorithm != x509.SHA1WithRSA &&
|
||||
cert.SignatureAlgorithm != x509.DSAWithSHA1 &&
|
||||
cert.SignatureAlgorithm != x509.ECDSAWithSHA1
|
||||
}
|
||||
|
|
|
@ -28,10 +28,19 @@ func TestCertsToKeys(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
|
||||
// Get our certList with Leaf Cert and Intermediate
|
||||
certList := []*x509.Certificate{leafCert, intermediateCA, rootCA}
|
||||
certMap := map[string]*x509.Certificate{
|
||||
"a": leafCert,
|
||||
"b": intermediateCA,
|
||||
"c": rootCA,
|
||||
}
|
||||
certList := []*x509.Certificate{
|
||||
leafCert,
|
||||
intermediateCA,
|
||||
rootCA,
|
||||
}
|
||||
|
||||
// Call CertsToKeys
|
||||
keys := CertsToKeys(certList, make(map[string][]*x509.Certificate))
|
||||
keys := CertsToKeys(certMap, make(map[string][]*x509.Certificate))
|
||||
require.NotNil(t, keys)
|
||||
require.Len(t, keys, 3)
|
||||
|
||||
|
@ -49,7 +58,7 @@ func TestCertsToKeys(t *testing.T) {
|
|||
emptyCert := x509.Certificate{}
|
||||
// Also try changing the pre-existing leaf cert into an invalid algorithm
|
||||
leafCert.PublicKeyAlgorithm = x509.DSA
|
||||
keys = CertsToKeys([]*x509.Certificate{&emptyCert, leafCert}, make(map[string][]*x509.Certificate))
|
||||
keys = CertsToKeys(map[string]*x509.Certificate{"d": &emptyCert, "e": leafCert}, make(map[string][]*x509.Certificate))
|
||||
require.Empty(t, keys)
|
||||
}
|
||||
|
||||
|
|
|
@ -37,13 +37,9 @@ func (err ErrRootRotationFail) Error() string {
|
|||
return fmt.Sprintf("could not rotate trust to a new trusted root: %s", err.Reason)
|
||||
}
|
||||
|
||||
func prettyFormatCertIDs(certs []*x509.Certificate) string {
|
||||
func prettyFormatCertIDs(certs map[string]*x509.Certificate) string {
|
||||
ids := make([]string, 0, len(certs))
|
||||
for _, cert := range certs {
|
||||
id, err := trustmanager.FingerprintCert(cert)
|
||||
if err != nil {
|
||||
id = fmt.Sprintf("[Error %s]", err)
|
||||
}
|
||||
for id := range certs {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
return strings.Join(ids, ", ")
|
||||
|
@ -53,8 +49,9 @@ func prettyFormatCertIDs(certs []*x509.Certificate) string {
|
|||
ValidateRoot receives a new root, validates its correctness and attempts to
|
||||
do root key rotation if needed.
|
||||
|
||||
First we list the current trusted certificates we have for a particular GUN. If
|
||||
that list is non-empty means that we've already seen this repository before, and
|
||||
First we check if we have any trusted certificates for a particular GUN in
|
||||
a previous root, if we have one. If the previous root is not nil and we find
|
||||
certificates for this GUN, we've already seen this repository before, and
|
||||
have a list of trusted certificates for it. In this case, we use this list of
|
||||
certificates to attempt to validate this root file.
|
||||
|
||||
|
@ -86,68 +83,66 @@ We shall call this: TOFUS.
|
|||
|
||||
Validation failure at any step will result in an ErrValidationFailed error.
|
||||
*/
|
||||
func ValidateRoot(certStore trustmanager.X509Store, root *data.Signed, gun string, trustPinning TrustPinConfig) error {
|
||||
func ValidateRoot(prevRoot *data.SignedRoot, root *data.Signed, gun string, trustPinning TrustPinConfig) (*data.SignedRoot, error) {
|
||||
logrus.Debugf("entered ValidateRoot with dns: %s", gun)
|
||||
signedRoot, err := data.RootFromSigned(root)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rootRole, err := signedRoot.BuildBaseRole(data.CanonicalRootRole)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Retrieve all the leaf and intermediate certificates in root for which the CN matches the GUN
|
||||
allLeafCerts, allIntCerts := parseAllCerts(signedRoot)
|
||||
certsFromRoot, err := validRootLeafCerts(allLeafCerts, gun)
|
||||
certsFromRoot, err := validRootLeafCerts(allLeafCerts, gun, true)
|
||||
if err != nil {
|
||||
logrus.Debugf("error retrieving valid leaf certificates for: %s, %v", gun, err)
|
||||
return &ErrValidationFail{Reason: "unable to retrieve valid leaf certificates"}
|
||||
return nil, &ErrValidationFail{Reason: "unable to retrieve valid leaf certificates"}
|
||||
}
|
||||
|
||||
// Retrieve all the trusted certificates that match this gun
|
||||
trustedCerts, err := certStore.GetCertificatesByCN(gun)
|
||||
if err != nil {
|
||||
// If the error that we get back is different than ErrNoCertificatesFound
|
||||
// we couldn't check if there are any certificates with this CN already
|
||||
// trusted. Let's take the conservative approach and return a failed validation
|
||||
if _, ok := err.(*trustmanager.ErrNoCertificatesFound); !ok {
|
||||
logrus.Debugf("error retrieving trusted certificates for: %s, %v", gun, err)
|
||||
return &ErrValidationFail{Reason: "unable to retrieve trusted certificates"}
|
||||
// If we have a previous root, let's try to use it to validate that this new root is valid.
|
||||
if prevRoot != nil {
|
||||
// Retrieve all the trusted certificates from our previous root
|
||||
// Note that we do not validate expiries here since our originally trusted root might have expired certs
|
||||
allTrustedLeafCerts, allTrustedIntCerts := parseAllCerts(prevRoot)
|
||||
trustedLeafCerts, err := validRootLeafCerts(allTrustedLeafCerts, gun, false)
|
||||
|
||||
// Use the certificates we found in the previous root for the GUN to verify its signatures
|
||||
// This could potentially be an empty set, in which case we will fail to verify
|
||||
logrus.Debugf("found %d valid root leaf certificates for %s: %s", len(trustedLeafCerts), gun,
|
||||
prettyFormatCertIDs(trustedLeafCerts))
|
||||
|
||||
// Extract the previous root's threshold for signature verification
|
||||
prevRootRoleData, ok := prevRoot.Signed.Roles[data.CanonicalRootRole]
|
||||
if !ok {
|
||||
return nil, &ErrValidationFail{Reason: "could not retrieve previous root role data"}
|
||||
}
|
||||
}
|
||||
// If we have certificates that match this specific GUN, let's make sure to
|
||||
// use them first to validate that this new root is valid.
|
||||
if len(trustedCerts) != 0 {
|
||||
logrus.Debugf("found %d valid root certificates for %s: %s", len(trustedCerts), gun,
|
||||
prettyFormatCertIDs(trustedCerts))
|
||||
|
||||
err = signed.VerifySignatures(
|
||||
root, data.BaseRole{Keys: trustmanager.CertsToKeys(trustedCerts, allIntCerts), Threshold: 1})
|
||||
root, data.BaseRole{Keys: trustmanager.CertsToKeys(trustedLeafCerts, allTrustedIntCerts), Threshold: prevRootRoleData.Threshold})
|
||||
if err != nil {
|
||||
logrus.Debugf("failed to verify TUF data for: %s, %v", gun, err)
|
||||
return &ErrValidationFail{Reason: "failed to validate data with current trusted certificates"}
|
||||
return nil, &ErrRootRotationFail{Reason: "failed to validate data with current trusted certificates"}
|
||||
}
|
||||
} else {
|
||||
logrus.Debugf("found no currently valid root certificates for %s, using trust_pinning config to bootstrap trust", gun)
|
||||
trustPinCheckFunc, err := NewTrustPinChecker(trustPinning, gun)
|
||||
if err != nil {
|
||||
return &ErrValidationFail{Reason: err.Error()}
|
||||
return nil, &ErrValidationFail{Reason: err.Error()}
|
||||
}
|
||||
|
||||
validPinnedCerts := []*x509.Certificate{}
|
||||
for _, cert := range certsFromRoot {
|
||||
certID, err := trustmanager.FingerprintCert(cert)
|
||||
if err != nil {
|
||||
validPinnedCerts := map[string]*x509.Certificate{}
|
||||
for id, cert := range certsFromRoot {
|
||||
if ok := trustPinCheckFunc(cert, allIntCerts[id]); !ok {
|
||||
continue
|
||||
}
|
||||
if ok := trustPinCheckFunc(cert, allIntCerts[certID]); !ok {
|
||||
continue
|
||||
}
|
||||
validPinnedCerts = append(validPinnedCerts, cert)
|
||||
validPinnedCerts[id] = cert
|
||||
}
|
||||
if len(validPinnedCerts) == 0 {
|
||||
return &ErrValidationFail{Reason: "unable to match any certificates to trust_pinning config"}
|
||||
return nil, &ErrValidationFail{Reason: "unable to match any certificates to trust_pinning config"}
|
||||
}
|
||||
certsFromRoot = validPinnedCerts
|
||||
}
|
||||
|
@ -159,64 +154,29 @@ func ValidateRoot(certStore trustmanager.X509Store, root *data.Signed, gun strin
|
|||
Keys: trustmanager.CertsToKeys(certsFromRoot, allIntCerts), Threshold: rootRole.Threshold})
|
||||
if err != nil {
|
||||
logrus.Debugf("failed to verify TUF data for: %s, %v", gun, err)
|
||||
return &ErrValidationFail{Reason: "failed to validate integrity of roots"}
|
||||
}
|
||||
|
||||
// Getting here means:
|
||||
// A) we had trusted certificates and both the old and new validated this root.
|
||||
// or
|
||||
// B) we had no trusted certificates but the new set of certificates has integrity (self-signed).
|
||||
logrus.Debugf("entering root certificate rotation for: %s", gun)
|
||||
|
||||
// Do root certificate rotation: we trust only the certs present in the new root
|
||||
// First we add all the new certificates (even if they already exist)
|
||||
for _, cert := range certsFromRoot {
|
||||
err := certStore.AddCert(cert)
|
||||
if err != nil {
|
||||
// If the error is already exists we don't fail the rotation
|
||||
if _, ok := err.(*trustmanager.ErrCertExists); ok {
|
||||
logrus.Debugf("ignoring certificate addition to: %s", gun)
|
||||
continue
|
||||
}
|
||||
logrus.Debugf("error adding new trusted certificate for: %s, %v", gun, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Now we delete old certificates that aren't present in the new root
|
||||
oldCertsToRemove, err := certsToRemove(trustedCerts, certsFromRoot)
|
||||
if err != nil {
|
||||
logrus.Debugf("inconsistency when removing old certificates: %v", err)
|
||||
return err
|
||||
}
|
||||
for certID, cert := range oldCertsToRemove {
|
||||
logrus.Debugf("removing certificate with certID: %s", certID)
|
||||
err = certStore.RemoveCert(cert)
|
||||
if err != nil {
|
||||
logrus.Debugf("failed to remove trusted certificate with keyID: %s, %v", certID, err)
|
||||
return &ErrRootRotationFail{Reason: "failed to rotate root keys"}
|
||||
}
|
||||
return nil, &ErrValidationFail{Reason: "failed to validate integrity of roots"}
|
||||
}
|
||||
|
||||
logrus.Debugf("Root validation succeeded for %s", gun)
|
||||
return nil
|
||||
return signedRoot, nil
|
||||
}
|
||||
|
||||
// validRootLeafCerts returns a list of non-expired, non-sha1 certificates
|
||||
// validRootLeafCerts returns a list of possibly (if checkExpiry is true) non-expired, non-sha1 certificates
|
||||
// found in root whose Common-Names match the provided GUN. Note that this
|
||||
// "validity" alone does not imply any measure of trust.
|
||||
func validRootLeafCerts(allLeafCerts map[string]*x509.Certificate, gun string) ([]*x509.Certificate, error) {
|
||||
var validLeafCerts []*x509.Certificate
|
||||
func validRootLeafCerts(allLeafCerts map[string]*x509.Certificate, gun string, checkExpiry bool) (map[string]*x509.Certificate, error) {
|
||||
validLeafCerts := make(map[string]*x509.Certificate)
|
||||
|
||||
// Go through every leaf certificate and check that the CN matches the gun
|
||||
for _, cert := range allLeafCerts {
|
||||
for id, cert := range allLeafCerts {
|
||||
// Validate that this leaf certificate has a CN that matches the exact gun
|
||||
if cert.Subject.CommonName != gun {
|
||||
logrus.Debugf("error leaf certificate CN: %s doesn't match the given GUN: %s",
|
||||
cert.Subject.CommonName, gun)
|
||||
continue
|
||||
}
|
||||
// Make sure the certificate is not expired
|
||||
if time.Now().After(cert.NotAfter) {
|
||||
// Make sure the certificate is not expired if checkExpiry is true
|
||||
if checkExpiry && time.Now().After(cert.NotAfter) {
|
||||
logrus.Debugf("error leaf certificate is expired")
|
||||
continue
|
||||
}
|
||||
|
@ -230,7 +190,7 @@ func validRootLeafCerts(allLeafCerts map[string]*x509.Certificate, gun string) (
|
|||
continue
|
||||
}
|
||||
|
||||
validLeafCerts = append(validLeafCerts, cert)
|
||||
validLeafCerts[id] = cert
|
||||
}
|
||||
|
||||
if len(validLeafCerts) < 1 {
|
||||
|
@ -246,11 +206,15 @@ func validRootLeafCerts(allLeafCerts map[string]*x509.Certificate, gun string) (
|
|||
// parseAllCerts returns two maps, one with all of the leafCertificates and one
|
||||
// with all the intermediate certificates found in signedRoot
|
||||
func parseAllCerts(signedRoot *data.SignedRoot) (map[string]*x509.Certificate, map[string][]*x509.Certificate) {
|
||||
if signedRoot == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
leafCerts := make(map[string]*x509.Certificate)
|
||||
intCerts := make(map[string][]*x509.Certificate)
|
||||
|
||||
// Before we loop through all root keys available, make sure any exist
|
||||
rootRoles, ok := signedRoot.Signed.Roles["root"]
|
||||
rootRoles, ok := signedRoot.Signed.Roles[data.CanonicalRootRole]
|
||||
if !ok {
|
||||
logrus.Debugf("tried to parse certificates from invalid root signed data")
|
||||
return nil, nil
|
||||
|
@ -290,59 +254,14 @@ func parseAllCerts(signedRoot *data.SignedRoot) (map[string]*x509.Certificate, m
|
|||
|
||||
// Get the ID of the leaf certificate
|
||||
leafCert := leafCertList[0]
|
||||
leafID, err := trustmanager.FingerprintCert(leafCert)
|
||||
if err != nil {
|
||||
logrus.Debugf("error while fingerprinting root certificate with keyID: %s, %v", keyID, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Store the leaf cert in the map
|
||||
leafCerts[leafID] = leafCert
|
||||
leafCerts[key.ID()] = leafCert
|
||||
|
||||
// Get all the remainder certificates marked as a CA to be used as intermediates
|
||||
intermediateCerts := trustmanager.GetIntermediateCerts(decodedCerts)
|
||||
intCerts[leafID] = intermediateCerts
|
||||
intCerts[key.ID()] = intermediateCerts
|
||||
}
|
||||
|
||||
return leafCerts, intCerts
|
||||
}
|
||||
|
||||
// certsToRemove returns all the certificates from oldCerts that aren't present
|
||||
// in newCerts. Note that newCerts should never be empty, else this function will error.
|
||||
// We expect newCerts to come from validateRootLeafCerts, which does not return empty sets.
|
||||
func certsToRemove(oldCerts, newCerts []*x509.Certificate) (map[string]*x509.Certificate, error) {
|
||||
certsToRemove := make(map[string]*x509.Certificate)
|
||||
|
||||
// Populate a map with all the IDs from newCert
|
||||
var newCertMap = make(map[string]struct{})
|
||||
for _, cert := range newCerts {
|
||||
certID, err := trustmanager.FingerprintCert(cert)
|
||||
if err != nil {
|
||||
logrus.Debugf("error while fingerprinting root certificate with keyID: %s, %v", certID, err)
|
||||
continue
|
||||
}
|
||||
newCertMap[certID] = struct{}{}
|
||||
}
|
||||
|
||||
// We don't want to "rotate" certificates to an empty set, nor keep old certificates if the
|
||||
// new root does not trust them. newCerts should come from validRootLeafCerts, which refuses
|
||||
// to return an empty set, and they should all be fingerprintable, so this should never happen
|
||||
// - fail just to be sure.
|
||||
if len(newCertMap) == 0 {
|
||||
return nil, &ErrRootRotationFail{Reason: "internal error, got no certificates to rotate to"}
|
||||
}
|
||||
|
||||
// Iterate over all the old certificates and check to see if we should remove them
|
||||
for _, cert := range oldCerts {
|
||||
certID, err := trustmanager.FingerprintCert(cert)
|
||||
if err != nil {
|
||||
logrus.Debugf("error while fingerprinting root certificate with certID: %s, %v", certID, err)
|
||||
continue
|
||||
}
|
||||
if _, ok := newCertMap[certID]; !ok {
|
||||
certsToRemove[certID] = cert
|
||||
}
|
||||
}
|
||||
|
||||
return certsToRemove, nil
|
||||
}
|
||||
|
|
|
@ -18,11 +18,11 @@ import (
|
|||
|
||||
"time"
|
||||
|
||||
"github.com/docker/notary"
|
||||
"github.com/docker/notary/cryptoservice"
|
||||
"github.com/docker/notary/trustmanager"
|
||||
"github.com/docker/notary/tuf/data"
|
||||
"github.com/docker/notary/tuf/signed"
|
||||
"github.com/docker/notary/tuf/testutils"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
@ -44,86 +44,6 @@ const rootPubKeyID = "49cf5c6404a35fa41d5a5aa2ce539dfee0d7a2176d0da488914a38603b
|
|||
|
||||
const targetsPubKeyID = "1fc4fdc38f66558658c5c59b67f1716bdc6a74ef138b023ae5931db69f51d670"
|
||||
|
||||
func TestCertsToRemove(t *testing.T) {
|
||||
// Get a few certificates to test with
|
||||
cert1, err := trustmanager.LoadCertFromFile("../fixtures/secure.example.com.crt")
|
||||
require.NoError(t, err)
|
||||
cert1KeyID, err := trustmanager.FingerprintCert(cert1)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Get intermediate certificate
|
||||
cert2, err := trustmanager.LoadCertFromFile("../fixtures/self-signed_secure.example.com.crt")
|
||||
require.NoError(t, err)
|
||||
cert2KeyID, err := trustmanager.FingerprintCert(cert2)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Get leaf certificate
|
||||
cert3, err := trustmanager.LoadCertFromFile("../fixtures/self-signed_docker.com-notary.crt")
|
||||
require.NoError(t, err)
|
||||
cert3KeyID, err := trustmanager.FingerprintCert(cert3)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Call CertsToRemove with only one old and one new
|
||||
oldCerts := []*x509.Certificate{cert1}
|
||||
newCerts := []*x509.Certificate{cert2}
|
||||
|
||||
certificates, err := certsToRemove(oldCerts, newCerts)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, certificates, 1)
|
||||
_, ok := certificates[cert1KeyID]
|
||||
require.True(t, ok)
|
||||
|
||||
// Call CertsToRemove with two old and one new
|
||||
oldCerts = []*x509.Certificate{cert1, cert2}
|
||||
newCerts = []*x509.Certificate{cert3}
|
||||
|
||||
certificates, err = certsToRemove(oldCerts, newCerts)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, certificates, 2)
|
||||
_, ok = certificates[cert1KeyID]
|
||||
require.True(t, ok)
|
||||
_, ok = certificates[cert2KeyID]
|
||||
require.True(t, ok)
|
||||
_, ok = certificates[cert3KeyID]
|
||||
require.False(t, ok)
|
||||
|
||||
// Call CertsToRemove with two new and one old
|
||||
oldCerts = []*x509.Certificate{cert3}
|
||||
newCerts = []*x509.Certificate{cert2, cert1}
|
||||
|
||||
certificates, err = certsToRemove(oldCerts, newCerts)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, certificates, 1)
|
||||
_, ok = certificates[cert3KeyID]
|
||||
require.True(t, ok)
|
||||
_, ok = certificates[cert1KeyID]
|
||||
require.False(t, ok)
|
||||
_, ok = certificates[cert2KeyID]
|
||||
require.False(t, ok)
|
||||
|
||||
// Call CertsToRemove with three old certificates and no new
|
||||
oldCerts = []*x509.Certificate{cert1, cert2, cert3}
|
||||
newCerts = []*x509.Certificate{}
|
||||
|
||||
certificates, err = certsToRemove(oldCerts, newCerts)
|
||||
require.Error(t, err)
|
||||
|
||||
// Call CertsToRemove with three new certificates and no old
|
||||
oldCerts = []*x509.Certificate{}
|
||||
newCerts = []*x509.Certificate{cert1, cert2, cert3}
|
||||
|
||||
certificates, err = certsToRemove(oldCerts, newCerts)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, certificates, 0)
|
||||
_, ok = certificates[cert1KeyID]
|
||||
require.False(t, ok)
|
||||
_, ok = certificates[cert2KeyID]
|
||||
require.False(t, ok)
|
||||
_, ok = certificates[cert3KeyID]
|
||||
require.False(t, ok)
|
||||
|
||||
}
|
||||
|
||||
func TestValidateRoot(t *testing.T) {
|
||||
var testSignedRoot data.Signed
|
||||
var signedRootBytes bytes.Buffer
|
||||
|
@ -133,14 +53,6 @@ func TestValidateRoot(t *testing.T) {
|
|||
defer os.RemoveAll(tempBaseDir)
|
||||
require.NoError(t, err, "failed to create a temporary directory: %s", err)
|
||||
|
||||
// Create a X509Store
|
||||
trustPath := filepath.Join(tempBaseDir, notary.TrustedCertsDir)
|
||||
certStore, err := trustmanager.NewX509FilteredFileStore(
|
||||
trustPath,
|
||||
trustmanager.FilterCertsExpiredSha1,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Execute our template
|
||||
templ, _ := template.New("SignedRSARootTemplate").Parse(signedRSARootTemplate)
|
||||
templ.Execute(&signedRootBytes, SignedRSARootTemplate{RootPem: validPEMEncodedRSARoot})
|
||||
|
@ -150,12 +62,12 @@ func TestValidateRoot(t *testing.T) {
|
|||
|
||||
// This call to ValidateRoot will succeed since we are using a valid PEM
|
||||
// encoded certificate, and have no other certificates for this CN
|
||||
err = ValidateRoot(certStore, &testSignedRoot, "docker.com/notary", TrustPinConfig{})
|
||||
_, err = ValidateRoot(nil, &testSignedRoot, "docker.com/notary", TrustPinConfig{})
|
||||
require.NoError(t, err)
|
||||
|
||||
// This call to ValidateRoot will fail since we are passing in a dnsName that
|
||||
// doesn't match the CN of the certificate.
|
||||
err = ValidateRoot(certStore, &testSignedRoot, "diogomonica.com/notary", TrustPinConfig{})
|
||||
_, err = ValidateRoot(nil, &testSignedRoot, "diogomonica.com/notary", TrustPinConfig{})
|
||||
require.Error(t, err, "An error was expected")
|
||||
require.Equal(t, err, &ErrValidationFail{Reason: "unable to retrieve valid leaf certificates"})
|
||||
|
||||
|
@ -169,7 +81,7 @@ func TestValidateRoot(t *testing.T) {
|
|||
// Unmarshal our signedroot
|
||||
json.Unmarshal(signedRootBytes.Bytes(), &testSignedRoot)
|
||||
|
||||
err = ValidateRoot(certStore, &testSignedRoot, "docker.com/notary", TrustPinConfig{})
|
||||
_, err = ValidateRoot(nil, &testSignedRoot, "docker.com/notary", TrustPinConfig{})
|
||||
require.Error(t, err, "illegal base64 data at input byte")
|
||||
|
||||
//
|
||||
|
@ -182,7 +94,7 @@ func TestValidateRoot(t *testing.T) {
|
|||
// Unmarshal our signedroot
|
||||
json.Unmarshal(signedRootBytes.Bytes(), &testSignedRoot)
|
||||
|
||||
err = ValidateRoot(certStore, &testSignedRoot, "docker.com/notary", TrustPinConfig{})
|
||||
_, err = ValidateRoot(nil, &testSignedRoot, "docker.com/notary", TrustPinConfig{})
|
||||
require.Error(t, err, "An error was expected")
|
||||
require.Equal(t, err, &ErrValidationFail{Reason: "unable to retrieve valid leaf certificates"})
|
||||
|
||||
|
@ -197,7 +109,7 @@ func TestValidateRoot(t *testing.T) {
|
|||
// Unmarshal our signedroot
|
||||
json.Unmarshal(signedRootBytes.Bytes(), &testSignedRoot)
|
||||
|
||||
err = ValidateRoot(certStore, &testSignedRoot, "docker.com/notary", TrustPinConfig{})
|
||||
_, err = ValidateRoot(nil, &testSignedRoot, "docker.com/notary", TrustPinConfig{})
|
||||
require.Error(t, err, "An error was expected")
|
||||
require.Equal(t, err, &ErrValidationFail{Reason: "unable to retrieve valid leaf certificates"})
|
||||
|
||||
|
@ -216,7 +128,7 @@ func TestValidateRoot(t *testing.T) {
|
|||
// Unmarshal our signedroot
|
||||
json.Unmarshal(signedRootBytes.Bytes(), &testSignedRoot)
|
||||
|
||||
err = ValidateRoot(certStore, &testSignedRoot, "secure.example.com", TrustPinConfig{})
|
||||
_, err = ValidateRoot(nil, &testSignedRoot, "secure.example.com", TrustPinConfig{})
|
||||
require.Error(t, err, "An error was expected")
|
||||
require.Equal(t, err, &ErrValidationFail{Reason: "unable to retrieve valid leaf certificates"})
|
||||
}
|
||||
|
@ -230,14 +142,6 @@ func TestValidateRootWithoutTOFUS(t *testing.T) {
|
|||
defer os.RemoveAll(tempBaseDir)
|
||||
require.NoError(t, err, "failed to create a temporary directory: %s", err)
|
||||
|
||||
// Create a X509Store
|
||||
trustPath := filepath.Join(tempBaseDir, notary.TrustedCertsDir)
|
||||
certStore, err := trustmanager.NewX509FilteredFileStore(
|
||||
trustPath,
|
||||
trustmanager.FilterCertsExpiredSha1,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Execute our template
|
||||
templ, _ := template.New("SignedRSARootTemplate").Parse(signedRSARootTemplate)
|
||||
templ.Execute(&signedRootBytes, SignedRSARootTemplate{RootPem: validPEMEncodedRSARoot})
|
||||
|
@ -246,7 +150,7 @@ func TestValidateRootWithoutTOFUS(t *testing.T) {
|
|||
json.Unmarshal(signedRootBytes.Bytes(), &testSignedRoot)
|
||||
|
||||
// This call to ValidateRoot will fail since we are explicitly disabling TOFU and have no local certs
|
||||
err = ValidateRoot(certStore, &testSignedRoot, "docker.com/notary", TrustPinConfig{DisableTOFU: true})
|
||||
_, err = ValidateRoot(nil, &testSignedRoot, "docker.com/notary", TrustPinConfig{DisableTOFU: true})
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
|
@ -259,31 +163,25 @@ func TestValidateRootWithPinnedCert(t *testing.T) {
|
|||
defer os.RemoveAll(tempBaseDir)
|
||||
require.NoError(t, err, "failed to create a temporary directory: %s", err)
|
||||
|
||||
// Create a X509Store
|
||||
trustPath := filepath.Join(tempBaseDir, notary.TrustedCertsDir)
|
||||
certStore, err := trustmanager.NewX509FilteredFileStore(
|
||||
trustPath,
|
||||
trustmanager.FilterCertsExpiredSha1,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Execute our template
|
||||
templ, _ := template.New("SignedRSARootTemplate").Parse(signedRSARootTemplate)
|
||||
templ.Execute(&signedRootBytes, SignedRSARootTemplate{RootPem: validPEMEncodedRSARoot})
|
||||
|
||||
// Unmarshal our signedroot
|
||||
json.Unmarshal(signedRootBytes.Bytes(), &testSignedRoot)
|
||||
typedSignedRoot, err := data.RootFromSigned(&testSignedRoot)
|
||||
require.NoError(t, err)
|
||||
|
||||
// This call to ValidateRoot should succeed with the correct Cert ID (same as root public key ID)
|
||||
err = ValidateRoot(certStore, &testSignedRoot, "docker.com/notary", TrustPinConfig{Certs: map[string][]string{"docker.com/notary": {rootPubKeyID}}, DisableTOFU: true})
|
||||
validatedSignedRoot, err := ValidateRoot(nil, &testSignedRoot, "docker.com/notary", TrustPinConfig{Certs: map[string][]string{"docker.com/notary": {rootPubKeyID}}, DisableTOFU: true})
|
||||
require.NoError(t, err)
|
||||
|
||||
// purge the cert store to check another valid certs trust pinning
|
||||
certStore.RemoveAll()
|
||||
generateRootKeyIDs(typedSignedRoot)
|
||||
require.Equal(t, validatedSignedRoot, typedSignedRoot)
|
||||
|
||||
// This call to ValidateRoot should also succeed with the correct Cert ID (same as root public key ID), even though we passed an extra bad one
|
||||
err = ValidateRoot(certStore, &testSignedRoot, "docker.com/notary", TrustPinConfig{Certs: map[string][]string{"docker.com/notary": {rootPubKeyID, "invalidID"}}, DisableTOFU: true})
|
||||
validatedSignedRoot, err = ValidateRoot(nil, &testSignedRoot, "docker.com/notary", TrustPinConfig{Certs: map[string][]string{"docker.com/notary": {rootPubKeyID, "invalidID"}}, DisableTOFU: true})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, validatedSignedRoot, typedSignedRoot)
|
||||
}
|
||||
|
||||
func TestValidateRootWithPinnerCertAndIntermediates(t *testing.T) {
|
||||
|
@ -436,19 +334,15 @@ func TestValidateRootWithPinnerCertAndIntermediates(t *testing.T) {
|
|||
err = signed.Sign(cs, signedRoot, []data.PublicKey{ecdsax509Key}, 1, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
typedSignedRoot, err := data.RootFromSigned(signedRoot)
|
||||
require.NoError(t, err)
|
||||
|
||||
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
|
||||
defer os.RemoveAll(tempBaseDir)
|
||||
require.NoError(t, err, "failed to create a temporary directory: %s", err)
|
||||
// Create a X509Store
|
||||
trustPath := filepath.Join(tempBaseDir, notary.TrustedCertsDir)
|
||||
certStore, err := trustmanager.NewX509FilteredFileStore(
|
||||
trustPath,
|
||||
trustmanager.FilterCertsExpiredSha1,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = ValidateRoot(
|
||||
certStore,
|
||||
validatedRoot, err := ValidateRoot(
|
||||
nil,
|
||||
signedRoot,
|
||||
"docker.io/notary/test",
|
||||
TrustPinConfig{
|
||||
|
@ -459,6 +353,8 @@ func TestValidateRootWithPinnerCertAndIntermediates(t *testing.T) {
|
|||
},
|
||||
)
|
||||
require.NoError(t, err, "failed to validate certID with intermediate")
|
||||
generateRootKeyIDs(typedSignedRoot)
|
||||
require.Equal(t, typedSignedRoot, validatedRoot)
|
||||
}
|
||||
|
||||
func TestValidateRootFailuresWithPinnedCert(t *testing.T) {
|
||||
|
@ -470,40 +366,36 @@ func TestValidateRootFailuresWithPinnedCert(t *testing.T) {
|
|||
defer os.RemoveAll(tempBaseDir)
|
||||
require.NoError(t, err, "failed to create a temporary directory: %s", err)
|
||||
|
||||
// Create a X509Store
|
||||
trustPath := filepath.Join(tempBaseDir, notary.TrustedCertsDir)
|
||||
certStore, err := trustmanager.NewX509FilteredFileStore(
|
||||
trustPath,
|
||||
trustmanager.FilterCertsExpiredSha1,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Execute our template
|
||||
templ, _ := template.New("SignedRSARootTemplate").Parse(signedRSARootTemplate)
|
||||
templ.Execute(&signedRootBytes, SignedRSARootTemplate{RootPem: validPEMEncodedRSARoot})
|
||||
|
||||
// Unmarshal our signedroot
|
||||
json.Unmarshal(signedRootBytes.Bytes(), &testSignedRoot)
|
||||
typedSignedRoot, err := data.RootFromSigned(&testSignedRoot)
|
||||
require.NoError(t, err)
|
||||
|
||||
// This call to ValidateRoot should fail due to an incorrect cert ID
|
||||
err = ValidateRoot(certStore, &testSignedRoot, "docker.com/notary", TrustPinConfig{Certs: map[string][]string{"docker.com/notary": {"ABSOLUTELY NOT A CERT ID"}}, DisableTOFU: true})
|
||||
_, err = ValidateRoot(nil, &testSignedRoot, "docker.com/notary", TrustPinConfig{Certs: map[string][]string{"docker.com/notary": {"ABSOLUTELY NOT A CERT ID"}}, DisableTOFU: true})
|
||||
require.Error(t, err)
|
||||
|
||||
// This call to ValidateRoot should fail due to an empty cert ID
|
||||
err = ValidateRoot(certStore, &testSignedRoot, "docker.com/notary", TrustPinConfig{Certs: map[string][]string{"docker.com/notary": {""}}, DisableTOFU: true})
|
||||
_, err = ValidateRoot(nil, &testSignedRoot, "docker.com/notary", TrustPinConfig{Certs: map[string][]string{"docker.com/notary": {""}}, DisableTOFU: true})
|
||||
require.Error(t, err)
|
||||
|
||||
// This call to ValidateRoot should fail due to an invalid GUN (even though the cert ID is correct), and TOFUS is set to false
|
||||
err = ValidateRoot(certStore, &testSignedRoot, "docker.com/notary", TrustPinConfig{Certs: map[string][]string{"not_a_gun": {rootPubKeyID}}, DisableTOFU: true})
|
||||
_, err = ValidateRoot(nil, &testSignedRoot, "docker.com/notary", TrustPinConfig{Certs: map[string][]string{"not_a_gun": {rootPubKeyID}}, DisableTOFU: true})
|
||||
require.Error(t, err)
|
||||
|
||||
// This call to ValidateRoot should fail due to an invalid cert ID, even though it's a valid key ID for targets
|
||||
err = ValidateRoot(certStore, &testSignedRoot, "docker.com/notary", TrustPinConfig{Certs: map[string][]string{"docker.com/notary": {targetsPubKeyID}}, DisableTOFU: true})
|
||||
_, err = ValidateRoot(nil, &testSignedRoot, "docker.com/notary", TrustPinConfig{Certs: map[string][]string{"docker.com/notary": {targetsPubKeyID}}, DisableTOFU: true})
|
||||
require.Error(t, err)
|
||||
|
||||
// This call to ValidateRoot should succeed because we fall through to TOFUS because we have no matching GUNs under Certs
|
||||
err = ValidateRoot(certStore, &testSignedRoot, "docker.com/notary", TrustPinConfig{Certs: map[string][]string{"not_a_gun": {rootPubKeyID}}, DisableTOFU: false})
|
||||
validatedRoot, err := ValidateRoot(nil, &testSignedRoot, "docker.com/notary", TrustPinConfig{Certs: map[string][]string{"not_a_gun": {rootPubKeyID}}, DisableTOFU: false})
|
||||
require.NoError(t, err)
|
||||
generateRootKeyIDs(typedSignedRoot)
|
||||
require.Equal(t, typedSignedRoot, validatedRoot)
|
||||
}
|
||||
|
||||
func TestValidateRootWithPinnedCA(t *testing.T) {
|
||||
|
@ -515,44 +407,39 @@ func TestValidateRootWithPinnedCA(t *testing.T) {
|
|||
defer os.RemoveAll(tempBaseDir)
|
||||
require.NoError(t, err, "failed to create a temporary directory: %s", err)
|
||||
|
||||
// Create a X509Store
|
||||
trustPath := filepath.Join(tempBaseDir, notary.TrustedCertsDir)
|
||||
certStore, err := trustmanager.NewX509FilteredFileStore(
|
||||
trustPath,
|
||||
trustmanager.FilterCertsExpiredSha1,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
templ, _ := template.New("SignedRSARootTemplate").Parse(signedRSARootTemplate)
|
||||
templ.Execute(&signedRootBytes, SignedRSARootTemplate{RootPem: validPEMEncodedRSARoot})
|
||||
// Unmarshal our signedRoot
|
||||
json.Unmarshal(signedRootBytes.Bytes(), &testSignedRoot)
|
||||
typedSignedRoot, err := data.RootFromSigned(&testSignedRoot)
|
||||
require.NoError(t, err)
|
||||
|
||||
// This call to ValidateRoot will fail because we have an invalid path for the CA
|
||||
err = ValidateRoot(certStore, &testSignedRoot, "docker.com/notary", TrustPinConfig{CA: map[string]string{"docker.com/notary": filepath.Join(tempBaseDir, "nonexistent")}})
|
||||
_, err = ValidateRoot(nil, &testSignedRoot, "docker.com/notary", TrustPinConfig{CA: map[string]string{"docker.com/notary": filepath.Join(tempBaseDir, "nonexistent")}})
|
||||
require.Error(t, err)
|
||||
|
||||
// This call to ValidateRoot will fail because we have no valid GUNs to use, and TOFUS is disabled
|
||||
err = ValidateRoot(certStore, &testSignedRoot, "docker.com/notary", TrustPinConfig{CA: map[string]string{"othergun": filepath.Join(tempBaseDir, "nonexistent")}, DisableTOFU: true})
|
||||
_, err = ValidateRoot(nil, &testSignedRoot, "docker.com/notary", TrustPinConfig{CA: map[string]string{"othergun": filepath.Join(tempBaseDir, "nonexistent")}, DisableTOFU: true})
|
||||
require.Error(t, err)
|
||||
|
||||
// This call to ValidateRoot will succeed because we have no valid GUNs to use and we fall back to enabled TOFUS
|
||||
err = ValidateRoot(certStore, &testSignedRoot, "docker.com/notary", TrustPinConfig{CA: map[string]string{"othergun": filepath.Join(tempBaseDir, "nonexistent")}, DisableTOFU: false})
|
||||
validatedRoot, err := ValidateRoot(nil, &testSignedRoot, "docker.com/notary", TrustPinConfig{CA: map[string]string{"othergun": filepath.Join(tempBaseDir, "nonexistent")}, DisableTOFU: false})
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, certStore.RemoveAll())
|
||||
generateRootKeyIDs(typedSignedRoot)
|
||||
require.Equal(t, typedSignedRoot, validatedRoot)
|
||||
|
||||
// Write an invalid CA cert (not even a PEM) to the tempDir and ensure validation fails when using it
|
||||
invalidCAFilepath := filepath.Join(tempBaseDir, "invalid.ca")
|
||||
require.NoError(t, ioutil.WriteFile(invalidCAFilepath, []byte("ABSOLUTELY NOT A PEM"), 0644))
|
||||
|
||||
// Using this invalid CA cert should fail on ValidateRoot
|
||||
err = ValidateRoot(certStore, &testSignedRoot, "docker.com/notary", TrustPinConfig{CA: map[string]string{"docker.com/notary": invalidCAFilepath}, DisableTOFU: true})
|
||||
_, err = ValidateRoot(nil, &testSignedRoot, "docker.com/notary", TrustPinConfig{CA: map[string]string{"docker.com/notary": invalidCAFilepath}, DisableTOFU: true})
|
||||
require.Error(t, err)
|
||||
|
||||
validCAFilepath := "../fixtures/root-ca.crt"
|
||||
|
||||
// If we pass an invalid Certs entry in addition to this valid CA entry, since Certs has priority for pinning we will fail
|
||||
err = ValidateRoot(certStore, &testSignedRoot, "docker.com/notary", TrustPinConfig{Certs: map[string][]string{"docker.com/notary": {"invalidID"}}, CA: map[string]string{"docker.com/notary": validCAFilepath}, DisableTOFU: true})
|
||||
_, err = ValidateRoot(nil, &testSignedRoot, "docker.com/notary", TrustPinConfig{Certs: map[string][]string{"docker.com/notary": {"invalidID"}}, CA: map[string]string{"docker.com/notary": validCAFilepath}, DisableTOFU: true})
|
||||
require.Error(t, err)
|
||||
|
||||
// Now construct a new root with a valid cert chain, such that signatures are correct over the 'notary-signer' GUN. Pin the root-ca and validate
|
||||
|
@ -601,12 +488,16 @@ func TestValidateRootWithPinnedCA(t *testing.T) {
|
|||
err = signed.Sign(cs, newTestSignedRoot, []data.PublicKey{newRootKey}, 1, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Check that we validate correctly against a pinned CA and provided bundle
|
||||
err = ValidateRoot(certStore, newTestSignedRoot, "notary-signer", TrustPinConfig{CA: map[string]string{"notary-signer": validCAFilepath}, DisableTOFU: true})
|
||||
newTypedSignedRoot, err := data.RootFromSigned(newTestSignedRoot)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Check that we validate correctly against a pinned CA and provided bundle
|
||||
validatedRoot, err = ValidateRoot(nil, newTestSignedRoot, "notary-signer", TrustPinConfig{CA: map[string]string{"notary-signer": validCAFilepath}, DisableTOFU: true})
|
||||
require.NoError(t, err)
|
||||
generateRootKeyIDs(newTypedSignedRoot)
|
||||
require.Equal(t, newTypedSignedRoot, validatedRoot)
|
||||
|
||||
// Add an expired CA for the same gun to our previous pinned bundle, ensure that we still validate correctly
|
||||
certStore.RemoveAll()
|
||||
goodRootCABundle, err := trustmanager.LoadCertBundleFromFile(validCAFilepath)
|
||||
require.NoError(t, err)
|
||||
memKeyStore := trustmanager.NewKeyMemoryStore(passphraseRetriever)
|
||||
|
@ -623,10 +514,10 @@ func TestValidateRootWithPinnedCA(t *testing.T) {
|
|||
require.NoError(t, ioutil.WriteFile(bundleWithExpiredCertPath, bundleWithExpiredCert, 0644))
|
||||
|
||||
// Check that we validate correctly against a pinned CA and provided bundle
|
||||
err = ValidateRoot(certStore, newTestSignedRoot, "notary-signer", TrustPinConfig{CA: map[string]string{"notary-signer": bundleWithExpiredCertPath}, DisableTOFU: true})
|
||||
validatedRoot, err = ValidateRoot(nil, newTestSignedRoot, "notary-signer", TrustPinConfig{CA: map[string]string{"notary-signer": bundleWithExpiredCertPath}, DisableTOFU: true})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, newTypedSignedRoot, validatedRoot)
|
||||
|
||||
certStore.RemoveAll()
|
||||
testPubKey2, err := cryptoService.Create("root", "notary-signer", data.ECDSAKey)
|
||||
require.NoError(t, err)
|
||||
testPrivKey2, _, err := memKeyStore.GetKey(testPubKey2.ID())
|
||||
|
@ -638,11 +529,10 @@ func TestValidateRootWithPinnedCA(t *testing.T) {
|
|||
allExpiredCertPath := filepath.Join(tempBaseDir, "all_expired_cert.pem")
|
||||
require.NoError(t, ioutil.WriteFile(allExpiredCertPath, allExpiredCertBundle, 0644))
|
||||
// Now only use expired certs in the bundle, we should fail
|
||||
err = ValidateRoot(certStore, newTestSignedRoot, "notary-signer", TrustPinConfig{CA: map[string]string{"notary-signer": allExpiredCertPath}, DisableTOFU: true})
|
||||
_, err = ValidateRoot(nil, newTestSignedRoot, "notary-signer", TrustPinConfig{CA: map[string]string{"notary-signer": allExpiredCertPath}, DisableTOFU: true})
|
||||
require.Error(t, err)
|
||||
|
||||
// Add a CA cert for a that won't validate against the root leaf certificate
|
||||
certStore.RemoveAll()
|
||||
testPubKey3, err := cryptoService.Create("root", "notary-signer", data.ECDSAKey)
|
||||
require.NoError(t, err)
|
||||
testPrivKey3, _, err := memKeyStore.GetKey(testPubKey3.ID())
|
||||
|
@ -653,7 +543,7 @@ func TestValidateRootWithPinnedCA(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
bundleWithWrongCertPath := filepath.Join(tempBaseDir, "bundle_with_expired_cert.pem")
|
||||
require.NoError(t, ioutil.WriteFile(bundleWithWrongCertPath, bundleWithWrongCert, 0644))
|
||||
err = ValidateRoot(certStore, newTestSignedRoot, "notary-signer", TrustPinConfig{CA: map[string]string{"notary-signer": bundleWithWrongCertPath}, DisableTOFU: true})
|
||||
_, err = ValidateRoot(nil, newTestSignedRoot, "notary-signer", TrustPinConfig{CA: map[string]string{"notary-signer": bundleWithWrongCertPath}, DisableTOFU: true})
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
|
@ -666,62 +556,43 @@ func TestValidateSuccessfulRootRotation(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// Generates a X509Store in a temporary directory and returns the
|
||||
// store and certificates for two keys which have been added to the keystore.
|
||||
// Also returns the temporary directory so it can be cleaned up.
|
||||
func filestoreWithTwoCerts(t *testing.T, gun, keyAlg string) (
|
||||
string, trustmanager.X509Store, *cryptoservice.CryptoService, []*x509.Certificate) {
|
||||
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
|
||||
require.NoError(t, err, "failed to create a temporary directory: %s", err)
|
||||
|
||||
fileKeyStore, err := trustmanager.NewKeyFileStore(tempBaseDir, passphraseRetriever)
|
||||
require.NoError(t, err)
|
||||
|
||||
cryptoService := cryptoservice.NewCryptoService(fileKeyStore)
|
||||
|
||||
// Create a store
|
||||
trustPath := filepath.Join(tempBaseDir, notary.TrustedCertsDir)
|
||||
certStore, err := trustmanager.NewX509FilteredFileStore(
|
||||
trustPath,
|
||||
trustmanager.FilterCertsExpiredSha1,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
certificates := make([]*x509.Certificate, 2)
|
||||
for i := 0; i < 2; i++ {
|
||||
pubKey, err := cryptoService.Create("root", gun, keyAlg)
|
||||
require.NoError(t, err)
|
||||
|
||||
key, _, err := fileKeyStore.GetKey(pubKey.ID())
|
||||
require.NoError(t, err)
|
||||
|
||||
cert, err := generateTestingCertificate(key, gun)
|
||||
require.NoError(t, err)
|
||||
|
||||
certificates[i] = cert
|
||||
}
|
||||
return tempBaseDir, certStore, cryptoService, certificates
|
||||
}
|
||||
|
||||
func testValidateSuccessfulRootRotation(t *testing.T, keyAlg, rootKeyType string) {
|
||||
// The gun to test
|
||||
gun := "docker.com/notary"
|
||||
|
||||
tempBaseDir, certStore, cs, certificates := filestoreWithTwoCerts(t, gun, keyAlg)
|
||||
defer os.RemoveAll(tempBaseDir)
|
||||
origRootCert := certificates[0]
|
||||
replRootCert := certificates[1]
|
||||
|
||||
// Add the old root cert part of trustedCertificates
|
||||
certStore.AddCert(origRootCert)
|
||||
|
||||
// We need the PEM representation of the replacement key to put it into the TUF data
|
||||
origRootPEMCert := trustmanager.CertToPEM(origRootCert)
|
||||
replRootPEMCert := trustmanager.CertToPEM(replRootCert)
|
||||
memKeyStore := trustmanager.NewKeyMemoryStore(passphraseRetriever)
|
||||
cs := cryptoservice.NewCryptoService(memKeyStore)
|
||||
|
||||
// Tuf key with PEM-encoded x509 certificate
|
||||
origRootKey := data.NewPublicKey(rootKeyType, origRootPEMCert)
|
||||
replRootKey := data.NewPublicKey(rootKeyType, replRootPEMCert)
|
||||
origRootKey, err := testutils.CreateKey(cs, gun, data.CanonicalRootRole, keyAlg)
|
||||
require.NoError(t, err)
|
||||
|
||||
origRootRole, err := data.NewRole(data.CanonicalRootRole, 1, []string{origRootKey.ID()}, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
origTestRoot, err := data.NewRoot(
|
||||
map[string]data.PublicKey{origRootKey.ID(): origRootKey},
|
||||
map[string]*data.RootRole{
|
||||
data.CanonicalRootRole: &origRootRole.RootRole,
|
||||
data.CanonicalTargetsRole: &origRootRole.RootRole,
|
||||
data.CanonicalSnapshotRole: &origRootRole.RootRole,
|
||||
data.CanonicalTimestampRole: &origRootRole.RootRole,
|
||||
},
|
||||
false,
|
||||
)
|
||||
require.NoError(t, err, "Failed to create new root")
|
||||
|
||||
signedOrigTestRoot, err := origTestRoot.ToSigned()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = signed.Sign(cs, signedOrigTestRoot, []data.PublicKey{origRootKey}, 1, nil)
|
||||
require.NoError(t, err)
|
||||
prevRoot, err := data.RootFromSigned(signedOrigTestRoot)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Tuf key with PEM-encoded x509 certificate
|
||||
replRootKey, err := testutils.CreateKey(cs, gun, data.CanonicalRootRole, keyAlg)
|
||||
require.NoError(t, err)
|
||||
|
||||
rootRole, err := data.NewRole(data.CanonicalRootRole, 1, []string{replRootKey.ID()}, nil)
|
||||
require.NoError(t, err)
|
||||
|
@ -743,15 +614,15 @@ func testValidateSuccessfulRootRotation(t *testing.T, keyAlg, rootKeyType string
|
|||
err = signed.Sign(cs, signedTestRoot, []data.PublicKey{replRootKey, origRootKey}, 2, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
// This call to ValidateRoot will succeed since we are using a valid PEM
|
||||
// encoded certificate, and have no other certificates for this CN
|
||||
err = ValidateRoot(certStore, signedTestRoot, gun, TrustPinConfig{})
|
||||
typedSignedRoot, err := data.RootFromSigned(signedTestRoot)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Finally, validate the only trusted certificate that exists is the new one
|
||||
certificates = certStore.GetCertificates()
|
||||
require.Len(t, certificates, 1)
|
||||
require.Equal(t, certificates[0], replRootCert)
|
||||
// This call to ValidateRoot will succeed since we are using a valid PEM
|
||||
// encoded certificate, and have no other certificates for this CN
|
||||
validatedRoot, err := ValidateRoot(prevRoot, signedTestRoot, gun, TrustPinConfig{})
|
||||
require.NoError(t, err)
|
||||
generateRootKeyIDs(typedSignedRoot)
|
||||
require.Equal(t, typedSignedRoot, validatedRoot)
|
||||
}
|
||||
|
||||
// TestValidateRootRotationMissingOrigSig runs through a full root certificate rotation
|
||||
|
@ -767,20 +638,39 @@ func TestValidateRootRotationMissingOrigSig(t *testing.T) {
|
|||
func testValidateRootRotationMissingOrigSig(t *testing.T, keyAlg, rootKeyType string) {
|
||||
gun := "docker.com/notary"
|
||||
|
||||
tempBaseDir, certStore, cryptoService, certificates := filestoreWithTwoCerts(
|
||||
t, gun, keyAlg)
|
||||
defer os.RemoveAll(tempBaseDir)
|
||||
origRootCert := certificates[0]
|
||||
replRootCert := certificates[1]
|
||||
|
||||
// Add the old root cert part of trustedCertificates
|
||||
certStore.AddCert(origRootCert)
|
||||
|
||||
// We need the PEM representation of the replacement key to put it into the TUF data
|
||||
replRootPEMCert := trustmanager.CertToPEM(replRootCert)
|
||||
memKeyStore := trustmanager.NewKeyMemoryStore(passphraseRetriever)
|
||||
cs := cryptoservice.NewCryptoService(memKeyStore)
|
||||
|
||||
// Tuf key with PEM-encoded x509 certificate
|
||||
replRootKey := data.NewPublicKey(rootKeyType, replRootPEMCert)
|
||||
origRootKey, err := testutils.CreateKey(cs, gun, data.CanonicalRootRole, keyAlg)
|
||||
require.NoError(t, err)
|
||||
|
||||
origRootRole, err := data.NewRole(data.CanonicalRootRole, 1, []string{origRootKey.ID()}, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
origTestRoot, err := data.NewRoot(
|
||||
map[string]data.PublicKey{origRootKey.ID(): origRootKey},
|
||||
map[string]*data.RootRole{
|
||||
data.CanonicalRootRole: &origRootRole.RootRole,
|
||||
data.CanonicalTargetsRole: &origRootRole.RootRole,
|
||||
data.CanonicalSnapshotRole: &origRootRole.RootRole,
|
||||
data.CanonicalTimestampRole: &origRootRole.RootRole,
|
||||
},
|
||||
false,
|
||||
)
|
||||
require.NoError(t, err, "Failed to create new root")
|
||||
|
||||
signedOrigTestRoot, err := origTestRoot.ToSigned()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = signed.Sign(cs, signedOrigTestRoot, []data.PublicKey{origRootKey}, 1, nil)
|
||||
require.NoError(t, err)
|
||||
prevRoot, err := data.RootFromSigned(signedOrigTestRoot)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Tuf key with PEM-encoded x509 certificate
|
||||
replRootKey, err := testutils.CreateKey(cs, gun, data.CanonicalRootRole, keyAlg)
|
||||
require.NoError(t, err)
|
||||
|
||||
rootRole, err := data.NewRole(data.CanonicalRootRole, 1, []string{replRootKey.ID()}, nil)
|
||||
require.NoError(t, err)
|
||||
|
@ -801,19 +691,13 @@ func testValidateRootRotationMissingOrigSig(t *testing.T, keyAlg, rootKeyType st
|
|||
require.NoError(t, err)
|
||||
|
||||
// We only sign with the new key, and not with the original one.
|
||||
err = signed.Sign(cryptoService, signedTestRoot, []data.PublicKey{replRootKey}, 1, nil)
|
||||
err = signed.Sign(cs, signedTestRoot, []data.PublicKey{replRootKey}, 1, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
// This call to ValidateRoot will succeed since we are using a valid PEM
|
||||
// encoded certificate, and have no other certificates for this CN
|
||||
err = ValidateRoot(certStore, signedTestRoot, gun, TrustPinConfig{})
|
||||
_, err = ValidateRoot(prevRoot, signedTestRoot, gun, TrustPinConfig{})
|
||||
require.Error(t, err, "insuficient signatures on root")
|
||||
|
||||
// Finally, validate the only trusted certificate that exists is still
|
||||
// the old one
|
||||
certificates = certStore.GetCertificates()
|
||||
require.Len(t, certificates, 1)
|
||||
require.Equal(t, certificates[0], origRootCert)
|
||||
}
|
||||
|
||||
// TestValidateRootRotationMissingNewSig runs through a full root certificate rotation
|
||||
|
@ -829,22 +713,39 @@ func TestValidateRootRotationMissingNewSig(t *testing.T) {
|
|||
func testValidateRootRotationMissingNewSig(t *testing.T, keyAlg, rootKeyType string) {
|
||||
gun := "docker.com/notary"
|
||||
|
||||
tempBaseDir, certStore, cryptoService, certificates := filestoreWithTwoCerts(
|
||||
t, gun, keyAlg)
|
||||
defer os.RemoveAll(tempBaseDir)
|
||||
origRootCert := certificates[0]
|
||||
replRootCert := certificates[1]
|
||||
|
||||
// Add the old root cert part of trustedCertificates
|
||||
certStore.AddCert(origRootCert)
|
||||
|
||||
// We need the PEM representation of the replacement key to put it into the TUF data
|
||||
origRootPEMCert := trustmanager.CertToPEM(origRootCert)
|
||||
replRootPEMCert := trustmanager.CertToPEM(replRootCert)
|
||||
memKeyStore := trustmanager.NewKeyMemoryStore(passphraseRetriever)
|
||||
cs := cryptoservice.NewCryptoService(memKeyStore)
|
||||
|
||||
// Tuf key with PEM-encoded x509 certificate
|
||||
origRootKey := data.NewPublicKey(rootKeyType, origRootPEMCert)
|
||||
replRootKey := data.NewPublicKey(rootKeyType, replRootPEMCert)
|
||||
origRootKey, err := testutils.CreateKey(cs, gun, data.CanonicalRootRole, keyAlg)
|
||||
require.NoError(t, err)
|
||||
|
||||
origRootRole, err := data.NewRole(data.CanonicalRootRole, 1, []string{origRootKey.ID()}, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
origTestRoot, err := data.NewRoot(
|
||||
map[string]data.PublicKey{origRootKey.ID(): origRootKey},
|
||||
map[string]*data.RootRole{
|
||||
data.CanonicalRootRole: &origRootRole.RootRole,
|
||||
data.CanonicalTargetsRole: &origRootRole.RootRole,
|
||||
data.CanonicalSnapshotRole: &origRootRole.RootRole,
|
||||
data.CanonicalTimestampRole: &origRootRole.RootRole,
|
||||
},
|
||||
false,
|
||||
)
|
||||
require.NoError(t, err, "Failed to create new root")
|
||||
|
||||
signedOrigTestRoot, err := origTestRoot.ToSigned()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = signed.Sign(cs, signedOrigTestRoot, []data.PublicKey{origRootKey}, 1, nil)
|
||||
require.NoError(t, err)
|
||||
prevRoot, err := data.RootFromSigned(signedOrigTestRoot)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Tuf key with PEM-encoded x509 certificate
|
||||
replRootKey, err := testutils.CreateKey(cs, gun, data.CanonicalRootRole, keyAlg)
|
||||
require.NoError(t, err)
|
||||
|
||||
rootRole, err := data.NewRole(data.CanonicalRootRole, 1, []string{replRootKey.ID()}, nil)
|
||||
require.NoError(t, err)
|
||||
|
@ -865,19 +766,13 @@ func testValidateRootRotationMissingNewSig(t *testing.T, keyAlg, rootKeyType str
|
|||
require.NoError(t, err)
|
||||
|
||||
// We only sign with the old key, and not with the new one
|
||||
err = signed.Sign(cryptoService, signedTestRoot, []data.PublicKey{origRootKey}, 1, nil)
|
||||
err = signed.Sign(cs, signedTestRoot, []data.PublicKey{origRootKey}, 1, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
// This call to ValidateRoot will succeed since we are using a valid PEM
|
||||
// encoded certificate, and have no other certificates for this CN
|
||||
err = ValidateRoot(certStore, signedTestRoot, gun, TrustPinConfig{})
|
||||
_, err = ValidateRoot(prevRoot, signedTestRoot, gun, TrustPinConfig{})
|
||||
require.Error(t, err, "insuficient signatures on root")
|
||||
|
||||
// Finally, validate the only trusted certificate that exists is still
|
||||
// the old one
|
||||
certificates = certStore.GetCertificates()
|
||||
require.Len(t, certificates, 1)
|
||||
require.Equal(t, certificates[0], origRootCert)
|
||||
}
|
||||
|
||||
func generateTestingCertificate(rootKey data.PrivateKey, gun string) (*x509.Certificate, error) {
|
||||
|
@ -889,3 +784,12 @@ func generateExpiredTestingCertificate(rootKey data.PrivateKey, gun string) (*x5
|
|||
startTime := time.Now().AddDate(-10, 0, 0)
|
||||
return cryptoservice.GenerateCertificate(rootKey, gun, startTime, startTime.AddDate(1, 0, 0))
|
||||
}
|
||||
|
||||
// Helper function for explicitly generating key IDs and unexported fields for equality testing
|
||||
func generateRootKeyIDs(r *data.SignedRoot) {
|
||||
for _, keyID := range r.Signed.Roles[data.CanonicalRootRole].KeyIDs {
|
||||
if k, ok := r.Signed.Keys[keyID]; ok {
|
||||
_ = k.ID()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package trustpinning
|
|||
import (
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/notary/trustmanager"
|
||||
"github.com/docker/notary/tuf/utils"
|
||||
"strings"
|
||||
|
@ -67,17 +68,12 @@ func NewTrustPinChecker(trustPinConfig TrustPinConfig, gun string) (CertChecker,
|
|||
func (t trustPinChecker) certsCheck(leafCert *x509.Certificate, intCerts []*x509.Certificate) bool {
|
||||
// reconstruct the leaf + intermediate cert chain, which is bundled as {leaf, intermediates...},
|
||||
// in order to get the matching id in the root file
|
||||
leafCertID, err := trustmanager.FingerprintCert(leafCert)
|
||||
key, err := trustmanager.CertBundleToKey(leafCert, intCerts)
|
||||
if err != nil {
|
||||
logrus.Debug("error creating cert bundle: ", err.Error())
|
||||
return false
|
||||
}
|
||||
rootKeys := trustmanager.CertsToKeys([]*x509.Certificate{leafCert}, map[string][]*x509.Certificate{leafCertID: intCerts})
|
||||
for keyID := range rootKeys {
|
||||
if utils.StrSliceContains(t.pinnedCertIDs, keyID) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return utils.StrSliceContains(t.pinnedCertIDs, key.ID())
|
||||
}
|
||||
|
||||
func (t trustPinChecker) caCheck(leafCert *x509.Certificate, intCerts []*x509.Certificate) bool {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package testutils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"sort"
|
||||
"time"
|
||||
|
@ -19,8 +20,8 @@ import (
|
|||
|
||||
// CreateKey creates a new key inside the cryptoservice for the given role and gun,
|
||||
// returning the public key. If the role is a root role, create an x509 key.
|
||||
func CreateKey(cs signed.CryptoService, gun, role string) (data.PublicKey, error) {
|
||||
key, err := cs.Create(role, gun, data.ECDSAKey)
|
||||
func CreateKey(cs signed.CryptoService, gun, role, keyAlgorithm string) (data.PublicKey, error) {
|
||||
key, err := cs.Create(role, gun, keyAlgorithm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -36,7 +37,17 @@ func CreateKey(cs signed.CryptoService, gun, role string) (data.PublicKey, error
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key = data.NewECDSAx509PublicKey(trustmanager.CertToPEM(cert))
|
||||
// Keep the x509 key type consistent with the key's algorithm
|
||||
switch keyAlgorithm {
|
||||
case data.RSAKey:
|
||||
key = data.NewRSAx509PublicKey(trustmanager.CertToPEM(cert))
|
||||
case data.ECDSAKey:
|
||||
key = data.NewECDSAx509PublicKey(trustmanager.CertToPEM(cert))
|
||||
default:
|
||||
// This should be impossible because of the Create() call above, but just in case
|
||||
return nil, fmt.Errorf("invalid key algorithm type")
|
||||
}
|
||||
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
|
@ -50,7 +61,7 @@ func EmptyRepo(gun string, delegationRoles ...string) (*tuf.Repo, signed.CryptoS
|
|||
|
||||
baseRoles := map[string]data.BaseRole{}
|
||||
for _, role := range data.BaseRoles {
|
||||
key, err := CreateKey(cs, gun, role)
|
||||
key, err := CreateKey(cs, gun, role, data.ECDSAKey)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -77,7 +88,7 @@ func EmptyRepo(gun string, delegationRoles ...string) (*tuf.Repo, signed.CryptoS
|
|||
sort.Strings(delegationRoles)
|
||||
for _, delgName := range delegationRoles {
|
||||
// create a delegations key and a delegation in the tuf repo
|
||||
delgKey, err := CreateKey(cs, gun, delgName)
|
||||
delgKey, err := CreateKey(cs, gun, delgName, data.ECDSAKey)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
|
|
@ -269,7 +269,7 @@ func (m *MetadataSwizzler) SignMetadataWithInvalidKey(role string) error {
|
|||
|
||||
// create an invalid key, but not in the existing CryptoService
|
||||
cs := cryptoservice.NewCryptoService(trustmanager.NewKeyMemoryStore(passphrase.ConstantRetriever("")))
|
||||
key, err := CreateKey(cs, m.Gun, role)
|
||||
key, err := CreateKey(cs, m.Gun, role, data.ECDSAKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -466,7 +466,7 @@ func (m *MetadataSwizzler) RotateKey(role string, key data.PublicKey) error {
|
|||
// ChangeRootKey swaps out the root key with a new key, and re-signs the metadata
|
||||
// with the new key
|
||||
func (m *MetadataSwizzler) ChangeRootKey() error {
|
||||
key, err := CreateKey(m.CryptoService, m.Gun, data.CanonicalRootRole)
|
||||
key, err := CreateKey(m.CryptoService, m.Gun, data.CanonicalRootRole, data.ECDSAKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue