From 0313aa59580f7831fffb61ac3c55ca66dd9c1763 Mon Sep 17 00:00:00 2001 From: Diogo Monica Date: Wed, 15 Jul 2015 17:29:33 -0700 Subject: [PATCH 01/17] Adding parsing of multiple certificates, and leaf cert filtering methods Signed-off-by: Diogo Monica --- trustmanager/x509utils.go | 51 +++++++++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/trustmanager/x509utils.go b/trustmanager/x509utils.go index 6b8acb6f1e..8b0c550f9b 100644 --- a/trustmanager/x509utils.go +++ b/trustmanager/x509utils.go @@ -127,25 +127,62 @@ func loadCertsFromDir(s *X509FileStore) { } } -// LoadCertFromFile tries to adds a X509 certificate to the store given a filename +// 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" func LoadCertFromFile(filename string) (*x509.Certificate, error) { - // TODO(diogo): handle multiple certificates in one file. + certs, err := LoadCertBundleFromFile(filename) + if err != nil { + return nil, err + } + return certs[0], nil +} + +// LoadCertBundleFromFile loads certificates from the []byte provided. The +// data is expected to be PEM Encoded and contain one of more certificates +// with PEM type "CERTIFICATE" +func LoadCertBundleFromFile(filename string) ([]*x509.Certificate, error) { b, err := ioutil.ReadFile(filename) if err != nil { return nil, err } + + return LoadCertBundleFromPEM(b) +} + +// LoadCertBundleFromPEM loads certificates from the []byte provided. The +// data is expected to be PEM Encoded and contain one of more certificates +// with PEM type "CERTIFICATE" +func LoadCertBundleFromPEM(pemBytes []byte) ([]*x509.Certificate, error) { + certificates := []*x509.Certificate{} var block *pem.Block - block, b = pem.Decode(b) - for ; block != nil; block, b = pem.Decode(b) { + block, pemBytes = pem.Decode(pemBytes) + for ; block != nil; block, pemBytes = pem.Decode(pemBytes) { if block.Type == "CERTIFICATE" { cert, err := x509.ParseCertificate(block.Bytes) - if err == nil { - return cert, nil + if err != nil { + return nil, err } + certificates = append(certificates, cert) + } else { + return nil, fmt.Errorf("invalid pem block type: %s", block.Type) } } - return nil, errors.New("could not load certificate from file") + return certificates, nil +} + +// GetLeafCerts parses a list of x509 Certificates and returns all of them +// that aren't CA +func GetLeafCerts(certs []*x509.Certificate) []*x509.Certificate { + var leafCerts []*x509.Certificate + for _, cert := range certs { + if cert.IsCA { + continue + } + leafCerts = append(leafCerts, cert) + } + return leafCerts } // ParsePEMPrivateKey returns a data.PrivateKey from a PEM encoded private key. It From 58e6544d0ae4a5602ca48ab06fa10430dc78c7a4 Mon Sep 17 00:00:00 2001 From: Diogo Monica Date: Wed, 15 Jul 2015 17:30:21 -0700 Subject: [PATCH 02/17] Adding Cert retrieval by common name, and renaming KeyID to CertID Signed-off-by: Diogo Monica --- cmd/notary/keys.go | 8 ++-- trustmanager/x509filestore.go | 63 ++++++++++++++++++++++--------- trustmanager/x509memstore.go | 49 ++++++++++++++++++------ trustmanager/x509memstore_test.go | 10 ++--- trustmanager/x509store.go | 3 +- 5 files changed, 94 insertions(+), 39 deletions(-) diff --git a/cmd/notary/keys.go b/cmd/notary/keys.go index cc7831e03f..62b9e7a47c 100644 --- a/cmd/notary/keys.go +++ b/cmd/notary/keys.go @@ -64,7 +64,7 @@ func keysRemove(cmd *cobra.Command, args []string) { gunOrID := args[0] // Try to retrieve the ID from the CA store. - cert, err := caStore.GetCertificateByKeyID(gunOrID) + cert, err := caStore.GetCertificateByCertID(gunOrID) if err == nil { fmt.Printf("Removing: ") printCert(cert) @@ -78,7 +78,7 @@ func keysRemove(cmd *cobra.Command, args []string) { } // Try to retrieve the ID from the Certificate store. - cert, err = certificateStore.GetCertificateByKeyID(gunOrID) + cert, err = certificateStore.GetCertificateByCertID(gunOrID) if err == nil { fmt.Printf("Removing: ") printCert(cert) @@ -216,12 +216,12 @@ func keysGenerate(cmd *cobra.Command, args []string) { func printCert(cert *x509.Certificate) { timeDifference := cert.NotAfter.Sub(time.Now()) - keyID, err := trustmanager.FingerprintCert(cert) + certID, err := trustmanager.FingerprintCert(cert) if err != nil { fatalf("could not fingerprint certificate: %v", err) } - fmt.Printf("%s %s (expires in: %v days)\n", cert.Subject.CommonName, keyID, math.Floor(timeDifference.Hours()/24)) + fmt.Printf("%s %s (expires in: %v days)\n", cert.Subject.CommonName, certID, math.Floor(timeDifference.Hours()/24)) } func printKey(keyPath string) { diff --git a/trustmanager/x509filestore.go b/trustmanager/x509filestore.go index 553565356b..d6c23647ea 100644 --- a/trustmanager/x509filestore.go +++ b/trustmanager/x509filestore.go @@ -70,14 +70,14 @@ func (s X509FileStore) AddCert(cert *x509.Certificate) error { // addNamedCert allows adding a certificate while controling 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, keyID, err := fileName(cert) + fileName, certID, err := fileName(cert) if err != nil { return err } - logrus.Debug("Adding cert with keyID: ", keyID) + logrus.Debug("Adding cert with certID: ", certID) // Validate if we already loaded this certificate before - if _, ok := s.fingerprintMap[keyID]; ok { + if _, ok := s.fingerprintMap[certID]; ok { return errors.New("certificate already in the store") } @@ -98,11 +98,11 @@ func (s X509FileStore) addNamedCert(cert *x509.Certificate) error { } // We wrote the certificate succcessfully, add it to our in-memory storage - s.fingerprintMap[keyID] = cert - s.fileMap[keyID] = fileName + s.fingerprintMap[certID] = cert + s.fileMap[certID] = fileName - name := string(cert.RawSubject) - s.nameMap[name] = append(s.nameMap[name], keyID) + name := string(cert.Subject.CommonName) + s.nameMap[name] = append(s.nameMap[name], certID) return nil } @@ -113,13 +113,13 @@ func (s X509FileStore) RemoveCert(cert *x509.Certificate) error { return errors.New("removing nil Certificate from X509Store") } - keyID, err := fingerprintCert(cert) + certID, err := fingerprintCert(cert) if err != nil { return err } - delete(s.fingerprintMap, keyID) - filename := s.fileMap[keyID] - delete(s.fileMap, keyID) + delete(s.fingerprintMap, certID) + filename := s.fileMap[certID] + delete(s.fileMap, certID) name := string(cert.RawSubject) @@ -127,7 +127,7 @@ func (s X509FileStore) RemoveCert(cert *x509.Certificate) error { fpList := s.nameMap[name] newfpList := fpList[:0] for _, x := range fpList { - if x != keyID { + if x != certID { newfpList = append(newfpList, x) } } @@ -183,21 +183,48 @@ func (s X509FileStore) GetCertificatePool() *x509.CertPool { return pool } -// GetCertificateByKeyID returns the certificate that matches a certain keyID or error -func (s X509FileStore) GetCertificateByKeyID(keyID string) (*x509.Certificate, error) { +// 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(keyID) != 64 { + 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(keyID)]; ok { + if cert, ok := s.fingerprintMap[CertID(certID)]; ok { return cert, nil } return nil, errors.New("certificate not found in Key Store") } +// 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, err + } + certs = append(certs, cert) + } + } + if len(certs) == 0 { + return nil, errors.New("common name not found in Key Store") + } + + 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. @@ -217,10 +244,10 @@ func (s X509FileStore) GetVerifyOptions(dnsName string) (x509.VerifyOptions, err } func fileName(cert *x509.Certificate) (string, CertID, error) { - keyID, err := fingerprintCert(cert) + certID, err := fingerprintCert(cert) if err != nil { return "", "", err } - return path.Join(cert.Subject.CommonName, string(keyID)), keyID, nil + return path.Join(cert.Subject.CommonName, string(certID)), certID, nil } diff --git a/trustmanager/x509memstore.go b/trustmanager/x509memstore.go index f6bcb9d796..db5b60b100 100644 --- a/trustmanager/x509memstore.go +++ b/trustmanager/x509memstore.go @@ -23,7 +23,7 @@ func NewX509MemStore() *X509MemStore { } } -// NewX509FilteredMemStore returns a new X509FileStore that validates certificates +// NewX509FilteredMemStore returns a new X509Memstore that validates certificates // that are added. func NewX509FilteredMemStore(validate func(*x509.Certificate) bool) *X509MemStore { s := &X509MemStore{ @@ -46,14 +46,14 @@ func (s X509MemStore) AddCert(cert *x509.Certificate) error { return errors.New("certificate failed validation") } - keyID, err := fingerprintCert(cert) + certID, err := fingerprintCert(cert) if err != nil { return err } - s.fingerprintMap[keyID] = cert + s.fingerprintMap[certID] = cert name := string(cert.RawSubject) - s.nameMap[name] = append(s.nameMap[name], keyID) + s.nameMap[name] = append(s.nameMap[name], certID) return nil } @@ -64,18 +64,18 @@ func (s X509MemStore) RemoveCert(cert *x509.Certificate) error { return errors.New("removing nil Certificate to X509Store") } - keyID, err := fingerprintCert(cert) + certID, err := fingerprintCert(cert) if err != nil { return err } - delete(s.fingerprintMap, keyID) + 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 != keyID { + if x != certID { newfpList = append(newfpList, x) } } @@ -125,21 +125,48 @@ func (s X509MemStore) GetCertificatePool() *x509.CertPool { return pool } -// GetCertificateByKeyID returns the certificate that matches a certain keyID or error -func (s X509MemStore) GetCertificateByKeyID(keyID string) (*x509.Certificate, error) { +// 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(keyID) != 64 { + 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(keyID)]; ok { + if cert, ok := s.fingerprintMap[CertID(certID)]; ok { return cert, nil } return nil, errors.New("certificate not found in Key Store") } +// 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, errors.New("common name not found in Key Store") + } + + 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. diff --git a/trustmanager/x509memstore_test.go b/trustmanager/x509memstore_test.go index 9cb46cd5c9..9560208bb1 100644 --- a/trustmanager/x509memstore_test.go +++ b/trustmanager/x509memstore_test.go @@ -106,14 +106,14 @@ func TestRemoveCert(t *testing.T) { } } -func TestInexistentGetCertificateByKeyID(t *testing.T) { +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.GetCertificateByKeyID("4d06afd30b8bed131d2a84c97d00b37f422021598bfae34285ce98e77b708b5a") + _, err = store.GetCertificateByCertID("4d06afd30b8bed131d2a84c97d00b37f422021598bfae34285ce98e77b708b5a") if err == nil { t.Fatalf("no error returned for inexistent certificate") } @@ -138,15 +138,15 @@ func TestGetCertificateByKeyID(t *testing.T) { t.Fatalf("failed to load certificate from PEM: %v", err) } - keyID, err := FingerprintCert(cert) + 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.GetCertificateByKeyID(keyID) + _, err = store.GetCertificateByCertID(certID) if err != nil { - t.Fatalf("expected certificate in store: %s", keyID) + t.Fatalf("expected certificate in store: %s", certID) } } diff --git a/trustmanager/x509store.go b/trustmanager/x509store.go index 85307b5fce..7c15456b01 100644 --- a/trustmanager/x509store.go +++ b/trustmanager/x509store.go @@ -14,7 +14,8 @@ type X509Store interface { AddCertFromPEM(pemCerts []byte) error AddCertFromFile(filename string) error RemoveCert(cert *x509.Certificate) error - GetCertificateByKeyID(keyID string) (*x509.Certificate, 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) From a7d8cb0a19eae05941d5699a2c51813b82a4234a Mon Sep 17 00:00:00 2001 From: Diogo Monica Date: Wed, 15 Jul 2015 17:31:57 -0700 Subject: [PATCH 03/17] Adding chain support for roots.json and scaning for leaf instead of hardcoding Signed-off-by: Diogo Monica --- keystoremanager/keystoremanager.go | 41 ++++++++++++++++-------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/keystoremanager/keystoremanager.go b/keystoremanager/keystoremanager.go index b1e43b5f9b..a19db210ab 100644 --- a/keystoremanager/keystoremanager.go +++ b/keystoremanager/keystoremanager.go @@ -3,8 +3,6 @@ package keystoremanager import ( "crypto/rand" "crypto/x509" - "encoding/json" - "encoding/pem" "errors" "fmt" "path/filepath" @@ -190,26 +188,32 @@ Example TUF Content for root key: } */ func (km *KeyStoreManager) ValidateRoot(root *data.Signed, dnsName string) error { - rootSigned := &data.Root{} - err := json.Unmarshal(root.Signed, rootSigned) + rootSigned, err := data.RootFromSigned(root) if err != nil { return err } - certs := make(map[string]data.PublicKey) - for _, keyID := range rootSigned.Roles["root"].KeyIDs { - // TODO(dlaw): currently assuming only one cert contained in - // public key entry. Need to fix when we want to pass in chains. - k, _ := pem.Decode([]byte(rootSigned.Keys[keyID].Public())) - decodedCerts, err := x509.ParseCertificates(k.Bytes) + validKeys := make(map[string]*data.PublicKey) + for _, keyID := range rootSigned.Signed.Roles["root"].KeyIDs { + // Decode all the x509 certificates that were bundled with this + // specific root key + decodedCerts, err := trustmanager.LoadCertBundleFromPEM([]byte(rootSigned.Signed.Keys[keyID].Public())) if err != nil { logrus.Debugf("error while parsing root certificate with keyID: %s, %v", keyID, err) continue } - // TODO(diogo): Assuming that first certificate is the leaf-cert. Need to - // iterate over all decodedCerts and find a non-CA one (should be the last). - leafCert := decodedCerts[0] + // Get all non-CA certificates in the decoded certificates + leafCerts := trustmanager.GetLeafCerts(decodedCerts) + + // If we got no leaf certificates or we got more than one, fail + if len(leafCerts) != 1 { + logrus.Debugf("error while parsing root certificate with keyID: %s, %v", keyID, err) + continue + } + + // Get the ID of the leaf certificate + leafCert := leafCerts[0] leafID, err := trustmanager.FingerprintCert(leafCert) if err != nil { logrus.Debugf("error while fingerprinting root certificate with keyID: %s, %v", keyID, err) @@ -222,23 +226,22 @@ func (km *KeyStoreManager) ValidateRoot(root *data.Signed, dnsName string) error // ID gets computed. _, err = km.trustedCertificateStore.GetCertificateByKeyID(leafID) if err == nil && leafCert.Subject.CommonName == dnsName { - certs[keyID] = rootSigned.Keys[keyID] + validKeys[keyID] = rootSigned.Signed.Keys[keyID] } - // Check to see if this leafCertificate has a chain to one of the Root CAs - // of our CA Store. + // Check to see if this leafCertificate has a chain to one of the Root // CAs of our CA Store. certList := []*x509.Certificate{leafCert} err = trustmanager.Verify(km.trustedCAStore, dnsName, certList) if err == nil { - certs[keyID] = rootSigned.Keys[keyID] + validKeys[keyID] = rootSigned.Signed.Keys[keyID] } } - if len(certs) < 1 { + if len(validKeys) < 1 { return errors.New("could not validate the path to a trusted root") } - _, err = signed.VerifyRoot(root, 0, certs, 1) + _, err = signed.VerifyRoot(root, 0, validKeys, 1) return err } From 8d9d2f1dde70666833dce5fea92d8e6988511653 Mon Sep 17 00:00:00 2001 From: Diogo Monica Date: Wed, 15 Jul 2015 22:32:05 -0700 Subject: [PATCH 04/17] Add TOFU logic Signed-off-by: Diogo Monica --- keystoremanager/keystoremanager.go | 40 +++++++++++++++++++----------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/keystoremanager/keystoremanager.go b/keystoremanager/keystoremanager.go index a19db210ab..0737ef45d4 100644 --- a/keystoremanager/keystoremanager.go +++ b/keystoremanager/keystoremanager.go @@ -193,9 +193,10 @@ func (km *KeyStoreManager) ValidateRoot(root *data.Signed, dnsName string) error return err } + // iterate over every keyID for the root role inside of roots.json validKeys := make(map[string]*data.PublicKey) for _, keyID := range rootSigned.Signed.Roles["root"].KeyIDs { - // Decode all the x509 certificates that were bundled with this + // decode all the x509 certificates that were bundled with this // specific root key decodedCerts, err := trustmanager.LoadCertBundleFromPEM([]byte(rootSigned.Signed.Keys[keyID].Public())) if err != nil { @@ -203,16 +204,16 @@ func (km *KeyStoreManager) ValidateRoot(root *data.Signed, dnsName string) error continue } - // Get all non-CA certificates in the decoded certificates + // get all non-CA certificates in the decoded certificates leafCerts := trustmanager.GetLeafCerts(decodedCerts) - // If we got no leaf certificates or we got more than one, fail + // gf we got no leaf certificates or we got more than one, fail if len(leafCerts) != 1 { logrus.Debugf("error while parsing root certificate with keyID: %s, %v", keyID, err) continue } - // Get the ID of the leaf certificate + // get the ID of the leaf certificate leafCert := leafCerts[0] leafID, err := trustmanager.FingerprintCert(leafCert) if err != nil { @@ -220,18 +221,28 @@ func (km *KeyStoreManager) ValidateRoot(root *data.Signed, dnsName string) error continue } - // Check to see if there is an exact match of this certificate. - // Checking the CommonName is not required since ID is calculated over - // Cert.Raw. It's included to prevent breaking logic with changes of how the - // ID gets computed. - _, err = km.trustedCertificateStore.GetCertificateByKeyID(leafID) - if err == nil && leafCert.Subject.CommonName == dnsName { - validKeys[keyID] = rootSigned.Signed.Keys[keyID] + // retrieve all the trusted certificates that match this dns Name + certsForCN, err := km.certificateStore.GetCertificatesByCN(dnsName) + + // if there are no certificates with this CN, lets TOFU! + // note that this logic should only exist in docker 1.8 + if len(certsForCN) == 0 { + km.certificateStore.AddCert(leafCert) + certsForCN = append(certsForCN, leafCert) } - // Check to see if this leafCertificate has a chain to one of the Root // CAs of our CA Store. - certList := []*x509.Certificate{leafCert} - err = trustmanager.Verify(km.trustedCAStore, dnsName, certList) + // iterate over all known certificates for this CN and see if any are trusted + for _, cert := range certsForCN { + // Check to see if there is an exact match of this certificate. + certID, err := trustmanager.FingerprintCert(cert) + if err == nil && certID == leafID { + validKeys[keyID] = rootSigned.Signed.Keys[keyID] + } + } + + // Check to see if this leafCertificate has a chain to one of the Root + // CAs of our CA Store. + err = trustmanager.Verify(km.caStore, dnsName, decodedCerts) if err == nil { validKeys[keyID] = rootSigned.Signed.Keys[keyID] } @@ -241,6 +252,7 @@ func (km *KeyStoreManager) ValidateRoot(root *data.Signed, dnsName string) error return errors.New("could not validate the path to a trusted root") } + // TODO(david): change hardcoded minversion on TUF. _, err = signed.VerifyRoot(root, 0, validKeys, 1) return err From 5a7797690136edb0f086bcbe43b5fe05de57c800 Mon Sep 17 00:00:00 2001 From: Diogo Monica Date: Wed, 15 Jul 2015 23:22:23 -0700 Subject: [PATCH 05/17] Rebasing from master Signed-off-by: Diogo Monica --- trustmanager/x509filestore_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/trustmanager/x509filestore_test.go b/trustmanager/x509filestore_test.go index 484dbc9761..37db73edaf 100644 --- a/trustmanager/x509filestore_test.go +++ b/trustmanager/x509filestore_test.go @@ -146,7 +146,7 @@ func TestInexistentGetCertificateByKeyIDX509FileStore(t *testing.T) { t.Fatalf("failed to load certificate from file: %v", err) } - _, err = store.GetCertificateByKeyID("4d06afd30b8bed131d2a84c97d00b37f422021598bfae34285ce98e77b708b5a") + _, err = store.GetCertificateByCertID("4d06afd30b8bed131d2a84c97d00b37f422021598bfae34285ce98e77b708b5a") if err == nil { t.Fatalf("no error returned for inexistent certificate") } @@ -181,7 +181,7 @@ func TestGetCertificateByKeyIDX509FileStore(t *testing.T) { } // Tries to retrieve cert by Subject Key IDs - _, err = store.GetCertificateByKeyID(keyID) + _, err = store.GetCertificateByCertID(keyID) if err != nil { t.Fatalf("expected certificate in store: %s", keyID) } From 5354c3b191f38a6a1e7a7a3f46458bde4474828f Mon Sep 17 00:00:00 2001 From: Diogo Monica Date: Wed, 15 Jul 2015 23:22:59 -0700 Subject: [PATCH 06/17] Added logic for root certificate rotation Signed-off-by: Diogo Monica --- keystoremanager/keystoremanager.go | 37 +++++++++++++++++++++++-- keystoremanager/keystoremanager_test.go | 1 + 2 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 keystoremanager/keystoremanager_test.go diff --git a/keystoremanager/keystoremanager.go b/keystoremanager/keystoremanager.go index 0737ef45d4..cba1e85dea 100644 --- a/keystoremanager/keystoremanager.go +++ b/keystoremanager/keystoremanager.go @@ -253,7 +253,40 @@ func (km *KeyStoreManager) ValidateRoot(root *data.Signed, dnsName string) error } // TODO(david): change hardcoded minversion on TUF. - _, err = signed.VerifyRoot(root, 0, validKeys, 1) + newRootKey, err := signed.VerifyRoot(root, 0, validKeys, 1) + if err != nil { + return err + } - return err + // VerifyRoot returns a non-nil value if there is a root key rotation happening + // if this happens, we should replace the old root of trust with the new one + if newRootKey != nil { + // retrieve all the certificates associated with the new root key + keyID := newRootKey.ID() + decodedCerts, err := trustmanager.LoadCertBundleFromPEM([]byte(rootSigned.Signed.Keys[keyID].Public())) + if err != nil { + logrus.Debugf("error while parsing root certificate with keyID: %s, %v", keyID, err) + return err + } + + // adds trust on the certificate of the new root key + leafCerts := trustmanager.GetLeafCerts(decodedCerts) + err = km.certificateStore.AddCert(leafCerts[0]) + if err != nil { + return err + } + + // iterate over all old valid keys and removes the associated certificates + // were previously valid + for _, key := range validKeys { + cert, err := km.certificateStore.GetCertificateByCertID(key.ID()) + if err != nil { + return err + } + // Remove the old certificate + km.certificateStore.RemoveCert(cert) + } + } + + return nil } diff --git a/keystoremanager/keystoremanager_test.go b/keystoremanager/keystoremanager_test.go new file mode 100644 index 0000000000..d713310eaa --- /dev/null +++ b/keystoremanager/keystoremanager_test.go @@ -0,0 +1 @@ +package keystoremanager From e72141f38accef280011805e078df242899739f7 Mon Sep 17 00:00:00 2001 From: Diogo Monica Date: Thu, 16 Jul 2015 11:48:17 -0700 Subject: [PATCH 07/17] Simplifying the root rotation logic Signed-off-by: Diogo Monica --- keystoremanager/keystoremanager.go | 56 +++++++++++++++--------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/keystoremanager/keystoremanager.go b/keystoremanager/keystoremanager.go index cba1e85dea..6d41a9fabe 100644 --- a/keystoremanager/keystoremanager.go +++ b/keystoremanager/keystoremanager.go @@ -193,27 +193,28 @@ func (km *KeyStoreManager) ValidateRoot(root *data.Signed, dnsName string) error return err } - // iterate over every keyID for the root role inside of roots.json + // Iterate over every keyID for the root role inside of roots.json validKeys := make(map[string]*data.PublicKey) + allCerts := make(map[string]*x509.Certificate) for _, keyID := range rootSigned.Signed.Roles["root"].KeyIDs { - // decode all the x509 certificates that were bundled with this - // specific root key + // Decode all the x509 certificates that were bundled with this + // Specific root key decodedCerts, err := trustmanager.LoadCertBundleFromPEM([]byte(rootSigned.Signed.Keys[keyID].Public())) if err != nil { logrus.Debugf("error while parsing root certificate with keyID: %s, %v", keyID, err) continue } - // get all non-CA certificates in the decoded certificates + // Get all non-CA certificates in the decoded certificates leafCerts := trustmanager.GetLeafCerts(decodedCerts) - // gf we got no leaf certificates or we got more than one, fail + // If we got no leaf certificates or we got more than one, fail if len(leafCerts) != 1 { logrus.Debugf("error while parsing root certificate with keyID: %s, %v", keyID, err) continue } - // get the ID of the leaf certificate + // Get the ID of the leaf certificate leafCert := leafCerts[0] leafID, err := trustmanager.FingerprintCert(leafCert) if err != nil { @@ -221,17 +222,20 @@ func (km *KeyStoreManager) ValidateRoot(root *data.Signed, dnsName string) error continue } - // retrieve all the trusted certificates that match this dns Name + // Add all the valid leafs to the certificates map so we can refer to them later + allCerts[leafID] = leafCert + + // Retrieve all the trusted certificates that match this dns Name certsForCN, err := km.certificateStore.GetCertificatesByCN(dnsName) - // if there are no certificates with this CN, lets TOFU! - // note that this logic should only exist in docker 1.8 + // If there are no certificates with this CN, lets TOFU! + // Note that this logic should only exist in docker 1.8 if len(certsForCN) == 0 { km.certificateStore.AddCert(leafCert) certsForCN = append(certsForCN, leafCert) } - // iterate over all known certificates for this CN and see if any are trusted + // Iterate over all known certificates for this CN and see if any are trusted for _, cert := range certsForCN { // Check to see if there is an exact match of this certificate. certID, err := trustmanager.FingerprintCert(cert) @@ -258,33 +262,29 @@ func (km *KeyStoreManager) ValidateRoot(root *data.Signed, dnsName string) error return err } - // VerifyRoot returns a non-nil value if there is a root key rotation happening - // if this happens, we should replace the old root of trust with the new one + // VerifyRoot returns a non-nil value if there is a root key rotation happening. + // If this happens, we should replace the old root of trust with the new one if newRootKey != nil { - // retrieve all the certificates associated with the new root key - keyID := newRootKey.ID() - decodedCerts, err := trustmanager.LoadCertBundleFromPEM([]byte(rootSigned.Signed.Keys[keyID].Public())) + // Retrieve the certificate associated with the new root key and trust it + newRootKeyCert := allCerts[newRootKey.ID()] + err := km.certificateStore.AddCert(newRootKeyCert) if err != nil { - logrus.Debugf("error while parsing root certificate with keyID: %s, %v", keyID, err) + logrus.Debugf("error while adding new root certificate with keyID: %s, %v", newRootKey.ID(), err) return err } - // adds trust on the certificate of the new root key - leafCerts := trustmanager.GetLeafCerts(decodedCerts) - err = km.certificateStore.AddCert(leafCerts[0]) - if err != nil { - return err - } + // Remove the new root certificate from the certificate mapping so we + // can remove trust from all of the remaining ones + delete(allCerts, newRootKey.ID()) - // iterate over all old valid keys and removes the associated certificates - // were previously valid - for _, key := range validKeys { - cert, err := km.certificateStore.GetCertificateByCertID(key.ID()) + // Iterate over all old valid certificates and remove them, essentially + // finishing the rotation of the currently trusted root certificate + for _, cert := range allCerts { + err := km.certificateStore.RemoveCert(cert) if err != nil { + logrus.Debugf("error while removing old root certificate: %v", err) return err } - // Remove the old certificate - km.certificateStore.RemoveCert(cert) } } From f5873eef8ce1db3de4c59d123153957e48dae787 Mon Sep 17 00:00:00 2001 From: Diogo Monica Date: Thu, 16 Jul 2015 16:08:06 -0700 Subject: [PATCH 08/17] Adding RemoveAll to X509FileStore and correcting functions caller Signed-off-by: Diogo Monica --- .../client_root_validation_test.go | 0 trustmanager/x509filestore.go | 34 ++++++++++++------ trustmanager/x509memstore.go | 35 +++++++++++++------ trustmanager/x509store.go | 1 + 4 files changed, 50 insertions(+), 20 deletions(-) rename keystoremanager/keystoremanager_test.go => client/client_root_validation_test.go (100%) diff --git a/keystoremanager/keystoremanager_test.go b/client/client_root_validation_test.go similarity index 100% rename from keystoremanager/keystoremanager_test.go rename to client/client_root_validation_test.go diff --git a/trustmanager/x509filestore.go b/trustmanager/x509filestore.go index d6c23647ea..2d0b275fb2 100644 --- a/trustmanager/x509filestore.go +++ b/trustmanager/x509filestore.go @@ -50,7 +50,7 @@ func newX509FileStore(directory string, validate func(*x509.Certificate) bool) ( } // AddCert creates a filename for a given cert and adds a certificate with that name -func (s X509FileStore) AddCert(cert *x509.Certificate) error { +func (s *X509FileStore) AddCert(cert *x509.Certificate) error { if cert == nil { return errors.New("adding nil Certificate to X509Store") } @@ -69,7 +69,7 @@ func (s X509FileStore) AddCert(cert *x509.Certificate) error { // addNamedCert allows adding a certificate while controling the filename it gets // stored under. If the file does not exist on disk, saves it. -func (s X509FileStore) addNamedCert(cert *x509.Certificate) error { +func (s *X509FileStore) addNamedCert(cert *x509.Certificate) error { fileName, certID, err := fileName(cert) if err != nil { return err @@ -108,7 +108,7 @@ func (s X509FileStore) addNamedCert(cert *x509.Certificate) error { } // RemoveCert removes a certificate from a X509FileStore. -func (s X509FileStore) RemoveCert(cert *x509.Certificate) error { +func (s *X509FileStore) RemoveCert(cert *x509.Certificate) error { if cert == nil { return errors.New("removing nil Certificate from X509Store") } @@ -141,6 +141,20 @@ func (s X509FileStore) RemoveCert(cert *x509.Certificate) error { 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 { @@ -152,7 +166,7 @@ func (s X509FileStore) AddCertFromPEM(pemBytes []byte) error { } // AddCertFromFile tries to adds a X509 certificate to the store given a filename -func (s X509FileStore) AddCertFromFile(filename string) error { +func (s *X509FileStore) AddCertFromFile(filename string) error { cert, err := LoadCertFromFile(filename) if err != nil { return err @@ -162,7 +176,7 @@ func (s X509FileStore) AddCertFromFile(filename string) error { } // GetCertificates returns an array with all of the current X509 Certificates. -func (s X509FileStore) GetCertificates() []*x509.Certificate { +func (s *X509FileStore) GetCertificates() []*x509.Certificate { certs := make([]*x509.Certificate, len(s.fingerprintMap)) i := 0 for _, v := range s.fingerprintMap { @@ -174,7 +188,7 @@ func (s X509FileStore) GetCertificates() []*x509.Certificate { // GetCertificatePool returns an x509 CertPool loaded with all the certificates // in the store. -func (s X509FileStore) GetCertificatePool() *x509.CertPool { +func (s *X509FileStore) GetCertificatePool() *x509.CertPool { pool := x509.NewCertPool() for _, v := range s.fingerprintMap { @@ -184,12 +198,12 @@ func (s X509FileStore) GetCertificatePool() *x509.CertPool { } // GetCertificateByCertID returns the certificate that matches a certain certID -func (s X509FileStore) GetCertificateByCertID(certID string) (*x509.Certificate, error) { +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) { +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") @@ -205,7 +219,7 @@ func (s X509FileStore) getCertificateByCertID(certID CertID) (*x509.Certificate, // GetCertificatesByCN returns all the certificates that match a specific // CommonName -func (s X509FileStore) GetCertificatesByCN(cn string) ([]*x509.Certificate, error) { +func (s *X509FileStore) GetCertificatesByCN(cn string) ([]*x509.Certificate, error) { var certs []*x509.Certificate if ids, ok := s.nameMap[cn]; ok { for _, v := range ids { @@ -228,7 +242,7 @@ func (s X509FileStore) GetCertificatesByCN(cn string) ([]*x509.Certificate, erro // 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) { +func (s *X509FileStore) GetVerifyOptions(dnsName string) (x509.VerifyOptions, error) { // If we have no Certificates loaded return error (we don't want to rever to using // system CAs). if len(s.fingerprintMap) == 0 { diff --git a/trustmanager/x509memstore.go b/trustmanager/x509memstore.go index db5b60b100..a169cd2f08 100644 --- a/trustmanager/x509memstore.go +++ b/trustmanager/x509memstore.go @@ -37,7 +37,7 @@ func NewX509FilteredMemStore(validate func(*x509.Certificate) bool) *X509MemStor } // AddCert adds a certificate to the store -func (s X509MemStore) AddCert(cert *x509.Certificate) error { +func (s *X509MemStore) AddCert(cert *x509.Certificate) error { if cert == nil { return errors.New("adding nil Certificate to X509Store") } @@ -59,7 +59,7 @@ func (s X509MemStore) AddCert(cert *x509.Certificate) error { } // RemoveCert removes a certificate from a X509MemStore. -func (s X509MemStore) RemoveCert(cert *x509.Certificate) error { +func (s *X509MemStore) RemoveCert(cert *x509.Certificate) error { if cert == nil { return errors.New("removing nil Certificate to X509Store") } @@ -84,8 +84,23 @@ func (s X509MemStore) RemoveCert(cert *x509.Certificate) error { 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 + } + } + + s.fingerprintMap = make(map[CertID]*x509.Certificate) + s.nameMap = make(map[string][]CertID) + + return nil +} + // AddCertFromPEM adds a certificate to the store from a PEM blob -func (s X509MemStore) AddCertFromPEM(pemBytes []byte) error { +func (s *X509MemStore) AddCertFromPEM(pemBytes []byte) error { cert, err := LoadCertFromPEM(pemBytes) if err != nil { return err @@ -94,7 +109,7 @@ func (s X509MemStore) AddCertFromPEM(pemBytes []byte) error { } // AddCertFromFile tries to adds a X509 certificate to the store given a filename -func (s X509MemStore) AddCertFromFile(originFilname string) error { +func (s *X509MemStore) AddCertFromFile(originFilname string) error { cert, err := LoadCertFromFile(originFilname) if err != nil { return err @@ -104,7 +119,7 @@ func (s X509MemStore) AddCertFromFile(originFilname string) error { } // GetCertificates returns an array with all of the current X509 Certificates. -func (s X509MemStore) GetCertificates() []*x509.Certificate { +func (s *X509MemStore) GetCertificates() []*x509.Certificate { certs := make([]*x509.Certificate, len(s.fingerprintMap)) i := 0 for _, v := range s.fingerprintMap { @@ -116,7 +131,7 @@ func (s X509MemStore) GetCertificates() []*x509.Certificate { // GetCertificatePool returns an x509 CertPool loaded with all the certificates // in the store. -func (s X509MemStore) GetCertificatePool() *x509.CertPool { +func (s *X509MemStore) GetCertificatePool() *x509.CertPool { pool := x509.NewCertPool() for _, v := range s.fingerprintMap { @@ -126,12 +141,12 @@ func (s X509MemStore) GetCertificatePool() *x509.CertPool { } // GetCertificateByCertID returns the certificate that matches a certain certID -func (s X509MemStore) GetCertificateByCertID(certID string) (*x509.Certificate, error) { +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) { +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") @@ -147,7 +162,7 @@ func (s X509MemStore) getCertificateByCertID(certID CertID) (*x509.Certificate, // GetCertificatesByCN returns all the certificates that match a specific // CommonName -func (s X509MemStore) GetCertificatesByCN(cn string) ([]*x509.Certificate, error) { +func (s *X509MemStore) GetCertificatesByCN(cn string) ([]*x509.Certificate, error) { var certs []*x509.Certificate if ids, ok := s.nameMap[cn]; ok { for _, v := range ids { @@ -170,7 +185,7 @@ func (s X509MemStore) GetCertificatesByCN(cn string) ([]*x509.Certificate, error // 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) { +func (s *X509MemStore) GetVerifyOptions(dnsName string) (x509.VerifyOptions, error) { // If we have no Certificates loaded return error (we don't want to rever to using // system CAs). if len(s.fingerprintMap) == 0 { diff --git a/trustmanager/x509store.go b/trustmanager/x509store.go index 7c15456b01..bb9cc01c39 100644 --- a/trustmanager/x509store.go +++ b/trustmanager/x509store.go @@ -14,6 +14,7 @@ type X509Store interface { 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 From 3ec4f1d7f40d68f11accdf383cbfd30571aa9b4a Mon Sep 17 00:00:00 2001 From: Diogo Monica Date: Thu, 16 Jul 2015 16:16:24 -0700 Subject: [PATCH 09/17] Adding RemoveKey and Test Signed-off-by: Diogo Monica --- trustmanager/keyfilestore.go | 5 ++++ trustmanager/keyfilestore_test.go | 50 +++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/trustmanager/keyfilestore.go b/trustmanager/keyfilestore.go index 0b6c471e41..65d2837026 100644 --- a/trustmanager/keyfilestore.go +++ b/trustmanager/keyfilestore.go @@ -162,3 +162,8 @@ func listKeys(s LimitedFileStore) []string { } return keyIDList } + +// RemoveKey removes the key from the keyfilestore +func (s *KeyFileStore) RemoveKey(name string) error { + return s.Remove(name) +} diff --git a/trustmanager/keyfilestore_test.go b/trustmanager/keyfilestore_test.go index 55cca4968b..05e1b50b54 100644 --- a/trustmanager/keyfilestore_test.go +++ b/trustmanager/keyfilestore_test.go @@ -251,3 +251,53 @@ func TestGetDecryptedWithInvalidPassphrase(t *testing.T) { t.Fatalf("expected error while decrypting the content due to invalid passphrase") } } + +func TestRemoveKey(t *testing.T) { + testName := "docker.com/notary/root" + testExt := "key" + + // Temporary directory where test files will be created + tempBaseDir, err := ioutil.TempDir("", "notary-test-") + if err != nil { + t.Fatalf("failed to create a temporary directory: %v", err) + } + defer os.RemoveAll(tempBaseDir) + + // Since we're generating this manually we need to add the extension '.' + expectedFilePath := filepath.Join(tempBaseDir, testName+"."+testExt) + + // Create our store + store, err := NewKeyFileStore(tempBaseDir) + if err != nil { + t.Fatalf("failed to create new key filestore: %v", err) + } + + privKey, err := GenerateRSAKey(rand.Reader, 512) + if err != nil { + t.Fatalf("could not generate private key: %v", err) + } + + // Call the AddKey function + err = store.AddKey(testName, privKey) + if err != nil { + t.Fatalf("failed to add file to store: %v", err) + } + + // Check to see if file exists + _, err = ioutil.ReadFile(expectedFilePath) + if err != nil { + t.Fatalf("expected file not found: %v", err) + } + + // Call remove key + err = store.RemoveKey(testName) + if err != nil { + t.Fatalf("unable to remove key: %v", err) + } + + // Check to see if file still exists + _, err = ioutil.ReadFile(expectedFilePath) + if err == nil { + t.Fatalf("file should not exist %s", expectedFilePath) + } +} From 2c9a0d63318fd4b8ce4afda3ee6cc86758dffe55 Mon Sep 17 00:00:00 2001 From: Diogo Monica Date: Thu, 16 Jul 2015 16:26:35 -0700 Subject: [PATCH 10/17] Adding tests to RemoveAll in X509Stores Signed-off-by: Diogo Monica --- trustmanager/x509filestore_test.go | 46 ++++++++++++++++++++++++++++++ trustmanager/x509memstore_test.go | 41 ++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/trustmanager/x509filestore_test.go b/trustmanager/x509filestore_test.go index 37db73edaf..8d4794fc4f 100644 --- a/trustmanager/x509filestore_test.go +++ b/trustmanager/x509filestore_test.go @@ -135,6 +135,52 @@ func TestRemoveCertX509FileStore(t *testing.T) { } } +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 1 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 { diff --git a/trustmanager/x509memstore_test.go b/trustmanager/x509memstore_test.go index 9560208bb1..6fdd1acc0a 100644 --- a/trustmanager/x509memstore_test.go +++ b/trustmanager/x509memstore_test.go @@ -106,6 +106,47 @@ func TestRemoveCert(t *testing.T) { } } +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 1 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") From 1c10f2233d4c84b1ab0182e5e94727956c18bffa Mon Sep 17 00:00:00 2001 From: Diogo Monica Date: Thu, 16 Jul 2015 18:05:13 -0700 Subject: [PATCH 11/17] Add better comments, debug code and errors to ValidateRoot Signed-off-by: Diogo Monica --- keystoremanager/keystoremanager.go | 55 +++++++++++++++++------------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/keystoremanager/keystoremanager.go b/keystoremanager/keystoremanager.go index 6d41a9fabe..4c931c6c07 100644 --- a/keystoremanager/keystoremanager.go +++ b/keystoremanager/keystoremanager.go @@ -34,6 +34,15 @@ const ( rsaRootKeySize = 4096 // Used for new root keys ) +var ( + // ErrValidationFail is returned when there is no trusted certificate in any of the + // root keys available in the roots.json + ErrValidationFail = errors.New("could not validate the path to a trusted root") + // ErrRootRotationFail is returned when we fail to do a full root key rotation + // by either failing to add the new root certificate, or delete the old ones + ErrRootRotationFail = errors.New("could not rotate trust to a new trusted root") +) + // NewKeyStoreManager returns an initialized KeyStoreManager, or an error // if it fails to create the KeyFileStores or load certificates func NewKeyStoreManager(baseDir string) (*KeyStoreManager, error) { @@ -164,30 +173,20 @@ attempts to validate the certificate by first checking for an exact match on the certificate store, and subsequently trying to find a valid chain on the trustedCAStore. -When this is being used with a notary repository, the dnsName parameter should -be the GUN associated with the repository. +Currently this method operates on a Trust On First Use (TOFU) model: if we +have never seen a certificate for a particular CN, we trust it. If later we see +a different certificate for that certificate, we return an ErrValidationFailed error. -Example TUF Content for root role: -"roles" : { - "root" : { - "threshold" : 1, - "keyids" : [ - "e6da5c303d572712a086e669ecd4df7b785adfc844e0c9a7b1f21a7dfc477a38" - ] - }, - ... -} +Note that since we only allow trust data to be downloaded over an HTTPS channel +we are using the current web-of-trust to validate the first download of the certificate +adding an extra layer of security over the normal (SSH style) trust model. +We shall call this: TOFUS. -Example TUF Content for root key: -"e6da5c303d572712a086e669ecd4df7b785adfc844e0c9a7b1f21a7dfc477a38" : { - "keytype" : "RSA", - "keyval" : { - "private" : "", - "public" : "Base64-encoded, PEM encoded x509 Certificate" - } -} +ValidateRoot also supports root key rotation, trusting a new certificate that has +been included in the roots.json, and removing trust in the old one. */ func (km *KeyStoreManager) ValidateRoot(root *data.Signed, dnsName string) error { + logrus.Debugf("entered ValidateRoot with dns: %s", dnsName) rootSigned, err := data.RootFromSigned(root) if err != nil { return err @@ -196,6 +195,7 @@ func (km *KeyStoreManager) ValidateRoot(root *data.Signed, dnsName string) error // Iterate over every keyID for the root role inside of roots.json validKeys := make(map[string]*data.PublicKey) allCerts := make(map[string]*x509.Certificate) + logrus.Debugf("found the following root keys in roots.json: %v", rootSigned.Signed.Keys) for _, keyID := range rootSigned.Signed.Roles["root"].KeyIDs { // Decode all the x509 certificates that were bundled with this // Specific root key @@ -228,11 +228,12 @@ func (km *KeyStoreManager) ValidateRoot(root *data.Signed, dnsName string) error // Retrieve all the trusted certificates that match this dns Name certsForCN, err := km.certificateStore.GetCertificatesByCN(dnsName) - // If there are no certificates with this CN, lets TOFU! + // If there are no certificates with this CN, lets TOFUS! // Note that this logic should only exist in docker 1.8 if len(certsForCN) == 0 { km.certificateStore.AddCert(leafCert) certsForCN = append(certsForCN, leafCert) + logrus.Debugf("using TOFUS on %s with keyID: %s", dnsName, leafID) } // Iterate over all known certificates for this CN and see if any are trusted @@ -241,6 +242,7 @@ func (km *KeyStoreManager) ValidateRoot(root *data.Signed, dnsName string) error certID, err := trustmanager.FingerprintCert(cert) if err == nil && certID == leafID { validKeys[keyID] = rootSigned.Signed.Keys[keyID] + logrus.Debugf("found an exact match for %s with keyID: %s", dnsName, keyID) } } @@ -249,11 +251,13 @@ func (km *KeyStoreManager) ValidateRoot(root *data.Signed, dnsName string) error err = trustmanager.Verify(km.caStore, dnsName, decodedCerts) if err == nil { validKeys[keyID] = rootSigned.Signed.Keys[keyID] + logrus.Debugf("found a CA path for %s with keyID: %s", dnsName, keyID) } } if len(validKeys) < 1 { - return errors.New("could not validate the path to a trusted root") + logrus.Debugf("wasn't able to trust any of the root keys") + return ErrValidationFail } // TODO(david): change hardcoded minversion on TUF. @@ -265,12 +269,13 @@ func (km *KeyStoreManager) ValidateRoot(root *data.Signed, dnsName string) error // VerifyRoot returns a non-nil value if there is a root key rotation happening. // If this happens, we should replace the old root of trust with the new one if newRootKey != nil { + logrus.Debugf("got a new root key to rotate to: %s", newRootKey.ID()) // Retrieve the certificate associated with the new root key and trust it newRootKeyCert := allCerts[newRootKey.ID()] err := km.certificateStore.AddCert(newRootKeyCert) if err != nil { logrus.Debugf("error while adding new root certificate with keyID: %s, %v", newRootKey.ID(), err) - return err + return ErrRootRotationFail } // Remove the new root certificate from the certificate mapping so we @@ -283,10 +288,12 @@ func (km *KeyStoreManager) ValidateRoot(root *data.Signed, dnsName string) error err := km.certificateStore.RemoveCert(cert) if err != nil { logrus.Debugf("error while removing old root certificate: %v", err) - return err + return ErrRootRotationFail } + logrus.Debugf("removed trust from old root certificate") } } + logrus.Debugf("Root validation succeeded") return nil } From 17c9fcd91126abd878e7b58cfcd5c721bbc46376 Mon Sep 17 00:00:00 2001 From: Diogo Monica Date: Thu, 16 Jul 2015 18:05:48 -0700 Subject: [PATCH 12/17] Added basic tests for ValidateRoot Signed-off-by: Diogo Monica --- client/client_root_validation_test.go | 130 ++++++++++++++++++++- fixtures/self-signed_docker.com-notary.crt | 12 ++ 2 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 fixtures/self-signed_docker.com-notary.crt diff --git a/client/client_root_validation_test.go b/client/client_root_validation_test.go index d713310eaa..7385818efa 100644 --- a/client/client_root_validation_test.go +++ b/client/client_root_validation_test.go @@ -1 +1,129 @@ -package keystoremanager +package client + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "os" + "path/filepath" + "testing" + + "github.com/Sirupsen/logrus" + "github.com/docker/notary/keystoremanager" + "github.com/endophage/gotuf/data" + "github.com/stretchr/testify/assert" +) + +// 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.DebugLevel) + validateRootSuccessfully(t, data.ECDSAKey) +} + +func validateRootSuccessfully(t *testing.T, rootType data.KeyAlgorithm) { + // Temporary directory where test files will be created + tempBaseDir, err := ioutil.TempDir("", "notary-test-") + defer os.RemoveAll(tempBaseDir) + + assert.NoError(t, err, "failed to create a temporary directory: %s", err) + + gun := "docker.com/notary" + + ts, mux := createTestServer(t) + defer ts.Close() + + repo, err := NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport) + assert.NoError(t, err, "error creating repository: %s", err) + + rootKeyID, err := repo.KeyStoreManager.GenRootKey(rootType.String(), "passphrase") + assert.NoError(t, err, "error generating root key: %s", err) + + rootCryptoService, err := repo.KeyStoreManager.GetRootCryptoService(rootKeyID, "passphrase") + assert.NoError(t, err, "error retrieving root key: %s", err) + + err = repo.Initialize(rootCryptoService) + assert.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.KeyStoreManager.CertificateStore().GetCertificates() + assert.Len(t, allCerts, 1) + + // Now test ListTargets. In preparation, we need to expose some signed + // metadata files on the internal HTTP server. + + var tempKey data.PrivateKey + json.Unmarshal([]byte(timestampECDSAKeyJSON), &tempKey) + + repo.KeyStoreManager.NonRootKeyStore().AddKey(filepath.Join(filepath.FromSlash(gun), tempKey.ID()), &tempKey) + + mux.HandleFunc("/v2/docker.com/notary/_trust/tuf/root.json", func(w http.ResponseWriter, r *http.Request) { + rootJSONFile := filepath.Join(tempBaseDir, "tuf", filepath.FromSlash(gun), "metadata", "root.json") + rootFileBytes, err := ioutil.ReadFile(rootJSONFile) + assert.NoError(t, err) + fmt.Fprint(w, string(rootFileBytes)) + }) + + // Because ListTargets will clear this + savedTUFRepo := repo.tufRepo + + mux.HandleFunc("/v2/docker.com/notary/_trust/tuf/timestamp.json", func(w http.ResponseWriter, r *http.Request) { + signedTimestamp, err := savedTUFRepo.SignTimestamp(data.DefaultExpires("timestamp"), nil) + assert.NoError(t, err) + timestampJSON, _ := json.Marshal(signedTimestamp) + fmt.Fprint(w, string(timestampJSON)) + }) + + mux.HandleFunc("/v2/docker.com/notary/_trust/tuf/snapshot.json", func(w http.ResponseWriter, r *http.Request) { + signedSnapshot, err := savedTUFRepo.SignSnapshot(data.DefaultExpires("snapshot"), nil) + assert.NoError(t, err) + snapshotJSON, _ := json.Marshal(signedSnapshot) + fmt.Fprint(w, string(snapshotJSON)) + }) + + mux.HandleFunc("/v2/docker.com/notary/_trust/tuf/targets.json", func(w http.ResponseWriter, r *http.Request) { + signedTargets, err := savedTUFRepo.SignTargets("targets", data.DefaultExpires("targets"), nil) + assert.NoError(t, err) + targetsJSON, _ := json.Marshal(signedTargets) + fmt.Fprint(w, string(targetsJSON)) + }) + + _, err = repo.ListTargets() + assert.NoError(t, err) + + // + // Test TOFU logic. We remove all certs and expect a new one to be added after ListTargets + // + err = repo.KeyStoreManager.CertificateStore().RemoveAll() + assert.NoError(t, err) + assert.Len(t, repo.KeyStoreManager.CertificateStore().GetCertificates(), 0) + + // This list targets is expected to succeed and the certificate store to have the new certificate + _, err = repo.ListTargets() + assert.NoError(t, err) + assert.Len(t, repo.KeyStoreManager.CertificateStore().GetCertificates(), 1) + + // + // Test certificate mismatch logic. We remove all certs, and a different cert to the + // same CN, and expect ValidateRoot to fail + // + + // First, remove all certs + err = repo.KeyStoreManager.CertificateStore().RemoveAll() + assert.NoError(t, err) + assert.Len(t, repo.KeyStoreManager.CertificateStore().GetCertificates(), 0) + + // Add a previously generated certificate with CN=docker.com/notary + err = repo.KeyStoreManager.CertificateStore().AddCertFromFile("../fixtures/self-signed_docker.com-notary.crt") + assert.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 TOFU doesn't apply + _, err = repo.ListTargets() + if assert.Error(t, err, "An error was expected") { + assert.Equal(t, err, keystoremanager.ErrValidationFail) + } +} diff --git a/fixtures/self-signed_docker.com-notary.crt b/fixtures/self-signed_docker.com-notary.crt new file mode 100644 index 0000000000..901a3de8e3 --- /dev/null +++ b/fixtures/self-signed_docker.com-notary.crt @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBpDCCAUqgAwIBAgIRAIquZ7lRJj1Um030Kd7GFXgwCgYIKoZIzj0EAwIwODEa +MBgGA1UEChMRZG9ja2VyLmNvbS9ub3RhcnkxGjAYBgNVBAMTEWRvY2tlci5jb20v +bm90YXJ5MB4XDTE1MDcxNzAwMzE1NFoXDTE3MDcxNjAwMzE1NFowODEaMBgGA1UE +ChMRZG9ja2VyLmNvbS9ub3RhcnkxGjAYBgNVBAMTEWRvY2tlci5jb20vbm90YXJ5 +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEjnnozttLzYgIN5fL8ZwYbsMig0pj +HSNupVTPjDIrLUYUnoQfG6IQ0E2BMixEGnI/A9WreeXP2oz06LZ4SROMQqM1MDMw +DgYDVR0PAQH/BAQDAgCgMBMGA1UdJQQMMAoGCCsGAQUFBwMDMAwGA1UdEwEB/wQC +MAAwCgYIKoZIzj0EAwIDSAAwRQIgT9cxottjza9BBQcMsoB/Uf2JYXWgSkp9QMXT +8mG4mMICIQDMYWFdgn5u8nDeThJ+bG8Lu5nIGb/NWEOFtU0xQv913Q== +-----END CERTIFICATE----- + From 945691912a23121b39cb359545bb021655dec615 Mon Sep 17 00:00:00 2001 From: Diogo Monica Date: Thu, 16 Jul 2015 23:52:45 -0700 Subject: [PATCH 13/17] Added error type to X509FileStore Signed-off-by: Diogo Monica --- trustmanager/x509filestore.go | 4 ++-- trustmanager/x509memstore.go | 4 ++-- trustmanager/x509store.go | 12 ++++++++++++ trustmanager/x509utils.go | 4 ++++ 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/trustmanager/x509filestore.go b/trustmanager/x509filestore.go index 2d0b275fb2..909afb3c25 100644 --- a/trustmanager/x509filestore.go +++ b/trustmanager/x509filestore.go @@ -214,7 +214,7 @@ func (s *X509FileStore) getCertificateByCertID(certID CertID) (*x509.Certificate return cert, nil } - return nil, errors.New("certificate not found in Key Store") + return nil, &ErrNoCertificatesFound{query: string(certID)} } // GetCertificatesByCN returns all the certificates that match a specific @@ -233,7 +233,7 @@ func (s *X509FileStore) GetCertificatesByCN(cn string) ([]*x509.Certificate, err } } if len(certs) == 0 { - return nil, errors.New("common name not found in Key Store") + return nil, &ErrNoCertificatesFound{query: cn} } return certs, nil diff --git a/trustmanager/x509memstore.go b/trustmanager/x509memstore.go index a169cd2f08..399b050b41 100644 --- a/trustmanager/x509memstore.go +++ b/trustmanager/x509memstore.go @@ -157,7 +157,7 @@ func (s *X509MemStore) getCertificateByCertID(certID CertID) (*x509.Certificate, return cert, nil } - return nil, errors.New("certificate not found in Key Store") + return nil, &ErrNoCertificatesFound{query: string(certID)} } // GetCertificatesByCN returns all the certificates that match a specific @@ -176,7 +176,7 @@ func (s *X509MemStore) GetCertificatesByCN(cn string) ([]*x509.Certificate, erro } } if len(certs) == 0 { - return nil, errors.New("common name not found in Key Store") + return nil, &ErrNoCertificatesFound{query: cn} } return certs, nil diff --git a/trustmanager/x509store.go b/trustmanager/x509store.go index bb9cc01c39..deb993766d 100644 --- a/trustmanager/x509store.go +++ b/trustmanager/x509store.go @@ -8,6 +8,18 @@ import ( 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) +} + // X509Store is the interface for all X509Stores type X509Store interface { AddCert(cert *x509.Certificate) error diff --git a/trustmanager/x509utils.go b/trustmanager/x509utils.go index 8b0c550f9b..6b55509237 100644 --- a/trustmanager/x509utils.go +++ b/trustmanager/x509utils.go @@ -169,6 +169,10 @@ func LoadCertBundleFromPEM(pemBytes []byte) ([]*x509.Certificate, error) { } } + if len(certificates) == 0 { + return nil, fmt.Errorf("no valid certificates found") + } + return certificates, nil } From c817ba3606ebf64cc155eca01a576c185d55456d Mon Sep 17 00:00:00 2001 From: Diogo Monica Date: Thu, 16 Jul 2015 23:54:32 -0700 Subject: [PATCH 14/17] Fixed bug in Root Validation and added more test Signed-off-by: Diogo Monica --- client/client_root_validation_test.go | 147 +++++++++++++++++++- fixtures/self-signed_secure.example.com.crt | 11 ++ keystoremanager/keystoremanager.go | 17 ++- 3 files changed, 171 insertions(+), 4 deletions(-) create mode 100755 fixtures/self-signed_secure.example.com.crt diff --git a/client/client_root_validation_test.go b/client/client_root_validation_test.go index 7385818efa..4d0260b18a 100644 --- a/client/client_root_validation_test.go +++ b/client/client_root_validation_test.go @@ -1,8 +1,11 @@ package client import ( + "bytes" "encoding/json" + "errors" "fmt" + "html/template" "io/ioutil" "net/http" "os" @@ -15,12 +18,24 @@ import ( "github.com/stretchr/testify/assert" ) +type SignedRSARootTemplate struct { + RootPem string +} + +const validPEMEncodedRSARoot = `LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZLekNDQXhXZ0F3SUJBZ0lRUnlwOVFxY0pmZDNheXFkaml6OHhJREFMQmdrcWhraUc5dzBCQVFzd09ERWEKTUJnR0ExVUVDaE1SWkc5amEyVnlMbU52YlM5dWIzUmhjbmt4R2pBWUJnTlZCQU1URVdSdlkydGxjaTVqYjIwdgpibTkwWVhKNU1CNFhEVEUxTURjeE56QTJNelF5TTFvWERURTNNRGN4TmpBMk16UXlNMW93T0RFYU1CZ0dBMVVFCkNoTVJaRzlqYTJWeUxtTnZiUzl1YjNSaGNua3hHakFZQmdOVkJBTVRFV1J2WTJ0bGNpNWpiMjB2Ym05MFlYSjUKTUlJQ0lqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FnOEFNSUlDQ2dLQ0FnRUFvUWZmcnpzWW5zSDh2R2Y0Smg1NQpDajV3cmpVR3pEL3NIa2FGSHB0ako2VG9KR0p2NXlNQVB4enlJbnU1c0lvR0xKYXBuWVZCb0FVMFlnSTlxbEFjCllBNlN4YVN3Z202cnB2bW5sOFFuMHFjNmdlcjNpbnBHYVVKeWxXSHVQd1drdmNpbVFBcUhaeDJkUXRMN2c2a3AKcm1LZVRXcFdvV0x3M0pvQVVaVVZoWk1kNmEyMlpML0R2QXcrSHJvZ2J6NFhleWFoRmI5SUg0MDJ6UHhONnZnYQpKRUZURjBKaTFqdE5nME1vNHBiOVNIc01zaXcrTFpLN1NmZkhWS1B4dmQyMW0vYmlObXdzZ0V4QTNVOE9PRzhwCnV5Z2ZhY3lzNWM4K1pyWCtaRkcvY3Z3S3owazYvUWZKVTQwczZNaFh3NUMyV3R0ZFZtc0c5LzdyR0ZZakhvSUoKd2VEeXhnV2s3dnhLelJKSS91bjdjYWdESWFRc0tySlFjQ0hJR0ZSbHBJUjVUd1g3dmwzUjdjUm5jckRSTVZ2YwpWU0VHMmVzeGJ3N2p0eklwL3lwblZSeGNPbnk3SXlweWpLcVZlcVo2SGd4WnRUQlZyRjFPL2FIbzJrdmx3eVJTCkF1czRrdmg2ejMranpUbTlFemZYaVBRelk5QkVrNWdPTHhoVzlyYzZVaGxTK3BlNWxrYU4vSHlxeS9sUHVxODkKZk1yMnJyN2xmNVdGZEZuemU2V05ZTUFhVzdkTkE0TkUwZHlENTM0MjhaTFh4TlZQTDRXVTY2R2FjNmx5blE4bApyNXRQc1lJRlh6aDZGVmFSS0dRVXRXMWh6OWVjTzZZMjdSaDJKc3lpSXhnVXFrMm9veEU2OXVONDJ0K2R0cUtDCjFzOEcvN1Z0WThHREFMRkxZVG56THZzQ0F3RUFBYU0xTURNd0RnWURWUjBQQVFIL0JBUURBZ0NnTUJNR0ExVWQKSlFRTU1Bb0dDQ3NHQVFVRkJ3TURNQXdHQTFVZEV3RUIvd1FDTUFBd0N3WUpLb1pJaHZjTkFRRUxBNElDQVFCTQpPbGwzRy9YQno4aWRpTmROSkRXVWgrNXczb2ptd2FuclRCZENkcUVrMVdlbmFSNkR0Y2ZsSng2WjNmL213VjRvCmIxc2tPQVgxeVg1UkNhaEpIVU14TWljei9RMzhwT1ZlbEdQclduYzNUSkIrVktqR3lIWGxRRFZrWkZiKzQrZWYKd3RqN0huZ1hoSEZGRFNnam0zRWRNbmR2Z0RRN1NRYjRza09uQ05TOWl5WDdlWHhoRkJDWm1aTCtIQUxLQmoyQgp5aFY0SWNCRHFtcDUwNHQxNHJ4OS9KdnR5MGRHN2ZZN0k1MWdFUXBtNFMwMkpNTDV4dlRtMXhmYm9XSWhaT0RJCnN3RUFPK2VrQm9GSGJTMVE5S01QaklBdzNUckNISDh4OFhacTV6c1l0QUMxeVpIZENLYTI2YVdkeTU2QTllSGoKTzFWeHp3bWJOeVhSZW5WdUJZUCswd3IzSFZLRkc0Sko0WlpwTlp6UVcvcHFFUGdoQ1RKSXZJdWVLNjUyQnlVYwovL3N2K25YZDVmMTlMZUVTOXBmMGwyNTNORGFGWlBiNmFlZ0tmcXVXaDhxbFFCbVVRMkd6YVRMYnRtTmQyOE02Clc3aUw3dGtLWmUxWm5CejlSS2d0UHJEampXR1pJbmpqY09VOEV0VDRTTHE3a0NWRG1QczVNRDh2YUFtOTZKc0UKam1MQzNVdS80azdIaURZWDBpMG1PV2tGalpRTWRWYXRjSUY1RlBTcHB3c1NiVzhRaWRuWHQ1NFV0d3RGREVQegpscGpzN3liZVFFNzFKWGNNWm5WSUs0YmpSWHNFRlBJOThScElsRWRlZGJTVWRZQW5jTE5KUlQ3SFpCTVBHU3daCjBQTkp1Z2xubHIzc3JWemRXMWR6MnhRamR2THd4eTZtTlVGNnJiUUJXQT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K` + +const validCAPEMEncodeRSARoot = `LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlHTXpDQ0JCdWdBd0lCQWdJQkFUQU5CZ2txaGtpRzl3MEJBUXNGQURCZk1Rc3dDUVlEVlFRR0V3SlZVekVMDQpNQWtHQTFVRUNBd0NRMEV4RmpBVUJnTlZCQWNNRFZOaGJpQkdjbUZ1WTJselkyOHhEekFOQmdOVkJBb01Ca1J2DQpZMnRsY2pFYU1CZ0dBMVVFQXd3UlRtOTBZWEo1SUZSbGMzUnBibWNnUTBFd0hoY05NVFV3TnpFMk1EUXlOVEF6DQpXaGNOTWpVd056RXpNRFF5TlRBeldqQmZNUm93R0FZRFZRUUREQkZPYjNSaGNua2dWR1Z6ZEdsdVp5QkRRVEVMDQpNQWtHQTFVRUJoTUNWVk14RmpBVUJnTlZCQWNNRFZOaGJpQkdjbUZ1WTJselkyOHhEekFOQmdOVkJBb01Ca1J2DQpZMnRsY2pFTE1Ba0dBMVVFQ0F3Q1EwRXdnZ0lpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElDRHdBd2dnSUtBb0lDDQpBUUN3VlZENHBLN3o3cFhQcEpiYVoxSGc1ZVJYSWNhWXRiRlBDbk4waXF5OUhzVkVHbkVuNUJQTlNFc3VQK20wDQo1TjBxVlY3REdiMVNqaWxvTFhEMXFERHZoWFdrK2dpUzlwcHFQSFBMVlBCNGJ2enNxd0RZcnRwYnFrWXZPMFlLDQowU0wza3hQWFVGZGxrRmZndTB4amxjem0yUGhXRzNKZDhhQXRzcEwvTCtWZlBBMTNKVWFXeFNMcHVpMUluOHJoDQpnQXlRVEs2UTRPZjZHYkpZVG5BSGI1OVVvTFhTekI1QWZxaVVxNkw3bkVZWUtvUGZsUGJSQUlXTC9VQm0wYytIDQpvY21zNzA2UFlwbVBTMlJRdjNpT0dtbm45aEVWcDNQNmpxN1dBZXZiQTRhWUd4NUVzYlZ0WUFCcUpCYkZXQXV3DQp3VEdSWW16bjBNajBlVE1nZTl6dFlCMi8yc3hkVGU2dWhtRmdwVVhuZ0RxSkk1TzlOM3pQZnZsRUltQ2t5M0hNDQpqSm9MN2c1c21xWDlvMVArRVNMaDBWWnpoaDdJRFB6UVRYcGNQSVMvNnowbDIyUUdrSy8xTjFQYUFEYVVIZExMDQp2U2F2M3kyQmFFbVB2ZjJma1pqOHlQNWVZZ2k3Q3c1T05oSExEWUhGY2w5Wm0veXdtZHhISkVUejluZmdYbnNXDQpITnhEcXJrQ1ZPNDZyL3U2clNyVXQ2aHIzb2RkSkc4czhKbzA2ZWFydzZYVTNNek0rM2dpd2tLMFNTTTN1UlBxDQo0QXNjUjFUditFMzFBdU9BbWpxWVFvVDI5Yk1JeG9TemVsamovWW5lZHdqVzQ1cFd5YzNKb0hhaWJEd3ZXOVVvDQpHU1pCVnk0aHJNL0ZhN1hDV3YxV2ZITlcxZ0R3YUxZd0RubDVqRm1SQnZjZnVRSURBUUFCbzRINU1JSDJNSUdSDQpCZ05WSFNNRWdZa3dnWWFBRkhVTTFVM0U0V3lMMW52RmQrZFBZOGY0TzJoWm9XT2tZVEJmTVFzd0NRWURWUVFHDQpFd0pWVXpFTE1Ba0dBMVVFQ0F3Q1EwRXhGakFVQmdOVkJBY01EVk5oYmlCR2NtRnVZMmx6WTI4eER6QU5CZ05WDQpCQW9NQmtSdlkydGxjakVhTUJnR0ExVUVBd3dSVG05MFlYSjVJRlJsYzNScGJtY2dRMEdDQ1FEQ2VETGJlbUlUDQpTekFTQmdOVkhSTUJBZjhFQ0RBR0FRSC9BZ0VBTUIwR0ExVWRKUVFXTUJRR0NDc0dBUVVGQndNQ0JnZ3JCZ0VGDQpCUWNEQVRBT0JnTlZIUThCQWY4RUJBTUNBVVl3SFFZRFZSME9CQllFRkhlNDhoY0JjQXAwYlVWbFR4WGVSQTRvDQpFMTZwTUEwR0NTcUdTSWIzRFFFQkN3VUFBNElDQVFBV1V0QVBkVUZwd1JxK04xU3pHVWVqU2lrZU1HeVBac2NaDQpKQlVDbWhab0Z1ZmdYR2JMTzVPcGNSTGFWM1hkYTB0LzVQdGRHTVNFemN6ZW9aSFdrbkR0dys3OU9CaXR0UFBqDQpTaDFvRkR1UG8zNVI3ZVA2MjRsVUNjaC9JblpDcGhUYUx4OW9ETEdjYUszYWlsUTl3akJkS2RsQmw4S05LSVpwDQphMTNhUDVyblNtMkp2YSt0WHkveWkzQlNkczNkR0Q4SVRLWnlJLzZBRkh4R3ZPYnJESUJwbzRGRi96Y1dYVkRqDQpwYU9teHBsUnRNNEhpdG0rc1hHdmZxSmU0eDVEdU9YT25QclQzZEh2UlQ2dlNaVW9Lb2J4TXFtUlRPY3JPSVBhDQpFZU1wT29ic2hPUnVSbnRNRFl2dmdPM0Q2cDZpY2lEVzJWcDlONnJkTWRmT1dFUU44SlZXdkI3SXhSSGs5cUtKDQp2WU9XVmJjekF0MHFwTXZYRjNQWExqWmJVTTBrbk9kVUtJRWJxUDRZVWJnZHp4NlJ0Z2lpWTkzMEFqNnRBdGNlDQowZnBnTmx2ak1ScFNCdVdUbEFmTk5qRy9ZaG5kTXo5dUk2OFRNZkZwUjNQY2dWSXYzMGtydy85VnpvTGkyRHBlDQpvdzZEckdPNm9pK0RoTjc4UDRqWS9POVVjelpLMnJvWkwxT2k1UDBSSXhmMjNVWkM3eDFEbGNOM25CcjRzWVN2DQpyQng0Y0ZUTU5wd1UrbnpzSWk0ZGpjRkRLbUpkRU95ak1ua1AydjBMd2U3eXZLMDhwWmRFdSswemJycTE3a3VlDQpYcFhMYzdLNjhRQjE1eXh6R3lsVTVyUnd6bUMvWXNBVnlFNGVvR3U4UHhXeHJFUnZIYnk0QjhZUDB2QWZPcmFMDQpsS21YbEs0ZFRnPT0NCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0NCg==` + +const validIntermediateAndCertRSA = `LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlHTXpDQ0JCdWdBd0lCQWdJQkFUQU5CZ2txaGtpRzl3MEJBUXNGQURCZk1Rc3dDUVlEVlFRR0V3SlZVekVMDQpNQWtHQTFVRUNBd0NRMEV4RmpBVUJnTlZCQWNNRFZOaGJpQkdjbUZ1WTJselkyOHhEekFOQmdOVkJBb01Ca1J2DQpZMnRsY2pFYU1CZ0dBMVVFQXd3UlRtOTBZWEo1SUZSbGMzUnBibWNnUTBFd0hoY05NVFV3TnpFMk1EUXlOVEF6DQpXaGNOTWpVd056RXpNRFF5TlRBeldqQmZNUm93R0FZRFZRUUREQkZPYjNSaGNua2dWR1Z6ZEdsdVp5QkRRVEVMDQpNQWtHQTFVRUJoTUNWVk14RmpBVUJnTlZCQWNNRFZOaGJpQkdjbUZ1WTJselkyOHhEekFOQmdOVkJBb01Ca1J2DQpZMnRsY2pFTE1Ba0dBMVVFQ0F3Q1EwRXdnZ0lpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElDRHdBd2dnSUtBb0lDDQpBUUN3VlZENHBLN3o3cFhQcEpiYVoxSGc1ZVJYSWNhWXRiRlBDbk4waXF5OUhzVkVHbkVuNUJQTlNFc3VQK20wDQo1TjBxVlY3REdiMVNqaWxvTFhEMXFERHZoWFdrK2dpUzlwcHFQSFBMVlBCNGJ2enNxd0RZcnRwYnFrWXZPMFlLDQowU0wza3hQWFVGZGxrRmZndTB4amxjem0yUGhXRzNKZDhhQXRzcEwvTCtWZlBBMTNKVWFXeFNMcHVpMUluOHJoDQpnQXlRVEs2UTRPZjZHYkpZVG5BSGI1OVVvTFhTekI1QWZxaVVxNkw3bkVZWUtvUGZsUGJSQUlXTC9VQm0wYytIDQpvY21zNzA2UFlwbVBTMlJRdjNpT0dtbm45aEVWcDNQNmpxN1dBZXZiQTRhWUd4NUVzYlZ0WUFCcUpCYkZXQXV3DQp3VEdSWW16bjBNajBlVE1nZTl6dFlCMi8yc3hkVGU2dWhtRmdwVVhuZ0RxSkk1TzlOM3pQZnZsRUltQ2t5M0hNDQpqSm9MN2c1c21xWDlvMVArRVNMaDBWWnpoaDdJRFB6UVRYcGNQSVMvNnowbDIyUUdrSy8xTjFQYUFEYVVIZExMDQp2U2F2M3kyQmFFbVB2ZjJma1pqOHlQNWVZZ2k3Q3c1T05oSExEWUhGY2w5Wm0veXdtZHhISkVUejluZmdYbnNXDQpITnhEcXJrQ1ZPNDZyL3U2clNyVXQ2aHIzb2RkSkc4czhKbzA2ZWFydzZYVTNNek0rM2dpd2tLMFNTTTN1UlBxDQo0QXNjUjFUditFMzFBdU9BbWpxWVFvVDI5Yk1JeG9TemVsamovWW5lZHdqVzQ1cFd5YzNKb0hhaWJEd3ZXOVVvDQpHU1pCVnk0aHJNL0ZhN1hDV3YxV2ZITlcxZ0R3YUxZd0RubDVqRm1SQnZjZnVRSURBUUFCbzRINU1JSDJNSUdSDQpCZ05WSFNNRWdZa3dnWWFBRkhVTTFVM0U0V3lMMW52RmQrZFBZOGY0TzJoWm9XT2tZVEJmTVFzd0NRWURWUVFHDQpFd0pWVXpFTE1Ba0dBMVVFQ0F3Q1EwRXhGakFVQmdOVkJBY01EVk5oYmlCR2NtRnVZMmx6WTI4eER6QU5CZ05WDQpCQW9NQmtSdlkydGxjakVhTUJnR0ExVUVBd3dSVG05MFlYSjVJRlJsYzNScGJtY2dRMEdDQ1FEQ2VETGJlbUlUDQpTekFTQmdOVkhSTUJBZjhFQ0RBR0FRSC9BZ0VBTUIwR0ExVWRKUVFXTUJRR0NDc0dBUVVGQndNQ0JnZ3JCZ0VGDQpCUWNEQVRBT0JnTlZIUThCQWY4RUJBTUNBVVl3SFFZRFZSME9CQllFRkhlNDhoY0JjQXAwYlVWbFR4WGVSQTRvDQpFMTZwTUEwR0NTcUdTSWIzRFFFQkN3VUFBNElDQVFBV1V0QVBkVUZwd1JxK04xU3pHVWVqU2lrZU1HeVBac2NaDQpKQlVDbWhab0Z1ZmdYR2JMTzVPcGNSTGFWM1hkYTB0LzVQdGRHTVNFemN6ZW9aSFdrbkR0dys3OU9CaXR0UFBqDQpTaDFvRkR1UG8zNVI3ZVA2MjRsVUNjaC9JblpDcGhUYUx4OW9ETEdjYUszYWlsUTl3akJkS2RsQmw4S05LSVpwDQphMTNhUDVyblNtMkp2YSt0WHkveWkzQlNkczNkR0Q4SVRLWnlJLzZBRkh4R3ZPYnJESUJwbzRGRi96Y1dYVkRqDQpwYU9teHBsUnRNNEhpdG0rc1hHdmZxSmU0eDVEdU9YT25QclQzZEh2UlQ2dlNaVW9Lb2J4TXFtUlRPY3JPSVBhDQpFZU1wT29ic2hPUnVSbnRNRFl2dmdPM0Q2cDZpY2lEVzJWcDlONnJkTWRmT1dFUU44SlZXdkI3SXhSSGs5cUtKDQp2WU9XVmJjekF0MHFwTXZYRjNQWExqWmJVTTBrbk9kVUtJRWJxUDRZVWJnZHp4NlJ0Z2lpWTkzMEFqNnRBdGNlDQowZnBnTmx2ak1ScFNCdVdUbEFmTk5qRy9ZaG5kTXo5dUk2OFRNZkZwUjNQY2dWSXYzMGtydy85VnpvTGkyRHBlDQpvdzZEckdPNm9pK0RoTjc4UDRqWS9POVVjelpLMnJvWkwxT2k1UDBSSXhmMjNVWkM3eDFEbGNOM25CcjRzWVN2DQpyQng0Y0ZUTU5wd1UrbnpzSWk0ZGpjRkRLbUpkRU95ak1ua1AydjBMd2U3eXZLMDhwWmRFdSswemJycTE3a3VlDQpYcFhMYzdLNjhRQjE1eXh6R3lsVTVyUnd6bUMvWXNBVnlFNGVvR3U4UHhXeHJFUnZIYnk0QjhZUDB2QWZPcmFMDQpsS21YbEs0ZFRnPT0NCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0NCi0tLS0tQkVHSU4gQ0VSVElGSUNBVEUtLS0tLQ0KTUlJRlZ6Q0NBeitnQXdJQkFnSUJBekFOQmdrcWhraUc5dzBCQVFzRkFEQmZNUm93R0FZRFZRUUREQkZPYjNSaA0KY25rZ1ZHVnpkR2x1WnlCRFFURUxNQWtHQTFVRUJoTUNWVk14RmpBVUJnTlZCQWNNRFZOaGJpQkdjbUZ1WTJseg0KWTI4eER6QU5CZ05WQkFvTUJrUnZZMnRsY2pFTE1Ba0dBMVVFQ0F3Q1EwRXdIaGNOTVRVd056RTJNRFF5TlRVdw0KV2hjTk1UWXdOekUxTURReU5UVXdXakJnTVJzd0dRWURWUVFEREJKelpXTjFjbVV1WlhoaGJYQnNaUzVqYjIweA0KQ3pBSkJnTlZCQVlUQWxWVE1SWXdGQVlEVlFRSERBMVRZVzRnUm5KaGJtTnBjMk52TVE4d0RRWURWUVFLREFaRQ0KYjJOclpYSXhDekFKQmdOVkJBZ01Ba05CTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQw0KQVFFQW1MWWlZQ1RBV0pCV0F1eFpMcVZtVjRGaVVkR2dFcW9RdkNiTjczekYvbVFmaHEwQ0lUbzZ4U3hzMVFpRw0KRE96VXRrcHpYenppU2o0SjUrZXQ0SmtGbGVlRUthTWNIYWRlSXNTbEhHdlZ0WER2OTNvUjN5ZG1mWk8rVUxSVQ0KOHhIbG9xY0xyMUtyT1AxZGFMZmRNUmJhY3RkNzVVUWd2dzlYVHNkZU1WWDVBbGljU0VOVktWK0FRWHZWcHY4UA0KVDEwTVN2bEJGYW00cmVYdVkvU2tlTWJJYVc1cEZ1NkFRdjNabWZ0dDJ0YTBDQjlrYjFtWWQrT0tydThIbm5xNQ0KYUp3NlIzR2hQMFRCZDI1UDFQa2lTeE0yS0dZWlprMFcvTlpxTEs5L0xURktUTkN2N1ZqQ2J5c1ZvN0h4Q1kwYg0KUWUvYkRQODJ2N1NuTHRiM2Fab2dmdmE0SFFJREFRQUJvNElCR3pDQ0FSY3dnWWdHQTFVZEl3U0JnREIrZ0JSMw0KdVBJWEFYQUtkRzFGWlU4VjNrUU9LQk5lcWFGanBHRXdYekVMTUFrR0ExVUVCaE1DVlZNeEN6QUpCZ05WQkFnTQ0KQWtOQk1SWXdGQVlEVlFRSERBMVRZVzRnUm5KaGJtTnBjMk52TVE4d0RRWURWUVFLREFaRWIyTnJaWEl4R2pBWQ0KQmdOVkJBTU1FVTV2ZEdGeWVTQlVaWE4wYVc1bklFTkJnZ0VCTUF3R0ExVWRFd0VCL3dRQ01BQXdIUVlEVlIwbA0KQkJZd0ZBWUlLd1lCQlFVSEF3SUdDQ3NHQVFVRkJ3TUJNQTRHQTFVZER3RUIvd1FFQXdJRm9EQXVCZ05WSFJFRQ0KSnpBbGdoSnpaV04xY21VdVpYaGhiWEJzWlM1amIyMkNDV3h2WTJGc2FHOXpkSWNFZndBQUFUQWRCZ05WSFE0RQ0KRmdRVURQRDRDYVhSYnU1UUJiNWU4eThvZHZUcVc0SXdEUVlKS29aSWh2Y05BUUVMQlFBRGdnSUJBSk95bG1jNA0KbjdKNjRHS3NQL3hoVWRLS1Y5L0tEK3VmenBLYnJMSW9qV243clR5ZTcwdlkwT2pRRnVPWGM1NHlqTVNJTCsvNQ0KbWxOUTdZL2ZKUzh4ZEg3OUVSKzRuV011RDJlY2lMbnNMZ2JZVWs0aGl5Ynk4LzVWKy9ZcVBlQ3BQQ242VEpSSw0KYTBFNmxWL1VqWEpkcmlnSnZKb05PUjhaZ3RFWi9RUGdqSkVWVXNnNDdkdHF6c0RwZ2VTOGRjanVNV3BaeFAwMg0KcWF2RkxEalNGelZIKzJENk90eTFEUXBsbS8vM1hhUlhoMjNkT0NQOHdqL2J4dm5WVG9GV3Mrek80dVQxTEYvUw0KS1hDTlFvZWlHeFdIeXpyWEZWVnRWbkM5RlNOejBHZzIvRW0xdGZSZ3ZoVW40S0xKY3ZaVzlvMVI3VlZDWDBMMQ0KMHgwZnlLM1ZXZVdjODZhNWE2ODFhbUtaU0ViakFtSVZaRjl6T1gwUE9EQzhveSt6cU9QV2EwV0NsNEs2ekRDNg0KMklJRkJCTnk1MFpTMmlPTjZSWTZtRTdObUE3OGdja2Y0MTVjcUlWcmxvWUpiYlREZXBmaFRWMjE4U0xlcHBoNA0KdUdiMi9zeGtsZkhPWUUrcnBIY2lpYld3WHJ3bE9ESmFYdXpYRmhwbFVkL292ZHVqQk5BSUhrQmZ6eStZNnoycw0KYndaY2ZxRDROSWIvQUdoSXlXMnZidnU0enNsRHAxTUVzTG9hTytTemlyTXpreU1CbEtSdDEyMHR3czRFa1VsbQ0KL1FoalNVb1pwQ0FzeTVDL3BWNCtieDBTeXNOZC9TK2tLYVJaYy9VNlkzWllCRmhzekxoN0phTFhLbWs3d0huRQ0KcmdnbTZvejRML0d5UFdjL0ZqZm5zZWZXS00yeUMzUURoanZqDQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tDQo=` + +const signedRSARootTemplate = `{"signed":{"_type":"Root","consistent_snapshot":false,"expires":"2016-07-16T23:34:13.389129622-07:00","keys":{"1fc4fdc38f66558658c5c59b67f1716bdc6a74ef138b023ae5931db69f51d670":{"keytype":"ecdsa","keyval":{"private":null,"public":"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8nIgzLigo5D47dWQe1IUjzHXxvyx0j/OL16VQymuloWsgVDxxT6+mH3CeviMAs+/McnEPE9exnm6SQGR5x3XMw=="}},"23c29cc372109c819e081bc953b7657d05e3f968f03c21d0d75ea457590f3d14":{"keytype":"ecdsa","keyval":{"private":null,"public":"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEClUFVWkc85OQScfTQRS02VaLIEaeCmxdwYS/hcTLVoTxlFfRfs7HyalTwXGAGO79XZZS+koE6s8D0xGcCJQkLQ=="}},"49cf5c6404a35fa41d5a5aa2ce539dfee0d7a2176d0da488914a38603b1f4292":{"keytype":"rsa-x509","keyval":{"private":null,"public":"{{.RootPem}}"}},"e3a5a4fdaf11ea1ec58f5efed6f3639b39cd4cfa1418c8b55c9a8c2447ace5d9":{"keytype":"ecdsa","keyval":{"private":null,"public":"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgl3rzMPMEKhS1k/AX16MM4PdidpjJr+z4pj0Td+30QnpbOIARgpyR1PiFztU8BZlqG3cUazvFclr2q/xHvfrqw=="}}},"roles":{"root":{"keyids":["49cf5c6404a35fa41d5a5aa2ce539dfee0d7a2176d0da488914a38603b1f4292"],"threshold":1},"snapshot":{"keyids":["23c29cc372109c819e081bc953b7657d05e3f968f03c21d0d75ea457590f3d14"],"threshold":1},"targets":{"keyids":["1fc4fdc38f66558658c5c59b67f1716bdc6a74ef138b023ae5931db69f51d670"],"threshold":1},"timestamp":{"keyids":["e3a5a4fdaf11ea1ec58f5efed6f3639b39cd4cfa1418c8b55c9a8c2447ace5d9"],"threshold":1}},"version":2},"signatures":[{"keyid":"49cf5c6404a35fa41d5a5aa2ce539dfee0d7a2176d0da488914a38603b1f4292","method":"rsapss","sig":"625670b428f4dbc5dcdb7f8a1df8fa828bc5118ea131b0573b91d3db417423965e20f6f597b3a6923122c29f591eeb02958f907aa88fd42161fc07c26efe2d2da9ea3245cfb66f14249d6131956af3a72889e077d8f4038fd93ac75844ac7cef3d46cecd3456d814241453191642c301dd3aa47f259b1c8f2ce85d91513ca884fbb010b2ff86d02067ef117eb2083cfce0c65d70b357e41f66cc68bd03b80396887d20467103c520a08d7c498a6ffdf61e4994aa33fc2cd86fa48842c68135a1719fa03a517b02b33f0ce95c0bf358a9cbd8d6e804e869fb1bca29adee8d3a87dd469df0eb4dc0dab30cbd84f0a8a2d8ea85ff77ed3b036f961875cf532e68b420326d63b0da1219df92639a1d7683a0dce909382b6cd7c0082f5b00545a54fc3253f54dfedff348e71a38746804651c8e0b0b19585392ffa42698b878329ee8ef2eece83ae8a23df26feec62f2a843c522784d70fff1dcf04ead5ad5791b6160b8a2eeacca0a67d14159c6a70ced1f839016b86dea72050976f87d69c437dc429eaf57ea071b3a2d20145a8f2b0ed42483e6f9e405b4601ba4016f4801f3324901804b67b798767c9cafe28daca0091bbbfcff8d80a63dff3a667cbdb181c36c94b70035c48e3ad862252eb2a9833d8bd1b469fd32c3f4f1013e27b708d59bcbb9d94c91fe15c889473f9a365c6116d2989387c2cf74dbe8be67faafef69279"}]}` + // 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.DebugLevel) - validateRootSuccessfully(t, data.ECDSAKey) + validateRootSuccessfully(t, data.RSAKey) } func validateRootSuccessfully(t *testing.T, rootType data.KeyAlgorithm) { @@ -95,7 +110,7 @@ func validateRootSuccessfully(t *testing.T, rootType data.KeyAlgorithm) { assert.NoError(t, err) // - // Test TOFU logic. We remove all certs and expect a new one to be added after ListTargets + // Test TOFUS logic. We remove all certs and expect a new one to be added after ListTargets // err = repo.KeyStoreManager.CertificateStore().RemoveAll() assert.NoError(t, err) @@ -121,9 +136,135 @@ func validateRootSuccessfully(t *testing.T, rootType data.KeyAlgorithm) { assert.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 TOFU doesn't apply + // in the store for the dnsName docker.com/notary, so TOFUS doesn't apply _, err = repo.ListTargets() if assert.Error(t, err, "An error was expected") { assert.Equal(t, err, keystoremanager.ErrValidationFail) } + + // + // Test CA fallback logic. + // If we already have a certificate that matches a CN and get presented a new + // certificate for that same CN and that certificate is signed by a CA we + // know about, then we trust it but don't remove the previous pinning. + // + + // First, remove all certs and trusted CAs + err = repo.KeyStoreManager.CertificateStore().RemoveAll() + assert.NoError(t, err) + assert.Len(t, repo.KeyStoreManager.CertificateStore().GetCertificates(), 0) + err = repo.KeyStoreManager.CAStore().RemoveAll() + assert.NoError(t, err) + assert.Len(t, repo.KeyStoreManager.CAStore().GetCertificates(), 0) + + // Add a trusted root-ca + err = repo.KeyStoreManager.CAStore().AddCertFromFile("../fixtures/root-ca.crt") + assert.NoError(t, err) + + // Add a previously generated certificate with CN=secure.example.com + err = repo.KeyStoreManager.CertificateStore().AddCertFromFile("../fixtures/self-signed_secure.example.com.crt") + assert.NoError(t, err) + + // Create a manual rootSigner with a valid intermediate + leaf cert + var testSignedRoot data.Signed + var signedRootBytes bytes.Buffer + + // Execute our template + templ, _ := template.New("SignedRSARootTemplate").Parse(signedRSARootTemplate) + templ.Execute(&signedRootBytes, SignedRSARootTemplate{RootPem: validIntermediateAndCertRSA}) + + // Unmarshal our signedroot + json.Unmarshal(signedRootBytes.Bytes(), &testSignedRoot) + + // + // This call to ValidateRoot will suceed in getting to the TUF validation, since + // we are usign a valid PEM encoded certificate chain of intermediate + leaf cert + // that are signed by a trusted root authority and the leaf cert has a correct CN. + // It will, however, fail to validate, because this is has an invalid TUF signature + // + err = repo.KeyStoreManager.ValidateRoot(&testSignedRoot, "secure.example.com") + if assert.Error(t, err, "An error was expected") { + assert.Equal(t, err, errors.New("tuf: valid signatures did not meet threshold")) + } +} + +func TestValidateRootWithInvalidData(t *testing.T) { + var testSignedRoot data.Signed + var signedRootBytes bytes.Buffer + + // Temporary directory where test files will be created + tempBaseDir, err := ioutil.TempDir("", "notary-test-") + defer os.RemoveAll(tempBaseDir) + assert.NoError(t, err, "failed to create a temporary directory: %s", err) + + // Create a FileStoreManager + keyStoreManager, err := keystoremanager.NewKeyStoreManager(tempBaseDir) + assert.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) + + // + // This call to ValidateRoot will succeed since we are usign a valid PEM + // encoded certificate, and have no other certificates for this CN + // + err = keyStoreManager.ValidateRoot(&testSignedRoot, "docker.com/notary") + assert.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 = keyStoreManager.ValidateRoot(&testSignedRoot, "diogomonica.com/notary") + if assert.Error(t, err, "An error was expected") { + assert.Equal(t, err, keystoremanager.ErrValidationFail) + } + + // + // This call to ValidateRoot will fail since we are passing an unparsable RootSigned + // + // Execute our template deleting the old buffer first + signedRootBytes.Reset() + templ, _ = template.New("SignedRSARootTemplate").Parse(signedRSARootTemplate) + templ.Execute(&signedRootBytes, SignedRSARootTemplate{RootPem: "------ ABSOLUTELY NOT A PEM -------"}) + // Unmarshal our signedroot + json.Unmarshal(signedRootBytes.Bytes(), &testSignedRoot) + + err = keyStoreManager.ValidateRoot(&testSignedRoot, "docker.com/notary") + assert.Error(t, err, "illegal base64 data at input byte") + + // + // This call to ValidateRoot will fail since we are passing an invalid PEM cert + // + // Execute our template deleting the old buffer first + signedRootBytes.Reset() + templ, _ = template.New("SignedRSARootTemplate").Parse(signedRSARootTemplate) + templ.Execute(&signedRootBytes, SignedRSARootTemplate{RootPem: "LS0tLS1CRUdJTiBDRVJU"}) + // Unmarshal our signedroot + json.Unmarshal(signedRootBytes.Bytes(), &testSignedRoot) + + err = keyStoreManager.ValidateRoot(&testSignedRoot, "docker.com/notary") + if assert.Error(t, err, "An error was expected") { + assert.Equal(t, err, keystoremanager.ErrValidationFail) + } + + // + // This call to ValidateRoot will fail since we are passing only CA certificate + // This will fail due to the lack of a leaf certificate + // + // Execute our template deleting the old buffer first + signedRootBytes.Reset() + templ, _ = template.New("SignedRSARootTemplate").Parse(signedRSARootTemplate) + templ.Execute(&signedRootBytes, SignedRSARootTemplate{RootPem: validCAPEMEncodeRSARoot}) + // Unmarshal our signedroot + json.Unmarshal(signedRootBytes.Bytes(), &testSignedRoot) + + err = keyStoreManager.ValidateRoot(&testSignedRoot, "docker.com/notary") + if assert.Error(t, err, "An error was expected") { + assert.Equal(t, err, keystoremanager.ErrValidationFail) + } } diff --git a/fixtures/self-signed_secure.example.com.crt b/fixtures/self-signed_secure.example.com.crt new file mode 100755 index 0000000000..c657270e15 --- /dev/null +++ b/fixtures/self-signed_secure.example.com.crt @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBqDCCAU6gAwIBAgIRAM1vKVhmZuWcrogc3ASBaZUwCgYIKoZIzj0EAwIwOjEb +MBkGA1UEChMSc2VjdXJlLmV4YW1wbGUuY29tMRswGQYDVQQDExJzZWN1cmUuZXhh +bXBsZS5jb20wHhcNMTUwNzE3MDU1NTIzWhcNMTcwNzE2MDU1NTIzWjA6MRswGQYD +VQQKExJzZWN1cmUuZXhhbXBsZS5jb20xGzAZBgNVBAMTEnNlY3VyZS5leGFtcGxl +LmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABI556M7bS82ICDeXy/GcGG7D +IoNKYx0jbqVUz4wyKy1GFJ6EHxuiENBNgTIsRBpyPwPVq3nlz9qM9Oi2eEkTjEKj +NTAzMA4GA1UdDwEB/wQEAwIAoDATBgNVHSUEDDAKBggrBgEFBQcDAzAMBgNVHRMB +Af8EAjAAMAoGCCqGSM49BAMCA0gAMEUCIER2XCkQ8dUWBZEUeT5kABg7neiHPtSL +VVE6bJxu2sxlAiEAkRG6u1ieXKGl38gUkCn75Yvo9nOSLdh0gtxUUcOXvUc= +-----END CERTIFICATE----- diff --git a/keystoremanager/keystoremanager.go b/keystoremanager/keystoremanager.go index 4c931c6c07..69a59532e5 100644 --- a/keystoremanager/keystoremanager.go +++ b/keystoremanager/keystoremanager.go @@ -210,7 +210,7 @@ func (km *KeyStoreManager) ValidateRoot(root *data.Signed, dnsName string) error // If we got no leaf certificates or we got more than one, fail if len(leafCerts) != 1 { - logrus.Debugf("error while parsing root certificate with keyID: %s, %v", keyID, err) + logrus.Debugf("wasn't able to find a leaf certificate in the chain of keyID: %s", keyID) continue } @@ -222,11 +222,26 @@ func (km *KeyStoreManager) ValidateRoot(root *data.Signed, dnsName string) error continue } + // Validate that this leaf certificate has a CN that matches the exact gun + if leafCert.Subject.CommonName != dnsName { + logrus.Debugf("error leaf certificate CN: %s doesn't match the given dns name: %s", leafCert.Subject.CommonName, dnsName) + continue + } + // Add all the valid leafs to the certificates map so we can refer to them later allCerts[leafID] = leafCert // Retrieve all the trusted certificates that match this dns Name certsForCN, err := km.certificateStore.GetCertificatesByCN(dnsName) + 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 not trust this key + if _, ok := err.(*trustmanager.ErrNoCertificatesFound); !ok { + logrus.Debugf("error retrieving certificates for: %s, %v", dnsName, err) + continue + } + } // If there are no certificates with this CN, lets TOFUS! // Note that this logic should only exist in docker 1.8 From 4c805611d0b2f6856ac8936f3f93488124f212a8 Mon Sep 17 00:00:00 2001 From: Diogo Monica Date: Fri, 17 Jul 2015 14:23:56 -0700 Subject: [PATCH 15/17] Adding more error types and being extra careful with checks Signed-off-by: Diogo Monica --- keystoremanager/keystoremanager.go | 36 +++++++++++++++++++++++++----- trustmanager/x509filestore.go | 6 ++--- trustmanager/x509memstore.go | 7 +++++- trustmanager/x509store.go | 20 +++++++++++++++++ 4 files changed, 59 insertions(+), 10 deletions(-) diff --git a/keystoremanager/keystoremanager.go b/keystoremanager/keystoremanager.go index 69a59532e5..929ac3d4c2 100644 --- a/keystoremanager/keystoremanager.go +++ b/keystoremanager/keystoremanager.go @@ -192,11 +192,23 @@ func (km *KeyStoreManager) ValidateRoot(root *data.Signed, dnsName string) error return err } - // Iterate over every keyID for the root role inside of roots.json + // validKeys will store all the keys that were considered valid either by + // direct certificate match, or CA chain path validKeys := make(map[string]*data.PublicKey) + + // allCerts will keep a list of all leafCerts that were found, and is used + // to aid on root certificate rotation allCerts := make(map[string]*x509.Certificate) - logrus.Debugf("found the following root keys in roots.json: %v", rootSigned.Signed.Keys) - for _, keyID := range rootSigned.Signed.Roles["root"].KeyIDs { + + // Before we loop through all root keys available, make sure any exist + rootRoles, ok := rootSigned.Signed.Roles["root"] + if !ok { + return errors.New("no root roles found in tuf metadata") + } + + logrus.Debugf("found the following root keys in roots.json: %v", rootRoles.KeyIDs) + // Iterate over every keyID for the root role inside of roots.json + for _, keyID := range rootRoles.KeyIDs { // Decode all the x509 certificates that were bundled with this // Specific root key decodedCerts, err := trustmanager.LoadCertBundleFromPEM([]byte(rootSigned.Signed.Keys[keyID].Public())) @@ -285,12 +297,24 @@ func (km *KeyStoreManager) ValidateRoot(root *data.Signed, dnsName string) error // If this happens, we should replace the old root of trust with the new one if newRootKey != nil { logrus.Debugf("got a new root key to rotate to: %s", newRootKey.ID()) + // Retrieve the certificate associated with the new root key and trust it - newRootKeyCert := allCerts[newRootKey.ID()] + newRootKeyCert, ok := allCerts[newRootKey.ID()] + // Paranoid check for the certificate still being in the map + if !ok { + logrus.Debugf("error while retrieving new root certificate with keyID: %s, %v", newRootKey.ID(), err) + return ErrRootRotationFail + } + + // Add the new root certificate to our certificate store err := km.certificateStore.AddCert(newRootKeyCert) if err != nil { - logrus.Debugf("error while adding new root certificate with keyID: %s, %v", newRootKey.ID(), err) - return ErrRootRotationFail + // Ignore the error if the certificate already exists + if _, ok := err.(*trustmanager.ErrCertExists); !ok { + logrus.Debugf("error while adding new root certificate with keyID: %s, %v", newRootKey.ID(), err) + return ErrRootRotationFail + } + logrus.Debugf("root certificate already exists in keystore: %s", newRootKey.ID()) } // Remove the new root certificate from the certificate mapping so we diff --git a/trustmanager/x509filestore.go b/trustmanager/x509filestore.go index 909afb3c25..a09ee1185e 100644 --- a/trustmanager/x509filestore.go +++ b/trustmanager/x509filestore.go @@ -57,7 +57,7 @@ func (s *X509FileStore) AddCert(cert *x509.Certificate) error { // Check if this certificate meets our validation criteria if !s.validate.Validate(cert) { - return errors.New("certificate validation failed") + return &ErrCertValidation{} } // Attempt to write the certificate to the file if err := s.addNamedCert(cert); err != nil { @@ -76,9 +76,9 @@ func (s *X509FileStore) addNamedCert(cert *x509.Certificate) error { } logrus.Debug("Adding cert with certID: ", certID) - // Validate if we already loaded this certificate before + // Validate if we already added this certificate before if _, ok := s.fingerprintMap[certID]; ok { - return errors.New("certificate already in the store") + return &ErrCertExists{} } // Convert certificate to PEM diff --git a/trustmanager/x509memstore.go b/trustmanager/x509memstore.go index 399b050b41..68f288c326 100644 --- a/trustmanager/x509memstore.go +++ b/trustmanager/x509memstore.go @@ -3,6 +3,8 @@ package trustmanager import ( "crypto/x509" "errors" + + "github.com/Sirupsen/logrus" ) // X509MemStore implements X509Store as an in-memory object with no persistence @@ -43,7 +45,7 @@ func (s *X509MemStore) AddCert(cert *x509.Certificate) error { } if !s.validate.Validate(cert) { - return errors.New("certificate failed validation") + return &ErrCertValidation{} } certID, err := fingerprintCert(cert) @@ -51,6 +53,9 @@ func (s *X509MemStore) AddCert(cert *x509.Certificate) error { 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) diff --git a/trustmanager/x509store.go b/trustmanager/x509store.go index deb993766d..22ddfa08d0 100644 --- a/trustmanager/x509store.go +++ b/trustmanager/x509store.go @@ -20,6 +20,26 @@ 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 { +} + +// ErrInvalidCertificate is returned when an invalid Certificate is given +func (err ErrCertExists) Error() string { + return fmt.Sprintf("certificate already in the store") +} + // X509Store is the interface for all X509Stores type X509Store interface { AddCert(cert *x509.Certificate) error From 00f8f56942dd3c27114f99e87f4a97e47462c57a Mon Sep 17 00:00:00 2001 From: Diogo Monica Date: Fri, 17 Jul 2015 14:27:42 -0700 Subject: [PATCH 16/17] Cosmetic code changes Signed-off-by: Diogo Monica --- trustmanager/x509filestore_test.go | 2 +- trustmanager/x509memstore.go | 3 --- trustmanager/x509memstore_test.go | 2 +- trustmanager/x509store.go | 2 +- 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/trustmanager/x509filestore_test.go b/trustmanager/x509filestore_test.go index 8d4794fc4f..a90a0a194a 100644 --- a/trustmanager/x509filestore_test.go +++ b/trustmanager/x509filestore_test.go @@ -164,7 +164,7 @@ func TestRemoveAllX509FileStore(t *testing.T) { } } - // Number of certificates should be 1 since we added the cert + // 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) diff --git a/trustmanager/x509memstore.go b/trustmanager/x509memstore.go index 68f288c326..55666c0976 100644 --- a/trustmanager/x509memstore.go +++ b/trustmanager/x509memstore.go @@ -98,9 +98,6 @@ func (s *X509MemStore) RemoveAll() error { } } - s.fingerprintMap = make(map[CertID]*x509.Certificate) - s.nameMap = make(map[string][]CertID) - return nil } diff --git a/trustmanager/x509memstore_test.go b/trustmanager/x509memstore_test.go index 6fdd1acc0a..e2470bb2c3 100644 --- a/trustmanager/x509memstore_test.go +++ b/trustmanager/x509memstore_test.go @@ -130,7 +130,7 @@ func TestRemoveAllX509MemStore(t *testing.T) { } } - // Number of certificates should be 1 since we added the cert + // 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) diff --git a/trustmanager/x509store.go b/trustmanager/x509store.go index 22ddfa08d0..05aaaba248 100644 --- a/trustmanager/x509store.go +++ b/trustmanager/x509store.go @@ -35,7 +35,7 @@ func (err ErrCertValidation) Error() string { type ErrCertExists struct { } -// ErrInvalidCertificate is returned when an invalid Certificate is given +// 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") } From f5325507427778b4d674af66f6f7a04c3783c620 Mon Sep 17 00:00:00 2001 From: Diogo Monica Date: Fri, 17 Jul 2015 14:44:51 -0700 Subject: [PATCH 17/17] Rebase works Signed-off-by: Diogo Monica --- client/client_root_validation_test.go | 29 +++++++++++++-------------- keystoremanager/keystoremanager.go | 12 +++++------ 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/client/client_root_validation_test.go b/client/client_root_validation_test.go index 4d0260b18a..e563989521 100644 --- a/client/client_root_validation_test.go +++ b/client/client_root_validation_test.go @@ -64,13 +64,12 @@ func validateRootSuccessfully(t *testing.T, rootType data.KeyAlgorithm) { // Initialize is supposed to have created new certificate for this repository // Lets check for it and store it for later use - allCerts := repo.KeyStoreManager.CertificateStore().GetCertificates() + allCerts := repo.KeyStoreManager.TrustedCertificateStore().GetCertificates() assert.Len(t, allCerts, 1) // Now test ListTargets. In preparation, we need to expose some signed // metadata files on the internal HTTP server. - - var tempKey data.PrivateKey + var tempKey data.TUFKey json.Unmarshal([]byte(timestampECDSAKeyJSON), &tempKey) repo.KeyStoreManager.NonRootKeyStore().AddKey(filepath.Join(filepath.FromSlash(gun), tempKey.ID()), &tempKey) @@ -112,14 +111,14 @@ func validateRootSuccessfully(t *testing.T, rootType data.KeyAlgorithm) { // // Test TOFUS logic. We remove all certs and expect a new one to be added after ListTargets // - err = repo.KeyStoreManager.CertificateStore().RemoveAll() + err = repo.KeyStoreManager.TrustedCertificateStore().RemoveAll() assert.NoError(t, err) - assert.Len(t, repo.KeyStoreManager.CertificateStore().GetCertificates(), 0) + assert.Len(t, repo.KeyStoreManager.TrustedCertificateStore().GetCertificates(), 0) // This list targets is expected to succeed and the certificate store to have the new certificate _, err = repo.ListTargets() assert.NoError(t, err) - assert.Len(t, repo.KeyStoreManager.CertificateStore().GetCertificates(), 1) + assert.Len(t, repo.KeyStoreManager.TrustedCertificateStore().GetCertificates(), 1) // // Test certificate mismatch logic. We remove all certs, and a different cert to the @@ -127,12 +126,12 @@ func validateRootSuccessfully(t *testing.T, rootType data.KeyAlgorithm) { // // First, remove all certs - err = repo.KeyStoreManager.CertificateStore().RemoveAll() + err = repo.KeyStoreManager.TrustedCertificateStore().RemoveAll() assert.NoError(t, err) - assert.Len(t, repo.KeyStoreManager.CertificateStore().GetCertificates(), 0) + assert.Len(t, repo.KeyStoreManager.TrustedCertificateStore().GetCertificates(), 0) // Add a previously generated certificate with CN=docker.com/notary - err = repo.KeyStoreManager.CertificateStore().AddCertFromFile("../fixtures/self-signed_docker.com-notary.crt") + err = repo.KeyStoreManager.TrustedCertificateStore().AddCertFromFile("../fixtures/self-signed_docker.com-notary.crt") assert.NoError(t, err) // This list targets is expected to fail, since there already exists a certificate @@ -150,19 +149,19 @@ func validateRootSuccessfully(t *testing.T, rootType data.KeyAlgorithm) { // // First, remove all certs and trusted CAs - err = repo.KeyStoreManager.CertificateStore().RemoveAll() + err = repo.KeyStoreManager.TrustedCertificateStore().RemoveAll() assert.NoError(t, err) - assert.Len(t, repo.KeyStoreManager.CertificateStore().GetCertificates(), 0) - err = repo.KeyStoreManager.CAStore().RemoveAll() + assert.Len(t, repo.KeyStoreManager.TrustedCertificateStore().GetCertificates(), 0) + err = repo.KeyStoreManager.TrustedCAStore().RemoveAll() assert.NoError(t, err) - assert.Len(t, repo.KeyStoreManager.CAStore().GetCertificates(), 0) + assert.Len(t, repo.KeyStoreManager.TrustedCAStore().GetCertificates(), 0) // Add a trusted root-ca - err = repo.KeyStoreManager.CAStore().AddCertFromFile("../fixtures/root-ca.crt") + err = repo.KeyStoreManager.TrustedCAStore().AddCertFromFile("../fixtures/root-ca.crt") assert.NoError(t, err) // Add a previously generated certificate with CN=secure.example.com - err = repo.KeyStoreManager.CertificateStore().AddCertFromFile("../fixtures/self-signed_secure.example.com.crt") + err = repo.KeyStoreManager.TrustedCertificateStore().AddCertFromFile("../fixtures/self-signed_secure.example.com.crt") assert.NoError(t, err) // Create a manual rootSigner with a valid intermediate + leaf cert diff --git a/keystoremanager/keystoremanager.go b/keystoremanager/keystoremanager.go index 929ac3d4c2..f90dde1bec 100644 --- a/keystoremanager/keystoremanager.go +++ b/keystoremanager/keystoremanager.go @@ -194,7 +194,7 @@ func (km *KeyStoreManager) ValidateRoot(root *data.Signed, dnsName string) error // validKeys will store all the keys that were considered valid either by // direct certificate match, or CA chain path - validKeys := make(map[string]*data.PublicKey) + validKeys := make(map[string]data.PublicKey) // allCerts will keep a list of all leafCerts that were found, and is used // to aid on root certificate rotation @@ -244,7 +244,7 @@ func (km *KeyStoreManager) ValidateRoot(root *data.Signed, dnsName string) error allCerts[leafID] = leafCert // Retrieve all the trusted certificates that match this dns Name - certsForCN, err := km.certificateStore.GetCertificatesByCN(dnsName) + certsForCN, err := km.trustedCertificateStore.GetCertificatesByCN(dnsName) 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 @@ -258,7 +258,7 @@ func (km *KeyStoreManager) ValidateRoot(root *data.Signed, dnsName string) error // If there are no certificates with this CN, lets TOFUS! // Note that this logic should only exist in docker 1.8 if len(certsForCN) == 0 { - km.certificateStore.AddCert(leafCert) + km.trustedCertificateStore.AddCert(leafCert) certsForCN = append(certsForCN, leafCert) logrus.Debugf("using TOFUS on %s with keyID: %s", dnsName, leafID) } @@ -275,7 +275,7 @@ func (km *KeyStoreManager) ValidateRoot(root *data.Signed, dnsName string) error // Check to see if this leafCertificate has a chain to one of the Root // CAs of our CA Store. - err = trustmanager.Verify(km.caStore, dnsName, decodedCerts) + err = trustmanager.Verify(km.trustedCAStore, dnsName, decodedCerts) if err == nil { validKeys[keyID] = rootSigned.Signed.Keys[keyID] logrus.Debugf("found a CA path for %s with keyID: %s", dnsName, keyID) @@ -307,7 +307,7 @@ func (km *KeyStoreManager) ValidateRoot(root *data.Signed, dnsName string) error } // Add the new root certificate to our certificate store - err := km.certificateStore.AddCert(newRootKeyCert) + err := km.trustedCertificateStore.AddCert(newRootKeyCert) if err != nil { // Ignore the error if the certificate already exists if _, ok := err.(*trustmanager.ErrCertExists); !ok { @@ -324,7 +324,7 @@ func (km *KeyStoreManager) ValidateRoot(root *data.Signed, dnsName string) error // Iterate over all old valid certificates and remove them, essentially // finishing the rotation of the currently trusted root certificate for _, cert := range allCerts { - err := km.certificateStore.RemoveCert(cert) + err := km.trustedCertificateStore.RemoveCert(cert) if err != nil { logrus.Debugf("error while removing old root certificate: %v", err) return ErrRootRotationFail