diff --git a/client/repo.go b/client/repo.go index c2b0b20278..97cba741af 100644 --- a/client/repo.go +++ b/client/repo.go @@ -7,6 +7,7 @@ import ( "net/http" "path/filepath" + "github.com/docker/notary" "github.com/docker/notary/cryptoservice" "github.com/docker/notary/keystoremanager" "github.com/docker/notary/passphrase" @@ -19,7 +20,7 @@ import ( // (usually ~/.docker/trust/). func NewNotaryRepository(baseDir, gun, baseURL string, rt http.RoundTripper, retriever passphrase.Retriever) (*NotaryRepository, error) { - keysPath := filepath.Join(baseDir, keystoremanager.PrivDir) + keysPath := filepath.Join(baseDir, notary.PrivDir) fileKeyStore, err := trustmanager.NewKeyFileStore(keysPath, retriever) if err != nil { return nil, fmt.Errorf("failed to create private key store in directory: %s", keysPath) diff --git a/client/repo_pkcs11.go b/client/repo_pkcs11.go index 8186e1a497..5f6fc7792e 100644 --- a/client/repo_pkcs11.go +++ b/client/repo_pkcs11.go @@ -7,6 +7,7 @@ import ( "net/http" "path/filepath" + "github.com/docker/notary" "github.com/docker/notary/cryptoservice" "github.com/docker/notary/keystoremanager" "github.com/docker/notary/passphrase" @@ -21,7 +22,7 @@ import ( func NewNotaryRepository(baseDir, gun, baseURL string, rt http.RoundTripper, retriever passphrase.Retriever) (*NotaryRepository, error) { - keysPath := filepath.Join(baseDir, keystoremanager.PrivDir) + keysPath := filepath.Join(baseDir, notary.PrivDir) fileKeyStore, err := trustmanager.NewKeyFileStore(keysPath, retriever) if err != nil { return nil, fmt.Errorf("failed to create private key store in directory: %s", keysPath) diff --git a/cmd/notary/cert.go b/cmd/notary/cert.go index 21223bcf8a..b4357980e5 100644 --- a/cmd/notary/cert.go +++ b/cmd/notary/cert.go @@ -8,6 +8,7 @@ import ( "path/filepath" "time" + "github.com/docker/notary" "github.com/docker/notary/keystoremanager" "github.com/docker/notary/trustmanager" @@ -56,7 +57,7 @@ func certRemove(cmd *cobra.Command, args []string) { parseConfig() - keysPath := filepath.Join(trustDir, keystoremanager.PrivDir) + keysPath := filepath.Join(trustDir, notary.PrivDir) fileKeyStore, err := trustmanager.NewKeyFileStore(keysPath, retriever) if err != nil { fatalf("failed to create private key store in directory: %s", keysPath) @@ -125,7 +126,7 @@ func certList(cmd *cobra.Command, args []string) { parseConfig() - keysPath := filepath.Join(trustDir, keystoremanager.PrivDir) + keysPath := filepath.Join(trustDir, notary.PrivDir) fileKeyStore, err := trustmanager.NewKeyFileStore(keysPath, retriever) if err != nil { fatalf("failed to create private key store in directory: %s", keysPath) diff --git a/cmd/notary/keys.go b/cmd/notary/keys.go index b6e0d226fa..36c5d578ba 100644 --- a/cmd/notary/keys.go +++ b/cmd/notary/keys.go @@ -8,9 +8,9 @@ import ( "sort" "strings" + "github.com/docker/notary" notaryclient "github.com/docker/notary/client" "github.com/docker/notary/cryptoservice" - "github.com/docker/notary/keystoremanager" "github.com/docker/notary/passphrase" "github.com/docker/notary/signer/api" "github.com/docker/notary/trustmanager" @@ -115,7 +115,7 @@ func keysRemoveKey(cmd *cobra.Command, args []string) { parseConfig() - keysPath := filepath.Join(trustDir, keystoremanager.PrivDir) + keysPath := filepath.Join(trustDir, notary.PrivDir) fileKeyStore, err := trustmanager.NewKeyFileStore(keysPath, retriever) if err != nil { fatalf("failed to create private key store in directory: %s", keysPath) @@ -157,7 +157,7 @@ func keysList(cmd *cobra.Command, args []string) { parseConfig() - keysPath := filepath.Join(trustDir, keystoremanager.PrivDir) + keysPath := filepath.Join(trustDir, notary.PrivDir) fileKeyStore, err := trustmanager.NewKeyFileStore(keysPath, retriever) if err != nil { fatalf("failed to create private key store in directory: %s", keysPath) @@ -213,7 +213,7 @@ func keysGenerateRootKey(cmd *cobra.Command, args []string) { parseConfig() - keysPath := filepath.Join(trustDir, keystoremanager.PrivDir) + keysPath := filepath.Join(trustDir, notary.PrivDir) fileKeyStore, err := trustmanager.NewKeyFileStore(keysPath, retriever) if err != nil { fatalf("failed to create private key store in directory: %s", keysPath) @@ -225,7 +225,7 @@ func keysGenerateRootKey(cmd *cobra.Command, args []string) { fatalf("failed to create a new root key: %v", err) } - fmt.Printf("Generated new %s key with keyID: %s\n", algorithm, pubKey.ID()) + fmt.Printf("Generated new %s root key with keyID: %s\n", algorithm, pubKey.ID()) } // keysExport exports a collection of keys to a ZIP file @@ -239,7 +239,7 @@ func keysExport(cmd *cobra.Command, args []string) { parseConfig() - keysPath := filepath.Join(trustDir, keystoremanager.PrivDir) + keysPath := filepath.Join(trustDir, notary.PrivDir) fileKeyStore, err := trustmanager.NewKeyFileStore(keysPath, retriever) if err != nil { fatalf("failed to create private key store in directory: %s", keysPath) @@ -284,7 +284,7 @@ func keysExportRoot(cmd *cobra.Command, args []string) { parseConfig() - keysPath := filepath.Join(trustDir, keystoremanager.PrivDir) + keysPath := filepath.Join(trustDir, notary.PrivDir) fileKeyStore, err := trustmanager.NewKeyFileStore(keysPath, retriever) if err != nil { fatalf("failed to create private key store in directory: %s", keysPath) @@ -321,7 +321,7 @@ func keysImport(cmd *cobra.Command, args []string) { parseConfig() - keysPath := filepath.Join(trustDir, keystoremanager.PrivDir) + keysPath := filepath.Join(trustDir, notary.PrivDir) fileKeyStore, err := trustmanager.NewKeyFileStore(keysPath, retriever) if err != nil { fatalf("failed to create private key store in directory: %s", keysPath) @@ -352,7 +352,7 @@ func keysImportRoot(cmd *cobra.Command, args []string) { parseConfig() - keysPath := filepath.Join(trustDir, keystoremanager.PrivDir) + keysPath := filepath.Join(trustDir, notary.PrivDir) fileKeyStore, err := trustmanager.NewKeyFileStore(keysPath, retriever) if err != nil { fatalf("failed to create private key store in directory: %s", keysPath) diff --git a/const.go b/const.go new file mode 100644 index 0000000000..5b1734a9a7 --- /dev/null +++ b/const.go @@ -0,0 +1,6 @@ +package notary + +// application wide constants +const ( + PrivDir = "private" +) diff --git a/cryptoservice/crypto_service.go b/cryptoservice/crypto_service.go index 4065df0f45..8aa0f214f7 100644 --- a/cryptoservice/crypto_service.go +++ b/cryptoservice/crypto_service.go @@ -57,10 +57,10 @@ func (cs *CryptoService) Create(role, algorithm string) (data.PublicKey, error) if role == data.CanonicalRootRole { keyPath = privKey.ID() } else { - keyPath = filepath.Join(ccs.gun, privKey.ID()) + keyPath = filepath.Join(cs.gun, privKey.ID()) } - for _, ks := range ccs.keyStores { + for _, ks := range cs.keyStores { err = ks.AddKey(keyPath, role, privKey) if err == nil { return data.PublicKeyFromPrivate(privKey), nil @@ -77,11 +77,11 @@ func (cs *CryptoService) Create(role, algorithm string) (data.PublicKey, error) // without a GUN (in which case it's a root key). If that fails, try to get // the key with the GUN (non-root key). // If that fails, then we don't have the key. -func (ccs *CryptoService) GetPrivateKey(keyID string) (k data.PrivateKey, id string, err error) { - keyPaths := []string{keyID, filepath.Join(ccs.gun, keyID)} - for _, ks := range ccs.keyStores { +func (cs *CryptoService) GetPrivateKey(keyID string) (k data.PrivateKey, role string, err error) { + keyPaths := []string{keyID, filepath.Join(cs.gun, keyID)} + for _, ks := range cs.keyStores { for _, keyPath := range keyPaths { - k, id, err = ks.GetKey(keyPath) + k, role, err = ks.GetKey(keyPath) if err != nil { continue } @@ -92,8 +92,8 @@ func (ccs *CryptoService) GetPrivateKey(keyID string) (k data.PrivateKey, id str } // GetKey returns a key by ID -func (ccs *CryptoService) GetKey(keyID string) data.PublicKey { - privKey, _, err := ccs.GetPrivateKey(keyID) +func (cs *CryptoService) GetKey(keyID string) data.PublicKey { + privKey, _, err := cs.GetPrivateKey(keyID) if err != nil { return nil } @@ -101,9 +101,9 @@ func (ccs *CryptoService) GetKey(keyID string) data.PublicKey { } // RemoveKey deletes a key by ID -func (ccs *CryptoService) RemoveKey(keyID string) (err error) { - keyPaths := []string{keyID, filepath.Join(ccs.gun, keyID)} - for _, ks := range ccs.keyStores { +func (cs *CryptoService) RemoveKey(keyID string) (err error) { + keyPaths := []string{keyID, filepath.Join(cs.gun, keyID)} + for _, ks := range cs.keyStores { for _, keyPath := range keyPaths { _, _, err = ks.GetKey(keyPath) if err != nil { @@ -124,7 +124,7 @@ func (cs *CryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signature for _, keyid := range keyIDs { keyName := keyid - privKey, _, err := ccs.GetPrivateKey(keyName) + privKey, _, err := cs.GetPrivateKey(keyName) if err != nil { logrus.Debugf("error attempting to retrieve private key: %s, %v", keyid, err) continue diff --git a/cryptoservice/import_export.go b/cryptoservice/import_export.go index ddc3e3b899..922779b789 100644 --- a/cryptoservice/import_export.go +++ b/cryptoservice/import_export.go @@ -11,7 +11,7 @@ import ( "path/filepath" "strings" - "github.com/docker/notary/keystoremanager" + "github.com/docker/notary" "github.com/docker/notary/passphrase" "github.com/docker/notary/trustmanager" ) @@ -72,7 +72,7 @@ func (cs *CryptoService) ExportRootKeyReencrypt(dest io.Writer, keyID string, ne tempBaseDir, err := ioutil.TempDir("", "notary-key-export-") defer os.RemoveAll(tempBaseDir) - tempKeysPath := filepath.Join(tempBaseDir, keystoremanager.PrivDir) + tempKeysPath := filepath.Join(tempBaseDir, notary.PrivDir) tempKeyStore, err := trustmanager.NewKeyFileStore(tempKeysPath, newPassphraseRetriever) if err != nil { return err @@ -127,7 +127,7 @@ func (cs *CryptoService) ExportAllKeys(dest io.Writer, newPassphraseRetriever pa defer os.RemoveAll(tempBaseDir) // Create temporary keystore to use as a staging area - tempKeysPath := filepath.Join(tempBaseDir, keystoremanager.PrivDir) + tempKeysPath := filepath.Join(tempBaseDir, notary.PrivDir) tempKeyStore, err := trustmanager.NewKeyFileStore(tempKeysPath, newPassphraseRetriever) if err != nil { return err @@ -141,7 +141,7 @@ func (cs *CryptoService) ExportAllKeys(dest io.Writer, newPassphraseRetriever pa zipWriter := zip.NewWriter(dest) - if err := addKeysToArchive(zipWriter, tempKeyStore, keystoremanager.PrivDir); err != nil { + if err := addKeysToArchive(zipWriter, tempKeyStore, notary.PrivDir); err != nil { return err } @@ -176,13 +176,13 @@ func (cs *CryptoService) ImportKeysZip(zipReader zip.Reader) error { // Note that using / as a separator is okay here - the zip // package guarantees that the separator will be / - if strings.HasPrefix(fNameTrimmed, keystoremanager.PrivDir) { + if strings.HasPrefix(fNameTrimmed, notary.PrivDir) { if fNameTrimmed[len(fNameTrimmed)-5:] == "_root" { if err = checkRootKeyIsEncrypted(fileBytes); err != nil { return err } } - keyName := strings.TrimPrefix(fNameTrimmed, keystoremanager.PrivDir) + keyName := strings.TrimPrefix(fNameTrimmed, notary.PrivDir) newKeys[keyName] = fileBytes } else { // This path inside the zip archive doesn't look like a @@ -194,9 +194,11 @@ func (cs *CryptoService) ImportKeysZip(zipReader zip.Reader) error { } for keyName, pemBytes := range newKeys { - parts := strings.Split(keyName, "_") + if keyName[len(keyName)-5:] == "_root" { + keyName = "root" + } for _, ks := range cs.keyStores { - if err := ks.ImportKey(pemBytes, parts[1]); err != nil { + if err := ks.ImportKey(pemBytes, keyName); err != nil { return err } } @@ -213,7 +215,7 @@ func (cs *CryptoService) ExportKeysByGUN(dest io.Writer, gun string, passphraseR defer os.RemoveAll(tempBaseDir) // Create temporary keystore to use as a staging area - tempKeysPath := filepath.Join(tempBaseDir, keystoremanager.PrivDir) + tempKeysPath := filepath.Join(tempBaseDir, notary.PrivDir) tempKeyStore, err := trustmanager.NewKeyFileStore(tempKeysPath, passphraseRetriever) if err != nil { return err @@ -231,7 +233,7 @@ func (cs *CryptoService) ExportKeysByGUN(dest io.Writer, gun string, passphraseR return ErrNoKeysFoundForGUN } - if err := addKeysToArchive(zipWriter, tempKeyStore, keystoremanager.PrivDir); err != nil { + if err := addKeysToArchive(zipWriter, tempKeyStore, notary.PrivDir); err != nil { return err } diff --git a/cryptoservice/import_export_test.go b/cryptoservice/import_export_test.go index 1db796696d..6b9ced61b4 100644 --- a/cryptoservice/import_export_test.go +++ b/cryptoservice/import_export_test.go @@ -1,447 +1,398 @@ package cryptoservice -//import ( -// "archive/zip" -// "bytes" -// "fmt" -// "io/ioutil" -// "net/http" -// "net/http/httptest" -// "os" -// "path/filepath" -// "strings" -// "testing" -// -// "github.com/docker/notary/client" -// "github.com/docker/notary/keystoremanager" -// "github.com/docker/notary/trustmanager" -// "github.com/docker/notary/tuf/data" -// "github.com/stretchr/testify/assert" -//) -// -//const timestampECDSAKeyJSON = ` -//{"keytype":"ecdsa","keyval":{"public":"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgl3rzMPMEKhS1k/AX16MM4PdidpjJr+z4pj0Td+30QnpbOIARgpyR1PiFztU8BZlqG3cUazvFclr2q/xHvfrqw==","private":"MHcCAQEEIDqtcdzU7H3AbIPSQaxHl9+xYECt7NpK7B1+6ep5cv9CoAoGCCqGSM49AwEHoUQDQgAEgl3rzMPMEKhS1k/AX16MM4PdidpjJr+z4pj0Td+30QnpbOIARgpyR1PiFztU8BZlqG3cUazvFclr2q/xHvfrqw=="}}` -// -//func createTestServer(t *testing.T) (*httptest.Server, *http.ServeMux) { -// mux := http.NewServeMux() -// // TUF will request /v2/docker.com/notary/_trust/tuf/timestamp.key -// // Return a canned timestamp.key -// mux.HandleFunc("/v2/docker.com/notary/_trust/tuf/timestamp.key", func(w http.ResponseWriter, r *http.Request) { -// // Also contains the private key, but for the purpose of this -// // test, we don't care -// fmt.Fprint(w, timestampECDSAKeyJSON) -// }) -// -// ts := httptest.NewServer(mux) -// -// return ts, mux -//} -// -//var oldPassphrase = "oldPassphrase" -//var exportPassphrase = "exportPassphrase" -//var oldPassphraseRetriever = func(string, string, bool, int) (string, bool, error) { return oldPassphrase, false, nil } -//var newPassphraseRetriever = func(string, string, bool, int) (string, bool, error) { return exportPassphrase, false, nil } -// -//func TestImportExportZip(t *testing.T) { -// gun := "docker.com/notary" -// -// // 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) -// -// ts, _ := createTestServer(t) -// defer ts.Close() -// -// repo, err := client.NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport, oldPassphraseRetriever) -// assert.NoError(t, err, "error creating repo: %s", err) -// -// rootKeyID, err := repo.KeyStoreManager.GenRootKey(data.ECDSAKey) -// assert.NoError(t, err, "error generating root key: %s", err) -// -// err = repo.Initialize(rootKeyID) -// assert.NoError(t, err, "error creating repository: %s", err) -// -// tempZipFile, err := ioutil.TempFile("", "notary-test-export-") -// tempZipFilePath := tempZipFile.Name() -// defer os.Remove(tempZipFilePath) -// -// err = repo.KeyStoreManager.ExportAllKeys(tempZipFile, newPassphraseRetriever) -// tempZipFile.Close() -// assert.NoError(t, err) -// -// // Reopen the zip file for importing -// zipReader, err := zip.OpenReader(tempZipFilePath) -// assert.NoError(t, err, "could not open zip file") -// -// // Map of files to expect in the zip file, with the passphrases -// passphraseByFile := make(map[string]string) -// -// // Add non-root keys to the map. These should use the new passphrase -// // because the passwords were chosen by the newPassphraseRetriever. -// privKeyMap := repo.KeyStoreManager.KeyStore.ListKeys() -// for privKeyName := range privKeyMap { -// _, alias, err := repo.KeyStoreManager.KeyStore.GetKey(privKeyName) -// assert.NoError(t, err, "privKey %s has no alias", privKeyName) -// -// if alias == "root" { -// continue -// } -// relKeyPath := filepath.Join("private", "tuf_keys", privKeyName+"_"+alias+".key") -// passphraseByFile[relKeyPath] = exportPassphrase -// } -// -// // Add root key to the map. This will use the export passphrase because it -// // will be reencrypted. -// relRootKey := filepath.Join("private", "root_keys", rootKeyID+"_root.key") -// passphraseByFile[relRootKey] = exportPassphrase -// -// // Iterate through the files in the archive, checking that the files -// // exist and are encrypted with the expected passphrase. -// for _, f := range zipReader.File { -// expectedPassphrase, present := passphraseByFile[f.Name] -// if !present { -// t.Fatalf("unexpected file %s in zip file", f.Name) -// } -// -// delete(passphraseByFile, f.Name) -// -// rc, err := f.Open() -// assert.NoError(t, err, "could not open file inside zip archive") -// -// pemBytes, err := ioutil.ReadAll(rc) -// assert.NoError(t, err, "could not read file from zip") -// -// _, err = trustmanager.ParsePEMPrivateKey(pemBytes, expectedPassphrase) -// assert.NoError(t, err, "PEM not encrypted with the expected passphrase") -// -// rc.Close() -// } -// -// zipReader.Close() -// -// // Are there any keys that didn't make it to the zip? -// for fileNotFound := range passphraseByFile { -// t.Fatalf("%s not found in zip", fileNotFound) -// } -// -// // Create new repo to test import -// tempBaseDir2, err := ioutil.TempDir("", "notary-test-") -// defer os.RemoveAll(tempBaseDir2) -// -// assert.NoError(t, err, "failed to create a temporary directory: %s", err) -// -// repo2, err := client.NewNotaryRepository(tempBaseDir2, gun, ts.URL, http.DefaultTransport, newPassphraseRetriever) -// assert.NoError(t, err, "error creating repo: %s", err) -// -// rootKeyID2, err := repo2.KeyStoreManager.GenRootKey(data.ECDSAKey) -// assert.NoError(t, err, "error generating root key: %s", err) -// -// err = repo2.Initialize(rootKeyID2) -// assert.NoError(t, err, "error creating repository: %s", err) -// -// // Reopen the zip file for importing -// zipReader, err = zip.OpenReader(tempZipFilePath) -// assert.NoError(t, err, "could not open zip file") -// -// // Now try with a valid passphrase. This time it should succeed. -// err = repo2.KeyStoreManager.ImportKeysZip(zipReader.Reader) -// assert.NoError(t, err) -// zipReader.Close() -// -// // Look for keys in private. The filenames should match the key IDs -// // in the repo's private key store. -// for privKeyName := range privKeyMap { -// _, alias, err := repo.KeyStoreManager.KeyStore.GetKey(privKeyName) -// assert.NoError(t, err, "privKey %s has no alias", privKeyName) -// -// if alias == "root" { -// continue -// } -// relKeyPath := filepath.Join("private", "tuf_keys", privKeyName+"_"+alias+".key") -// privKeyFileName := filepath.Join(tempBaseDir2, relKeyPath) -// _, err = os.Stat(privKeyFileName) -// assert.NoError(t, err, "missing private key: %s", privKeyName) -// } -// -// // Look for keys in root_keys -// // There should be a file named after the key ID of the root key we -// // passed in. -// rootKeyFilename := rootKeyID + "_root.key" -// _, err = os.Stat(filepath.Join(tempBaseDir2, "private", "root_keys", rootKeyFilename)) -// assert.NoError(t, err, "missing root key") -//} -// -//func TestImportExportGUN(t *testing.T) { -// gun := "docker.com/notary" -// -// // 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) -// -// ts, _ := createTestServer(t) -// defer ts.Close() -// -// repo, err := client.NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport, oldPassphraseRetriever) -// assert.NoError(t, err, "error creating repo: %s", err) -// -// rootKeyID, err := repo.KeyStoreManager.GenRootKey(data.ECDSAKey) -// assert.NoError(t, err, "error generating root key: %s", err) -// -// err = repo.Initialize(rootKeyID) -// assert.NoError(t, err, "error creating repository: %s", err) -// -// tempZipFile, err := ioutil.TempFile("", "notary-test-export-") -// tempZipFilePath := tempZipFile.Name() -// defer os.Remove(tempZipFilePath) -// -// err = repo.KeyStoreManager.ExportKeysByGUN(tempZipFile, gun, newPassphraseRetriever) -// assert.NoError(t, err) -// -// // With an invalid GUN, this should return an error -// err = repo.KeyStoreManager.ExportKeysByGUN(tempZipFile, "does.not.exist/in/repository", newPassphraseRetriever) -// assert.EqualError(t, err, keystoremanager.ErrNoKeysFoundForGUN.Error()) -// -// tempZipFile.Close() -// -// // Reopen the zip file for importing -// zipReader, err := zip.OpenReader(tempZipFilePath) -// assert.NoError(t, err, "could not open zip file") -// -// // Map of files to expect in the zip file, with the passphrases -// passphraseByFile := make(map[string]string) -// -// // Add keys non-root keys to the map. These should use the new passphrase -// // because they were formerly unencrypted. -// privKeyMap := repo.KeyStoreManager.KeyStore.ListKeys() -// for privKeyName := range privKeyMap { -// _, alias, err := repo.KeyStoreManager.KeyStore.GetKey(privKeyName) -// if err != nil { -// t.Fatalf("privKey %s has no alias", privKeyName) -// } -// if alias == "root" { -// continue -// } -// relKeyPath := filepath.Join("private", "tuf_keys", privKeyName+"_"+alias+".key") -// -// passphraseByFile[relKeyPath] = exportPassphrase -// } -// -// // Iterate through the files in the archive, checking that the files -// // exist and are encrypted with the expected passphrase. -// for _, f := range zipReader.File { -// -// expectedPassphrase, present := passphraseByFile[f.Name] -// if !present { -// t.Fatalf("unexpected file %s in zip file", f.Name) -// } -// -// delete(passphraseByFile, f.Name) -// -// rc, err := f.Open() -// assert.NoError(t, err, "could not open file inside zip archive") -// -// pemBytes, err := ioutil.ReadAll(rc) -// assert.NoError(t, err, "could not read file from zip") -// -// _, err = trustmanager.ParsePEMPrivateKey(pemBytes, expectedPassphrase) -// assert.NoError(t, err, "PEM not encrypted with the expected passphrase") -// -// rc.Close() -// } -// -// zipReader.Close() -// -// // Are there any keys that didn't make it to the zip? -// for fileNotFound := range passphraseByFile { -// t.Fatalf("%s not found in zip", fileNotFound) -// } -// -// // Create new repo to test import -// tempBaseDir2, err := ioutil.TempDir("", "notary-test-") -// defer os.RemoveAll(tempBaseDir2) -// -// assert.NoError(t, err, "failed to create a temporary directory: %s", err) -// -// repo2, err := client.NewNotaryRepository(tempBaseDir2, gun, ts.URL, http.DefaultTransport, oldPassphraseRetriever) -// assert.NoError(t, err, "error creating repo: %s", err) -// -// rootKeyID2, err := repo2.KeyStoreManager.GenRootKey(data.ECDSAKey) -// assert.NoError(t, err, "error generating root key: %s", err) -// -// err = repo2.Initialize(rootKeyID2) -// assert.NoError(t, err, "error creating repository: %s", err) -// -// // Reopen the zip file for importing -// zipReader, err = zip.OpenReader(tempZipFilePath) -// assert.NoError(t, err, "could not open zip file") -// -// // Now try with a valid passphrase. This time it should succeed. -// err = repo2.KeyStoreManager.ImportKeysZip(zipReader.Reader) -// assert.NoError(t, err) -// zipReader.Close() -// -// // Look for keys in private. The filenames should match the key IDs -// // in the repo's private key store. -// for privKeyName := range privKeyMap { -// _, alias, err := repo.KeyStoreManager.KeyStore.GetKey(privKeyName) -// if err != nil { -// t.Fatalf("privKey %s has no alias", privKeyName) -// } -// if alias == "root" { -// continue -// } -// relKeyPath := filepath.Join("private", "tuf_keys", privKeyName+"_"+alias+".key") -// privKeyFileName := filepath.Join(tempBaseDir2, relKeyPath) -// _, err = os.Stat(privKeyFileName) -// } -//} -// -//func TestImportExportRootKey(t *testing.T) { -// gun := "docker.com/notary" -// -// // 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) -// -// ts, _ := createTestServer(t) -// defer ts.Close() -// -// repo, err := client.NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport, oldPassphraseRetriever) -// assert.NoError(t, err, "error creating repo: %s", err) -// -// rootKeyID, err := repo.KeyStoreManager.GenRootKey(data.ECDSAKey) -// assert.NoError(t, err, "error generating root key: %s", err) -// -// err = repo.Initialize(rootKeyID) -// assert.NoError(t, err, "error creating repository: %s", err) -// -// tempKeyFile, err := ioutil.TempFile("", "notary-test-export-") -// tempKeyFilePath := tempKeyFile.Name() -// defer os.Remove(tempKeyFilePath) -// -// err = repo.KeyStoreManager.ExportRootKey(tempKeyFile, rootKeyID) -// assert.NoError(t, err) -// tempKeyFile.Close() -// -// // Create new repo to test import -// tempBaseDir2, err := ioutil.TempDir("", "notary-test-") -// defer os.RemoveAll(tempBaseDir2) -// -// assert.NoError(t, err, "failed to create a temporary directory: %s", err) -// -// repo2, err := client.NewNotaryRepository(tempBaseDir2, gun, ts.URL, http.DefaultTransport, oldPassphraseRetriever) -// assert.NoError(t, err, "error creating repo: %s", err) -// -// rootKeyID2, err := repo2.KeyStoreManager.GenRootKey(data.ECDSAKey) -// assert.NoError(t, err, "error generating root key: %s", err) -// -// err = repo2.Initialize(rootKeyID2) -// assert.NoError(t, err, "error creating repository: %s", err) -// -// keyReader, err := os.Open(tempKeyFilePath) -// assert.NoError(t, err, "could not open key file") -// -// err = repo2.KeyStoreManager.ImportRootKey(keyReader) -// assert.NoError(t, err) -// keyReader.Close() -// -// // Look for repo's root key in repo2 -// // There should be a file named after the key ID of the root key we -// // imported. -// rootKeyFilename := rootKeyID + "_root.key" -// _, err = os.Stat(filepath.Join(tempBaseDir2, "private", "root_keys", rootKeyFilename)) -// assert.NoError(t, err, "missing root key") -// -// // Try to import a decrypted version of the root key and make sure it -// // doesn't succeed -// pemBytes, err := ioutil.ReadFile(tempKeyFilePath) -// assert.NoError(t, err, "could not read key file") -// privKey, err := trustmanager.ParsePEMPrivateKey(pemBytes, oldPassphrase) -// assert.NoError(t, err, "could not decrypt key file") -// decryptedPEMBytes, err := trustmanager.KeyToPEM(privKey) -// assert.NoError(t, err, "could not convert key to PEM") -// -// err = repo2.KeyStoreManager.ImportRootKey(bytes.NewReader(decryptedPEMBytes)) -// assert.EqualError(t, err, keystoremanager.ErrRootKeyNotEncrypted.Error()) -// -// // Try to import garbage and make sure it doesn't succeed -// err = repo2.KeyStoreManager.ImportRootKey(strings.NewReader("this is not PEM")) -// assert.EqualError(t, err, keystoremanager.ErrNoValidPrivateKey.Error()) -// -// // Should be able to unlock the root key with the old password -// key, alias, err := repo2.KeyStoreManager.KeyStore.GetKey(rootKeyID) -// assert.NoError(t, err, "could not unlock root key") -// assert.Equal(t, "root", alias) -// assert.Equal(t, rootKeyID, key.ID()) -//} -// -//func TestImportExportRootKeyReencrypt(t *testing.T) { -// gun := "docker.com/notary" -// -// // 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) -// -// ts, _ := createTestServer(t) -// defer ts.Close() -// -// repo, err := client.NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport, oldPassphraseRetriever) -// assert.NoError(t, err, "error creating repo: %s", err) -// -// rootKeyID, err := repo.KeyStoreManager.GenRootKey(data.ECDSAKey) -// assert.NoError(t, err, "error generating root key: %s", err) -// -// err = repo.Initialize(rootKeyID) -// assert.NoError(t, err, "error creating repository: %s", err) -// -// tempKeyFile, err := ioutil.TempFile("", "notary-test-export-") -// tempKeyFilePath := tempKeyFile.Name() -// defer os.Remove(tempKeyFilePath) -// -// err = repo.KeyStoreManager.ExportRootKeyReencrypt(tempKeyFile, rootKeyID, newPassphraseRetriever) -// assert.NoError(t, err) -// tempKeyFile.Close() -// -// // Create new repo to test import -// tempBaseDir2, err := ioutil.TempDir("", "notary-test-") -// defer os.RemoveAll(tempBaseDir2) -// -// assert.NoError(t, err, "failed to create a temporary directory: %s", err) -// -// repo2, err := client.NewNotaryRepository(tempBaseDir2, gun, ts.URL, http.DefaultTransport, newPassphraseRetriever) -// assert.NoError(t, err, "error creating repo: %s", err) -// -// rootKeyID2, err := repo2.KeyStoreManager.GenRootKey(data.ECDSAKey) -// assert.NoError(t, err, "error generating root key: %s", err) -// -// err = repo2.Initialize(rootKeyID2) -// assert.NoError(t, err, "error creating repository: %s", err) -// -// keyReader, err := os.Open(tempKeyFilePath) -// assert.NoError(t, err, "could not open key file") -// -// err = repo2.KeyStoreManager.ImportRootKey(keyReader) -// assert.NoError(t, err) -// keyReader.Close() -// -// // Look for repo's root key in repo2 -// // There should be a file named after the key ID of the root key we -// // imported. -// rootKeyFilename := rootKeyID + "_root.key" -// _, err = os.Stat(filepath.Join(tempBaseDir2, "private", "root_keys", rootKeyFilename)) -// assert.NoError(t, err, "missing root key") -// -// // Should be able to unlock the root key with the new password -// key, alias, err := repo2.KeyStoreManager.KeyStore.GetKey(rootKeyID) -// assert.NoError(t, err, "could not unlock root key") -// assert.Equal(t, "root", alias) -// assert.Equal(t, rootKeyID, key.ID()) -//} +import ( + "archive/zip" + "bytes" + "fmt" + "io/ioutil" + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/docker/notary/trustmanager" + "github.com/docker/notary/tuf/data" + "github.com/stretchr/testify/assert" +) + +const timestampECDSAKeyJSON = ` +{"keytype":"ecdsa","keyval":{"public":"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgl3rzMPMEKhS1k/AX16MM4PdidpjJr+z4pj0Td+30QnpbOIARgpyR1PiFztU8BZlqG3cUazvFclr2q/xHvfrqw==","private":"MHcCAQEEIDqtcdzU7H3AbIPSQaxHl9+xYECt7NpK7B1+6ep5cv9CoAoGCCqGSM49AwEHoUQDQgAEgl3rzMPMEKhS1k/AX16MM4PdidpjJr+z4pj0Td+30QnpbOIARgpyR1PiFztU8BZlqG3cUazvFclr2q/xHvfrqw=="}}` + +func createTestServer(t *testing.T) (*httptest.Server, *http.ServeMux) { + mux := http.NewServeMux() + // TUF will request /v2/docker.com/notary/_trust/tuf/timestamp.key + // Return a canned timestamp.key + mux.HandleFunc("/v2/docker.com/notary/_trust/tuf/timestamp.key", func(w http.ResponseWriter, r *http.Request) { + // Also contains the private key, but for the purpose of this + // test, we don't care + fmt.Fprint(w, timestampECDSAKeyJSON) + }) + + ts := httptest.NewServer(mux) + + return ts, mux +} + +var oldPassphrase = "oldPassphrase" +var exportPassphrase = "exportPassphrase" +var oldPassphraseRetriever = func(string, string, bool, int) (string, bool, error) { return oldPassphrase, false, nil } +var newPassphraseRetriever = func(string, string, bool, int) (string, bool, error) { return exportPassphrase, false, nil } + +func TestImportExportZip(t *testing.T) { + gun := "docker.com/notary" + + // 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) + + fileStore, err := trustmanager.NewKeyFileStore(filepath.Join(tempBaseDir, "private"), newPassphraseRetriever) + cs := NewCryptoService(gun, fileStore) + pubKey, err := cs.Create(data.CanonicalRootRole, data.ECDSAKey) + assert.NoError(t, err) + + rootKeyID := pubKey.ID() + + tempZipFile, err := ioutil.TempFile("", "notary-test-export-") + tempZipFilePath := tempZipFile.Name() + defer os.Remove(tempZipFilePath) + + err = cs.ExportAllKeys(tempZipFile, newPassphraseRetriever) + tempZipFile.Close() + assert.NoError(t, err) + + // Reopen the zip file for importing + zipReader, err := zip.OpenReader(tempZipFilePath) + assert.NoError(t, err, "could not open zip file") + + // Map of files to expect in the zip file, with the passphrases + passphraseByFile := make(map[string]string) + + // Add non-root keys to the map. These should use the new passphrase + // because the passwords were chosen by the newPassphraseRetriever. + privKeyMap := cs.ListAllKeys() + for privKeyName := range privKeyMap { + _, alias, err := cs.GetPrivateKey(privKeyName) + assert.NoError(t, err, "privKey %s has no alias", privKeyName) + + if alias == "root" { + continue + } + relKeyPath := filepath.Join("private", "tuf_keys", privKeyName+"_"+alias+".key") + passphraseByFile[relKeyPath] = exportPassphrase + } + + // Add root key to the map. This will use the export passphrase because it + // will be reencrypted. + relRootKey := filepath.Join("private", "root_keys", rootKeyID+"_root.key") + passphraseByFile[relRootKey] = exportPassphrase + + // Iterate through the files in the archive, checking that the files + // exist and are encrypted with the expected passphrase. + for _, f := range zipReader.File { + expectedPassphrase, present := passphraseByFile[f.Name] + if !present { + t.Fatalf("unexpected file %s in zip file", f.Name) + } + + delete(passphraseByFile, f.Name) + + rc, err := f.Open() + assert.NoError(t, err, "could not open file inside zip archive") + + pemBytes, err := ioutil.ReadAll(rc) + assert.NoError(t, err, "could not read file from zip") + + _, err = trustmanager.ParsePEMPrivateKey(pemBytes, expectedPassphrase) + assert.NoError(t, err, "PEM not encrypted with the expected passphrase") + + rc.Close() + } + + zipReader.Close() + + // Are there any keys that didn't make it to the zip? + for fileNotFound := range passphraseByFile { + t.Fatalf("%s not found in zip", fileNotFound) + } + + // Create new repo to test import + tempBaseDir2, err := ioutil.TempDir("", "notary-test-") + defer os.RemoveAll(tempBaseDir2) + assert.NoError(t, err, "failed to create a temporary directory: %s", err) + + fileStore2, err := trustmanager.NewKeyFileStore(filepath.Join(tempBaseDir2, "private"), newPassphraseRetriever) + assert.NoError(t, err) + cs2 := NewCryptoService(gun, fileStore2) + + // Reopen the zip file for importing + zipReader, err = zip.OpenReader(tempZipFilePath) + assert.NoError(t, err, "could not open zip file") + + // Now try with a valid passphrase. This time it should succeed. + err = cs2.ImportKeysZip(zipReader.Reader) + assert.NoError(t, err) + zipReader.Close() + + // Look for keys in private. The filenames should match the key IDs + // in the repo's private key store. + for privKeyName := range privKeyMap { + _, alias, err := cs2.GetPrivateKey(privKeyName) + assert.NoError(t, err, "privKey %s has no alias", privKeyName) + + if alias == "root" { + continue + } + relKeyPath := filepath.Join("private", "tuf_keys", privKeyName+"_"+alias+".key") + privKeyFileName := filepath.Join(tempBaseDir2, relKeyPath) + _, err = os.Stat(privKeyFileName) + assert.NoError(t, err, "missing private key for role %s: %s", alias, privKeyName) + } + + // Look for keys in root_keys + // There should be a file named after the key ID of the root key we + // passed in. + rootKeyFilename := rootKeyID + "_root.key" + _, err = os.Stat(filepath.Join(tempBaseDir2, "private", "root_keys", rootKeyFilename)) + assert.NoError(t, err, "missing root key") +} + +func TestImportExportGUN(t *testing.T) { + gun := "docker.com/notary" + + // 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) + + fileStore, err := trustmanager.NewKeyFileStore(filepath.Join(tempBaseDir, "private"), newPassphraseRetriever) + cs := NewCryptoService(gun, fileStore) + _, err = cs.Create(data.CanonicalRootRole, data.ECDSAKey) + _, err = cs.Create(data.CanonicalTargetsRole, data.ECDSAKey) + _, err = cs.Create(data.CanonicalSnapshotRole, data.ECDSAKey) + assert.NoError(t, err) + + tempZipFile, err := ioutil.TempFile("", "notary-test-export-") + tempZipFilePath := tempZipFile.Name() + defer os.Remove(tempZipFilePath) + + err = cs.ExportKeysByGUN(tempZipFile, gun, newPassphraseRetriever) + assert.NoError(t, err) + + // With an invalid GUN, this should return an error + err = cs.ExportKeysByGUN(tempZipFile, "does.not.exist/in/repository", newPassphraseRetriever) + assert.EqualError(t, err, ErrNoKeysFoundForGUN.Error()) + + tempZipFile.Close() + + // Reopen the zip file for importing + zipReader, err := zip.OpenReader(tempZipFilePath) + assert.NoError(t, err, "could not open zip file") + + // Map of files to expect in the zip file, with the passphrases + passphraseByFile := make(map[string]string) + + // Add keys non-root keys to the map. These should use the new passphrase + // because they were formerly unencrypted. + privKeyMap := cs.ListAllKeys() + for privKeyName := range privKeyMap { + _, alias, err := cs.GetPrivateKey(privKeyName) + if err != nil { + t.Fatalf("privKey %s has no alias", privKeyName) + } + if alias == "root" { + continue + } + relKeyPath := filepath.Join("private", "tuf_keys", privKeyName+"_"+alias+".key") + + passphraseByFile[relKeyPath] = exportPassphrase + } + + // Iterate through the files in the archive, checking that the files + // exist and are encrypted with the expected passphrase. + for _, f := range zipReader.File { + + expectedPassphrase, present := passphraseByFile[f.Name] + if !present { + t.Fatalf("unexpected file %s in zip file", f.Name) + } + + delete(passphraseByFile, f.Name) + + rc, err := f.Open() + assert.NoError(t, err, "could not open file inside zip archive") + + pemBytes, err := ioutil.ReadAll(rc) + assert.NoError(t, err, "could not read file from zip") + + _, err = trustmanager.ParsePEMPrivateKey(pemBytes, expectedPassphrase) + assert.NoError(t, err, "PEM not encrypted with the expected passphrase") + + rc.Close() + } + + zipReader.Close() + + // Are there any keys that didn't make it to the zip? + for fileNotFound := range passphraseByFile { + t.Fatalf("%s not found in zip", fileNotFound) + } + + // Create new repo to test import + tempBaseDir2, err := ioutil.TempDir("", "notary-test-") + defer os.RemoveAll(tempBaseDir2) + assert.NoError(t, err, "failed to create a temporary directory: %s", err) + + fileStore2, err := trustmanager.NewKeyFileStore(filepath.Join(tempBaseDir2, "private"), newPassphraseRetriever) + cs2 := NewCryptoService(gun, fileStore2) + + // Reopen the zip file for importing + zipReader, err = zip.OpenReader(tempZipFilePath) + assert.NoError(t, err, "could not open zip file") + + // Now try with a valid passphrase. This time it should succeed. + err = cs2.ImportKeysZip(zipReader.Reader) + assert.NoError(t, err) + zipReader.Close() + + // Look for keys in private. The filenames should match the key IDs + // in the repo's private key store. + for privKeyName, role := range privKeyMap { + if role == "root" { + continue + } + _, alias, err := cs2.GetPrivateKey(privKeyName) + if err != nil { + t.Fatalf("privKey %s has no alias", privKeyName) + } + if alias == "root" { + continue + } + relKeyPath := filepath.Join("private", "tuf_keys", privKeyName+"_"+alias+".key") + privKeyFileName := filepath.Join(tempBaseDir2, relKeyPath) + _, err = os.Stat(privKeyFileName) + assert.NoError(t, err) + } +} + +func TestImportExportRootKey(t *testing.T) { + gun := "docker.com/notary" + + // 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) + + fileStore, err := trustmanager.NewKeyFileStore(filepath.Join(tempBaseDir, "private"), oldPassphraseRetriever) + cs := NewCryptoService(gun, fileStore) + pubKey, err := cs.Create(data.CanonicalRootRole, data.ECDSAKey) + assert.NoError(t, err) + + rootKeyID := pubKey.ID() + + tempKeyFile, err := ioutil.TempFile("", "notary-test-export-") + tempKeyFilePath := tempKeyFile.Name() + defer os.Remove(tempKeyFilePath) + + err = cs.ExportRootKey(tempKeyFile, rootKeyID) + assert.NoError(t, err) + tempKeyFile.Close() + + // Create new repo to test import + tempBaseDir2, err := ioutil.TempDir("", "notary-test-") + defer os.RemoveAll(tempBaseDir2) + assert.NoError(t, err, "failed to create a temporary directory: %s", err) + + fileStore2, err := trustmanager.NewKeyFileStore(filepath.Join(tempBaseDir2, "private"), oldPassphraseRetriever) + cs2 := NewCryptoService(gun, fileStore2) + + keyReader, err := os.Open(tempKeyFilePath) + assert.NoError(t, err, "could not open key file") + + err = cs2.ImportRootKey(keyReader) + assert.NoError(t, err) + keyReader.Close() + + // Look for repo's root key in repo2 + // There should be a file named after the key ID of the root key we + // imported. + rootKeyFilename := rootKeyID + "_root.key" + _, err = os.Stat(filepath.Join(tempBaseDir2, "private", "root_keys", rootKeyFilename)) + assert.NoError(t, err, "missing root key") + + // Try to import a decrypted version of the root key and make sure it + // doesn't succeed + pemBytes, err := ioutil.ReadFile(tempKeyFilePath) + assert.NoError(t, err, "could not read key file") + privKey, err := trustmanager.ParsePEMPrivateKey(pemBytes, oldPassphrase) + assert.NoError(t, err, "could not decrypt key file") + decryptedPEMBytes, err := trustmanager.KeyToPEM(privKey) + assert.NoError(t, err, "could not convert key to PEM") + + err = cs2.ImportRootKey(bytes.NewReader(decryptedPEMBytes)) + assert.EqualError(t, err, ErrRootKeyNotEncrypted.Error()) + + // Try to import garbage and make sure it doesn't succeed + err = cs2.ImportRootKey(strings.NewReader("this is not PEM")) + assert.EqualError(t, err, ErrNoValidPrivateKey.Error()) + + // Should be able to unlock the root key with the old password + key, alias, err := cs2.GetPrivateKey(rootKeyID) + assert.NoError(t, err, "could not unlock root key") + assert.Equal(t, "root", alias) + assert.Equal(t, rootKeyID, key.ID()) +} + +func TestImportExportRootKeyReencrypt(t *testing.T) { + gun := "docker.com/notary" + + // 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) + + fileStore, err := trustmanager.NewKeyFileStore(filepath.Join(tempBaseDir, "private"), oldPassphraseRetriever) + cs := NewCryptoService(gun, fileStore) + pubKey, err := cs.Create(data.CanonicalRootRole, data.ECDSAKey) + assert.NoError(t, err) + + rootKeyID := pubKey.ID() + + tempKeyFile, err := ioutil.TempFile("", "notary-test-export-") + tempKeyFilePath := tempKeyFile.Name() + defer os.Remove(tempKeyFilePath) + + err = cs.ExportRootKeyReencrypt(tempKeyFile, rootKeyID, newPassphraseRetriever) + assert.NoError(t, err) + tempKeyFile.Close() + + // Create new repo to test import + tempBaseDir2, err := ioutil.TempDir("", "notary-test-") + defer os.RemoveAll(tempBaseDir2) + assert.NoError(t, err, "failed to create a temporary directory: %s", err) + + fileStore2, err := trustmanager.NewKeyFileStore(filepath.Join(tempBaseDir2, "private"), newPassphraseRetriever) + cs2 := NewCryptoService(gun, fileStore2) + + keyReader, err := os.Open(tempKeyFilePath) + assert.NoError(t, err, "could not open key file") + + err = cs2.ImportRootKey(keyReader) + assert.NoError(t, err) + keyReader.Close() + + // Look for repo's root key in repo2 + // There should be a file named after the key ID of the root key we + // imported. + rootKeyFilename := rootKeyID + "_root.key" + _, err = os.Stat(filepath.Join(tempBaseDir2, "private", "root_keys", rootKeyFilename)) + assert.NoError(t, err, "missing root key") + + // Should be able to unlock the root key with the new password + key, alias, err := cs2.GetPrivateKey(rootKeyID) + assert.NoError(t, err, "could not unlock root key") + assert.Equal(t, "root", alias) + assert.Equal(t, rootKeyID, key.ID()) +} diff --git a/keystoremanager/keystoremanager.go b/keystoremanager/keystoremanager.go index 6b61f1c009..d22da537e0 100644 --- a/keystoremanager/keystoremanager.go +++ b/keystoremanager/keystoremanager.go @@ -24,9 +24,7 @@ type KeyStoreManager struct { } const ( - trustDir = "trusted_certificates" - // PrivDir is the name of the private directory - PrivDir = "private" + trustDir = "trusted_certificates" rsaRootKeySize = 4096 // Used for new root keys ) diff --git a/keystoremanager/keystoremanager_test.go b/keystoremanager/keystoremanager_test.go index da19c08ad5..1c5d3e316f 100644 --- a/keystoremanager/keystoremanager_test.go +++ b/keystoremanager/keystoremanager_test.go @@ -1,435 +1,436 @@ package keystoremanager -//import ( -// "bytes" -// "crypto/x509" -// "encoding/json" -// "io/ioutil" -// "os" -// "path/filepath" -// "testing" -// "text/template" -// -// "github.com/docker/notary/cryptoservice" -// "github.com/docker/notary/trustmanager" -// "github.com/docker/notary/tuf/data" -// "github.com/docker/notary/tuf/signed" -// "github.com/stretchr/testify/assert" -//) -// -//type SignedRSARootTemplate struct { -// RootPem string -//} -// -//var passphraseRetriever = func(string, string, bool, int) (string, bool, error) { return "passphrase", false, nil } -// -//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":"YlZwtCj028Xc23+KHfj6govFEY6hMbBXO5HT20F0I5ZeIPb1l7OmkjEiwp9ZHusClY+QeqiP1CFh\n/AfCbv4tLanqMkXPtm8UJJ1hMZVq86coieB32PQDj9k6x1hErHzvPUbOzTRW2BQkFFMZFkLDAd06\npH8lmxyPLOhdkVE8qIT7sBCy/4bQIGfvEX6yCDz84MZdcLNX5B9mzGi9A7gDloh9IEZxA8UgoI18\nSYpv/fYeSZSqM/ws2G+kiELGgTWhcZ+gOlF7ArM/DOlcC/NYqcvY1ugE6Gn7G8opre6NOofdRp3w\n603A2rMMvYTwqKLY6oX/d+07A2+WGHXPUy5otCAybWOw2hIZ35Jjmh12g6Dc6Qk4K2zXwAgvWwBU\nWlT8MlP1Tf7f80jnGjh0aARlHI4LCxlYU5L/pCaYuHgynujvLuzoOuiiPfJv7sYvKoQ8UieE1w//\nHc8E6tWtV5G2FguKLurMoKZ9FBWcanDO0fg5AWuG3qcgUJdvh9acQ33EKer1fqBxs6LSAUWo8rDt\nQkg+b55AW0YBukAW9IAfMySQGAS2e3mHZ8nK/ijaygCRu7/P+NgKY9/zpmfL2xgcNslLcANcSOOt\nhiJS6yqYM9i9G0af0yw/TxAT4ntwjVm8u52UyR/hXIiUc/mjZcYRbSmJOHws902+i+Z/qv72knk="}]}` -// -//func TestCertsToRemove(t *testing.T) { -// // Get a few certificates to test with -// cert1, err := trustmanager.LoadCertFromFile("../fixtures/secure.example.com.crt") -// assert.NoError(t, err) -// cert1KeyID, err := trustmanager.FingerprintCert(cert1) -// assert.NoError(t, err) -// -// // Get intermediate certificate -// cert2, err := trustmanager.LoadCertFromFile("../fixtures/self-signed_secure.example.com.crt") -// assert.NoError(t, err) -// cert2KeyID, err := trustmanager.FingerprintCert(cert2) -// assert.NoError(t, err) -// -// // Get leaf certificate -// cert3, err := trustmanager.LoadCertFromFile("../fixtures/self-signed_docker.com-notary.crt") -// assert.NoError(t, err) -// cert3KeyID, err := trustmanager.FingerprintCert(cert3) -// assert.NoError(t, err) -// -// // Call CertsToRemove with only one old and one new -// oldCerts := []*x509.Certificate{cert1} -// newCerts := []*x509.Certificate{cert2} -// -// certs := certsToRemove(oldCerts, newCerts) -// assert.Len(t, certs, 1) -// _, ok := certs[cert1KeyID] -// assert.True(t, ok) -// -// // Call CertsToRemove with two old and one new -// oldCerts = []*x509.Certificate{cert1, cert2} -// newCerts = []*x509.Certificate{cert3} -// -// certs = certsToRemove(oldCerts, newCerts) -// assert.Len(t, certs, 2) -// _, ok = certs[cert1KeyID] -// assert.True(t, ok) -// _, ok = certs[cert2KeyID] -// assert.True(t, ok) -// _, ok = certs[cert3KeyID] -// assert.False(t, ok) -// -// // Call CertsToRemove with two new and one old -// oldCerts = []*x509.Certificate{cert3} -// newCerts = []*x509.Certificate{cert2, cert1} -// -// certs = certsToRemove(oldCerts, newCerts) -// assert.Len(t, certs, 1) -// _, ok = certs[cert3KeyID] -// assert.True(t, ok) -// _, ok = certs[cert1KeyID] -// assert.False(t, ok) -// _, ok = certs[cert2KeyID] -// assert.False(t, ok) -// -// // Call CertsToRemove with three old certs and no new -// oldCerts = []*x509.Certificate{cert1, cert2, cert3} -// newCerts = []*x509.Certificate{} -// -// certs = certsToRemove(oldCerts, newCerts) -// assert.Len(t, certs, 0) -// _, ok = certs[cert1KeyID] -// assert.False(t, ok) -// _, ok = certs[cert2KeyID] -// assert.False(t, ok) -// _, ok = certs[cert3KeyID] -// assert.False(t, ok) -// -// // Call CertsToRemove with three new certs and no old -// oldCerts = []*x509.Certificate{} -// newCerts = []*x509.Certificate{cert1, cert2, cert3} -// -// certs = certsToRemove(oldCerts, newCerts) -// assert.Len(t, certs, 0) -// _, ok = certs[cert1KeyID] -// assert.False(t, ok) -// _, ok = certs[cert2KeyID] -// assert.False(t, ok) -// _, ok = certs[cert3KeyID] -// assert.False(t, ok) -// -//} -// -//func TestValidateRoot(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) -// -// keysPath := filepath.Join(tempBaseDir, PrivDir) -// fileKeyStore, err := trustmanager.NewKeyFileStore(keysPath, passphraseRetriever) -// assert.NoError(t, err) -// -// // Create a FileStoreManager -// keyStoreManager, err := NewKeyStoreManager(tempBaseDir, fileKeyStore) -// 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 using 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, &ErrValidationFail{Reason: "unable to retrieve valid leaf certificates"}) -// } -// -// // -// // 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, &ErrValidationFail{Reason: "unable to retrieve valid leaf certificates"}) -// } -// -// // -// // 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, &ErrValidationFail{Reason: "unable to retrieve valid leaf certificates"}) -// } -// -// // -// // This call to ValidateRoot will suceed in getting to the TUF validation, since -// // we are using 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 it has an invalid TUF signature -// // -// // Execute our template deleting the old buffer first -// signedRootBytes.Reset() -// templ, _ = template.New("SignedRSARootTemplate").Parse(signedRSARootTemplate) -// templ.Execute(&signedRootBytes, SignedRSARootTemplate{RootPem: validIntermediateAndCertRSA}) -// -// // Unmarshal our signedroot -// json.Unmarshal(signedRootBytes.Bytes(), &testSignedRoot) -// -// err = keyStoreManager.ValidateRoot(&testSignedRoot, "secure.example.com") -// if assert.Error(t, err, "An error was expected") { -// assert.Equal(t, err, &ErrValidationFail{Reason: "failed to validate integrity of roots"}) -// } -//} -// -//// TestValidateSuccessfulRootRotation runs through a full root certificate rotation -//// We test this with both an RSA and ECDSA root certificate -//func TestValidateSuccessfulRootRotation(t *testing.T) { -// testValidateSuccessfulRootRotation(t, data.ECDSAKey, data.ECDSAx509Key) -// if !testing.Short() { -// testValidateSuccessfulRootRotation(t, data.RSAKey, data.RSAx509Key) -// } -//} -// -//// Generates a KeyStoreManager in a temporary directory and returns the -//// manager and certificates for two keys which have been added to the keystore. -//// Also returns the temporary directory so it can be cleaned up. -//func filestoreWithTwoCerts(t *testing.T, gun, keyAlg string) ( -// string, *KeyStoreManager, []*x509.Certificate) { -// tempBaseDir, err := ioutil.TempDir("", "notary-test-") -// assert.NoError(t, err, "failed to create a temporary directory: %s", err) -// -// keysPath := filepath.Join(tempBaseDir, PrivDir) -// fileKeyStore, err := trustmanager.NewKeyFileStore(keysPath, passphraseRetriever) -// assert.NoError(t, err) -// -// // Create a FileStoreManager -// keyStoreManager, err := NewKeyStoreManager(tempBaseDir, fileKeyStore) -// assert.NoError(t, err) -// -// certs := make([]*x509.Certificate, 2) -// for i := 0; i < 2; i++ { -// keyID, err := keyStoreManager.GenRootKey(keyAlg) -// assert.NoError(t, err) -// -// key, _, err := keyStoreManager.KeyStore.GetKey(keyID) -// assert.NoError(t, err) -// -// cert, err := cryptoservice.GenerateCertificate(key, gun) -// assert.NoError(t, err) -// -// certs[i] = cert -// } -// return tempBaseDir, keyStoreManager, certs -//} -// -//func testValidateSuccessfulRootRotation(t *testing.T, keyAlg, rootKeyType string) { -// // The gun to test -// gun := "docker.com/notary" -// -// tempBaseDir, keyStoreManager, certs := filestoreWithTwoCerts(t, gun, keyAlg) -// defer os.RemoveAll(tempBaseDir) -// origRootCert := certs[0] -// replRootCert := certs[1] -// -// // Add the old root cert part of trustedCertificates -// keyStoreManager.AddTrustedCert(origRootCert) -// -// // We need the PEM representation of the replacement key to put it into the TUF data -// origRootPEMCert := trustmanager.CertToPEM(origRootCert) -// replRootPEMCert := trustmanager.CertToPEM(replRootCert) -// -// // Tuf key with PEM-encoded x509 certificate -// origRootKey := data.NewPublicKey(rootKeyType, origRootPEMCert) -// replRootKey := data.NewPublicKey(rootKeyType, replRootPEMCert) -// -// rootRole, err := data.NewRole("root", 1, []string{replRootKey.ID()}, nil, nil) -// assert.NoError(t, err) -// -// testRoot, err := data.NewRoot( -// map[string]data.PublicKey{replRootKey.ID(): replRootKey}, -// map[string]*data.RootRole{"root": &rootRole.RootRole}, -// false, -// ) -// assert.NoError(t, err, "Failed to create new root") -// -// signedTestRoot, err := testRoot.ToSigned() -// assert.NoError(t, err) -// -// cs := cryptoservice.NewCryptoService(gun, keyStoreManager.KeyStore) -// -// err = signed.Sign(cs, signedTestRoot, replRootKey) -// assert.NoError(t, err) -// -// err = signed.Sign(cs, signedTestRoot, origRootKey) -// assert.NoError(t, err) -// -// // -// // This call to ValidateRoot will succeed since we are using a valid PEM -// // encoded certificate, and have no other certificates for this CN -// // -// err = keyStoreManager.ValidateRoot(signedTestRoot, gun) -// assert.NoError(t, err) -// -// // Finally, validate the only trusted certificate that exists is the new one -// certs = keyStoreManager.trustedCertificateStore.GetCertificates() -// assert.Len(t, certs, 1) -// assert.Equal(t, certs[0], replRootCert) -//} -// -//// TestValidateRootRotationMissingOrigSig runs through a full root certificate rotation -//// where we are missing the original root key signature. Verification should fail. -//// We test this with both an RSA and ECDSA root certificate -//func TestValidateRootRotationMissingOrigSig(t *testing.T) { -// testValidateRootRotationMissingOrigSig(t, data.ECDSAKey, data.ECDSAx509Key) -// if !testing.Short() { -// testValidateRootRotationMissingOrigSig(t, data.RSAKey, data.RSAx509Key) -// } -//} -// -//func testValidateRootRotationMissingOrigSig(t *testing.T, keyAlg, rootKeyType string) { -// gun := "docker.com/notary" -// -// tempBaseDir, keyStoreManager, certs := filestoreWithTwoCerts(t, gun, keyAlg) -// defer os.RemoveAll(tempBaseDir) -// origRootCert := certs[0] -// replRootCert := certs[1] -// -// // Add the old root cert part of trustedCertificates -// keyStoreManager.AddTrustedCert(origRootCert) -// -// // We need the PEM representation of the replacement key to put it into the TUF data -// replRootPEMCert := trustmanager.CertToPEM(replRootCert) -// -// // Tuf key with PEM-encoded x509 certificate -// replRootKey := data.NewPublicKey(rootKeyType, replRootPEMCert) -// -// rootRole, err := data.NewRole("root", 1, []string{replRootKey.ID()}, nil, nil) -// assert.NoError(t, err) -// -// testRoot, err := data.NewRoot( -// map[string]data.PublicKey{replRootKey.ID(): replRootKey}, -// map[string]*data.RootRole{"root": &rootRole.RootRole}, -// false, -// ) -// assert.NoError(t, err, "Failed to create new root") -// -// signedTestRoot, err := testRoot.ToSigned() -// assert.NoError(t, err) -// -// // We only sign with the new key, and not with the original one. -// err = signed.Sign( -// cryptoservice.NewCryptoService(gun, keyStoreManager.KeyStore), -// signedTestRoot, replRootKey) -// assert.NoError(t, err) -// -// // -// // This call to ValidateRoot will succeed since we are using a valid PEM -// // encoded certificate, and have no other certificates for this CN -// // -// err = keyStoreManager.ValidateRoot(signedTestRoot, gun) -// assert.Error(t, err, "insuficient signatures on root") -// -// // Finally, validate the only trusted certificate that exists is still -// // the old one -// certs = keyStoreManager.trustedCertificateStore.GetCertificates() -// assert.Len(t, certs, 1) -// assert.Equal(t, certs[0], origRootCert) -//} -// -//// TestValidateRootRotationMissingNewSig runs through a full root certificate rotation -//// where we are missing the new root key signature. Verification should fail. -//// We test this with both an RSA and ECDSA root certificate -//func TestValidateRootRotationMissingNewSig(t *testing.T) { -// testValidateRootRotationMissingNewSig(t, data.ECDSAKey, data.ECDSAx509Key) -// if !testing.Short() { -// testValidateRootRotationMissingNewSig(t, data.RSAKey, data.RSAx509Key) -// } -//} -// -//func testValidateRootRotationMissingNewSig(t *testing.T, keyAlg, rootKeyType string) { -// gun := "docker.com/notary" -// -// tempBaseDir, keyStoreManager, certs := filestoreWithTwoCerts(t, gun, keyAlg) -// defer os.RemoveAll(tempBaseDir) -// origRootCert := certs[0] -// replRootCert := certs[1] -// -// // Add the old root cert part of trustedCertificates -// keyStoreManager.AddTrustedCert(origRootCert) -// -// // We need the PEM representation of the replacement key to put it into the TUF data -// origRootPEMCert := trustmanager.CertToPEM(origRootCert) -// replRootPEMCert := trustmanager.CertToPEM(replRootCert) -// -// // Tuf key with PEM-encoded x509 certificate -// origRootKey := data.NewPublicKey(rootKeyType, origRootPEMCert) -// replRootKey := data.NewPublicKey(rootKeyType, replRootPEMCert) -// -// rootRole, err := data.NewRole("root", 1, []string{replRootKey.ID()}, nil, nil) -// assert.NoError(t, err) -// -// testRoot, err := data.NewRoot( -// map[string]data.PublicKey{replRootKey.ID(): replRootKey}, -// map[string]*data.RootRole{"root": &rootRole.RootRole}, -// false, -// ) -// assert.NoError(t, err, "Failed to create new root") -// -// signedTestRoot, err := testRoot.ToSigned() -// assert.NoError(t, err) -// -// // We only sign with the old key, and not with the new one -// err = signed.Sign( -// cryptoservice.NewCryptoService(gun, keyStoreManager.KeyStore), -// signedTestRoot, origRootKey) -// assert.NoError(t, err) -// -// // -// // This call to ValidateRoot will succeed since we are using a valid PEM -// // encoded certificate, and have no other certificates for this CN -// // -// err = keyStoreManager.ValidateRoot(signedTestRoot, gun) -// assert.Error(t, err, "insuficient signatures on root") -// -// // Finally, validate the only trusted certificate that exists is still -// // the old one -// certs = keyStoreManager.trustedCertificateStore.GetCertificates() -// assert.Len(t, certs, 1) -// assert.Equal(t, certs[0], origRootCert) -//} +import ( + "bytes" + "crypto/x509" + "encoding/json" + "io/ioutil" + "os" + "path/filepath" + "testing" + "text/template" + + "github.com/docker/notary" + "github.com/docker/notary/cryptoservice" + "github.com/docker/notary/trustmanager" + "github.com/docker/notary/tuf/data" + "github.com/docker/notary/tuf/signed" + "github.com/stretchr/testify/assert" +) + +type SignedRSARootTemplate struct { + RootPem string +} + +var passphraseRetriever = func(string, string, bool, int) (string, bool, error) { return "passphrase", false, nil } + +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":"YlZwtCj028Xc23+KHfj6govFEY6hMbBXO5HT20F0I5ZeIPb1l7OmkjEiwp9ZHusClY+QeqiP1CFh\n/AfCbv4tLanqMkXPtm8UJJ1hMZVq86coieB32PQDj9k6x1hErHzvPUbOzTRW2BQkFFMZFkLDAd06\npH8lmxyPLOhdkVE8qIT7sBCy/4bQIGfvEX6yCDz84MZdcLNX5B9mzGi9A7gDloh9IEZxA8UgoI18\nSYpv/fYeSZSqM/ws2G+kiELGgTWhcZ+gOlF7ArM/DOlcC/NYqcvY1ugE6Gn7G8opre6NOofdRp3w\n603A2rMMvYTwqKLY6oX/d+07A2+WGHXPUy5otCAybWOw2hIZ35Jjmh12g6Dc6Qk4K2zXwAgvWwBU\nWlT8MlP1Tf7f80jnGjh0aARlHI4LCxlYU5L/pCaYuHgynujvLuzoOuiiPfJv7sYvKoQ8UieE1w//\nHc8E6tWtV5G2FguKLurMoKZ9FBWcanDO0fg5AWuG3qcgUJdvh9acQ33EKer1fqBxs6LSAUWo8rDt\nQkg+b55AW0YBukAW9IAfMySQGAS2e3mHZ8nK/ijaygCRu7/P+NgKY9/zpmfL2xgcNslLcANcSOOt\nhiJS6yqYM9i9G0af0yw/TxAT4ntwjVm8u52UyR/hXIiUc/mjZcYRbSmJOHws902+i+Z/qv72knk="}]}` + +func TestCertsToRemove(t *testing.T) { + // Get a few certificates to test with + cert1, err := trustmanager.LoadCertFromFile("../fixtures/secure.example.com.crt") + assert.NoError(t, err) + cert1KeyID, err := trustmanager.FingerprintCert(cert1) + assert.NoError(t, err) + + // Get intermediate certificate + cert2, err := trustmanager.LoadCertFromFile("../fixtures/self-signed_secure.example.com.crt") + assert.NoError(t, err) + cert2KeyID, err := trustmanager.FingerprintCert(cert2) + assert.NoError(t, err) + + // Get leaf certificate + cert3, err := trustmanager.LoadCertFromFile("../fixtures/self-signed_docker.com-notary.crt") + assert.NoError(t, err) + cert3KeyID, err := trustmanager.FingerprintCert(cert3) + assert.NoError(t, err) + + // Call CertsToRemove with only one old and one new + oldCerts := []*x509.Certificate{cert1} + newCerts := []*x509.Certificate{cert2} + + certs := certsToRemove(oldCerts, newCerts) + assert.Len(t, certs, 1) + _, ok := certs[cert1KeyID] + assert.True(t, ok) + + // Call CertsToRemove with two old and one new + oldCerts = []*x509.Certificate{cert1, cert2} + newCerts = []*x509.Certificate{cert3} + + certs = certsToRemove(oldCerts, newCerts) + assert.Len(t, certs, 2) + _, ok = certs[cert1KeyID] + assert.True(t, ok) + _, ok = certs[cert2KeyID] + assert.True(t, ok) + _, ok = certs[cert3KeyID] + assert.False(t, ok) + + // Call CertsToRemove with two new and one old + oldCerts = []*x509.Certificate{cert3} + newCerts = []*x509.Certificate{cert2, cert1} + + certs = certsToRemove(oldCerts, newCerts) + assert.Len(t, certs, 1) + _, ok = certs[cert3KeyID] + assert.True(t, ok) + _, ok = certs[cert1KeyID] + assert.False(t, ok) + _, ok = certs[cert2KeyID] + assert.False(t, ok) + + // Call CertsToRemove with three old certs and no new + oldCerts = []*x509.Certificate{cert1, cert2, cert3} + newCerts = []*x509.Certificate{} + + certs = certsToRemove(oldCerts, newCerts) + assert.Len(t, certs, 0) + _, ok = certs[cert1KeyID] + assert.False(t, ok) + _, ok = certs[cert2KeyID] + assert.False(t, ok) + _, ok = certs[cert3KeyID] + assert.False(t, ok) + + // Call CertsToRemove with three new certs and no old + oldCerts = []*x509.Certificate{} + newCerts = []*x509.Certificate{cert1, cert2, cert3} + + certs = certsToRemove(oldCerts, newCerts) + assert.Len(t, certs, 0) + _, ok = certs[cert1KeyID] + assert.False(t, ok) + _, ok = certs[cert2KeyID] + assert.False(t, ok) + _, ok = certs[cert3KeyID] + assert.False(t, ok) + +} + +func TestValidateRoot(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) + + keysPath := filepath.Join(tempBaseDir, notary.PrivDir) + fileKeyStore, err := trustmanager.NewKeyFileStore(keysPath, passphraseRetriever) + assert.NoError(t, err) + + // Create a FileStoreManager + keyStoreManager, err := NewKeyStoreManager(tempBaseDir, fileKeyStore) + 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 using 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, &ErrValidationFail{Reason: "unable to retrieve valid leaf certificates"}) + } + + // + // 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, &ErrValidationFail{Reason: "unable to retrieve valid leaf certificates"}) + } + + // + // 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, &ErrValidationFail{Reason: "unable to retrieve valid leaf certificates"}) + } + + // + // This call to ValidateRoot will suceed in getting to the TUF validation, since + // we are using 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 it has an invalid TUF signature + // + // Execute our template deleting the old buffer first + signedRootBytes.Reset() + templ, _ = template.New("SignedRSARootTemplate").Parse(signedRSARootTemplate) + templ.Execute(&signedRootBytes, SignedRSARootTemplate{RootPem: validIntermediateAndCertRSA}) + + // Unmarshal our signedroot + json.Unmarshal(signedRootBytes.Bytes(), &testSignedRoot) + + err = keyStoreManager.ValidateRoot(&testSignedRoot, "secure.example.com") + if assert.Error(t, err, "An error was expected") { + assert.Equal(t, err, &ErrValidationFail{Reason: "failed to validate integrity of roots"}) + } +} + +// TestValidateSuccessfulRootRotation runs through a full root certificate rotation +// We test this with both an RSA and ECDSA root certificate +func TestValidateSuccessfulRootRotation(t *testing.T) { + testValidateSuccessfulRootRotation(t, data.ECDSAKey, data.ECDSAx509Key) + if !testing.Short() { + testValidateSuccessfulRootRotation(t, data.RSAKey, data.RSAx509Key) + } +} + +// Generates a KeyStoreManager in a temporary directory and returns the +// manager and certificates for two keys which have been added to the keystore. +// Also returns the temporary directory so it can be cleaned up. +func filestoreWithTwoCerts(t *testing.T, gun, keyAlg string) ( + string, *KeyStoreManager, []*x509.Certificate) { + tempBaseDir, err := ioutil.TempDir("", "notary-test-") + assert.NoError(t, err, "failed to create a temporary directory: %s", err) + + keysPath := filepath.Join(tempBaseDir, notary.PrivDir) + fileKeyStore, err := trustmanager.NewKeyFileStore(keysPath, passphraseRetriever) + assert.NoError(t, err) + + // Create a FileStoreManager + keyStoreManager, err := NewKeyStoreManager(tempBaseDir, fileKeyStore) + assert.NoError(t, err) + + certs := make([]*x509.Certificate, 2) + for i := 0; i < 2; i++ { + keyID, err := keyStoreManager.GenRootKey(keyAlg) + assert.NoError(t, err) + + key, _, err := keyStoreManager.KeyStore.GetKey(keyID) + assert.NoError(t, err) + + cert, err := cryptoservice.GenerateCertificate(key, gun) + assert.NoError(t, err) + + certs[i] = cert + } + return tempBaseDir, keyStoreManager, certs +} + +func testValidateSuccessfulRootRotation(t *testing.T, keyAlg, rootKeyType string) { + // The gun to test + gun := "docker.com/notary" + + tempBaseDir, keyStoreManager, certs := filestoreWithTwoCerts(t, gun, keyAlg) + defer os.RemoveAll(tempBaseDir) + origRootCert := certs[0] + replRootCert := certs[1] + + // Add the old root cert part of trustedCertificates + keyStoreManager.AddTrustedCert(origRootCert) + + // We need the PEM representation of the replacement key to put it into the TUF data + origRootPEMCert := trustmanager.CertToPEM(origRootCert) + replRootPEMCert := trustmanager.CertToPEM(replRootCert) + + // Tuf key with PEM-encoded x509 certificate + origRootKey := data.NewPublicKey(rootKeyType, origRootPEMCert) + replRootKey := data.NewPublicKey(rootKeyType, replRootPEMCert) + + rootRole, err := data.NewRole("root", 1, []string{replRootKey.ID()}, nil, nil) + assert.NoError(t, err) + + testRoot, err := data.NewRoot( + map[string]data.PublicKey{replRootKey.ID(): replRootKey}, + map[string]*data.RootRole{"root": &rootRole.RootRole}, + false, + ) + assert.NoError(t, err, "Failed to create new root") + + signedTestRoot, err := testRoot.ToSigned() + assert.NoError(t, err) + + cs := cryptoservice.NewCryptoService(gun, keyStoreManager.KeyStore) + + err = signed.Sign(cs, signedTestRoot, replRootKey) + assert.NoError(t, err) + + err = signed.Sign(cs, signedTestRoot, origRootKey) + assert.NoError(t, err) + + // + // This call to ValidateRoot will succeed since we are using a valid PEM + // encoded certificate, and have no other certificates for this CN + // + err = keyStoreManager.ValidateRoot(signedTestRoot, gun) + assert.NoError(t, err) + + // Finally, validate the only trusted certificate that exists is the new one + certs = keyStoreManager.trustedCertificateStore.GetCertificates() + assert.Len(t, certs, 1) + assert.Equal(t, certs[0], replRootCert) +} + +// TestValidateRootRotationMissingOrigSig runs through a full root certificate rotation +// where we are missing the original root key signature. Verification should fail. +// We test this with both an RSA and ECDSA root certificate +func TestValidateRootRotationMissingOrigSig(t *testing.T) { + testValidateRootRotationMissingOrigSig(t, data.ECDSAKey, data.ECDSAx509Key) + if !testing.Short() { + testValidateRootRotationMissingOrigSig(t, data.RSAKey, data.RSAx509Key) + } +} + +func testValidateRootRotationMissingOrigSig(t *testing.T, keyAlg, rootKeyType string) { + gun := "docker.com/notary" + + tempBaseDir, keyStoreManager, certs := filestoreWithTwoCerts(t, gun, keyAlg) + defer os.RemoveAll(tempBaseDir) + origRootCert := certs[0] + replRootCert := certs[1] + + // Add the old root cert part of trustedCertificates + keyStoreManager.AddTrustedCert(origRootCert) + + // We need the PEM representation of the replacement key to put it into the TUF data + replRootPEMCert := trustmanager.CertToPEM(replRootCert) + + // Tuf key with PEM-encoded x509 certificate + replRootKey := data.NewPublicKey(rootKeyType, replRootPEMCert) + + rootRole, err := data.NewRole("root", 1, []string{replRootKey.ID()}, nil, nil) + assert.NoError(t, err) + + testRoot, err := data.NewRoot( + map[string]data.PublicKey{replRootKey.ID(): replRootKey}, + map[string]*data.RootRole{"root": &rootRole.RootRole}, + false, + ) + assert.NoError(t, err, "Failed to create new root") + + signedTestRoot, err := testRoot.ToSigned() + assert.NoError(t, err) + + // We only sign with the new key, and not with the original one. + err = signed.Sign( + cryptoservice.NewCryptoService(gun, keyStoreManager.KeyStore), + signedTestRoot, replRootKey) + assert.NoError(t, err) + + // + // This call to ValidateRoot will succeed since we are using a valid PEM + // encoded certificate, and have no other certificates for this CN + // + err = keyStoreManager.ValidateRoot(signedTestRoot, gun) + assert.Error(t, err, "insuficient signatures on root") + + // Finally, validate the only trusted certificate that exists is still + // the old one + certs = keyStoreManager.trustedCertificateStore.GetCertificates() + assert.Len(t, certs, 1) + assert.Equal(t, certs[0], origRootCert) +} + +// TestValidateRootRotationMissingNewSig runs through a full root certificate rotation +// where we are missing the new root key signature. Verification should fail. +// We test this with both an RSA and ECDSA root certificate +func TestValidateRootRotationMissingNewSig(t *testing.T) { + testValidateRootRotationMissingNewSig(t, data.ECDSAKey, data.ECDSAx509Key) + if !testing.Short() { + testValidateRootRotationMissingNewSig(t, data.RSAKey, data.RSAx509Key) + } +} + +func testValidateRootRotationMissingNewSig(t *testing.T, keyAlg, rootKeyType string) { + gun := "docker.com/notary" + + tempBaseDir, keyStoreManager, certs := filestoreWithTwoCerts(t, gun, keyAlg) + defer os.RemoveAll(tempBaseDir) + origRootCert := certs[0] + replRootCert := certs[1] + + // Add the old root cert part of trustedCertificates + keyStoreManager.AddTrustedCert(origRootCert) + + // We need the PEM representation of the replacement key to put it into the TUF data + origRootPEMCert := trustmanager.CertToPEM(origRootCert) + replRootPEMCert := trustmanager.CertToPEM(replRootCert) + + // Tuf key with PEM-encoded x509 certificate + origRootKey := data.NewPublicKey(rootKeyType, origRootPEMCert) + replRootKey := data.NewPublicKey(rootKeyType, replRootPEMCert) + + rootRole, err := data.NewRole("root", 1, []string{replRootKey.ID()}, nil, nil) + assert.NoError(t, err) + + testRoot, err := data.NewRoot( + map[string]data.PublicKey{replRootKey.ID(): replRootKey}, + map[string]*data.RootRole{"root": &rootRole.RootRole}, + false, + ) + assert.NoError(t, err, "Failed to create new root") + + signedTestRoot, err := testRoot.ToSigned() + assert.NoError(t, err) + + // We only sign with the old key, and not with the new one + err = signed.Sign( + cryptoservice.NewCryptoService(gun, keyStoreManager.KeyStore), + signedTestRoot, origRootKey) + assert.NoError(t, err) + + // + // This call to ValidateRoot will succeed since we are using a valid PEM + // encoded certificate, and have no other certificates for this CN + // + err = keyStoreManager.ValidateRoot(signedTestRoot, gun) + assert.Error(t, err, "insuficient signatures on root") + + // Finally, validate the only trusted certificate that exists is still + // the old one + certs = keyStoreManager.trustedCertificateStore.GetCertificates() + assert.Len(t, certs, 1) + assert.Equal(t, certs[0], origRootCert) +} diff --git a/signer/api/ecdsa_hardware_crypto_service.go b/signer/api/ecdsa_hardware_crypto_service.go index 1042f24d20..82f4bf5bd2 100644 --- a/signer/api/ecdsa_hardware_crypto_service.go +++ b/signer/api/ecdsa_hardware_crypto_service.go @@ -504,7 +504,8 @@ func (s *YubiKeyStore) AddKey(keyID, role string, privKey data.PrivateKey) error if k, ok := s.keys[keyID]; ok { if k.role == role { - + // already have the key and it's associated with the correct role + return nil } } diff --git a/trustmanager/keyfilestore.go b/trustmanager/keyfilestore.go index e364669e46..ed8339cfe1 100644 --- a/trustmanager/keyfilestore.go +++ b/trustmanager/keyfilestore.go @@ -329,12 +329,17 @@ func encryptAndAddKey(s LimitedFileStore, passwd string, cachedKeys map[string]* func importKey(s LimitedFileStore, passphraseRetriever passphrase.Retriever, cachedKeys map[string]*cachedKey, alias string, pemBytes []byte) error { + if alias != data.CanonicalRootRole { + return s.Add(alias, pemBytes) + } + privKey, passphrase, err := GetPasswdDecryptBytes(passphraseRetriever, pemBytes, "imported", alias) if err != nil { return err } - return encryptAndAddKey( - s, passphrase, cachedKeys, privKey.ID(), alias, privKey) + var name string + name = privKey.ID() + return encryptAndAddKey(s, passphrase, cachedKeys, name, alias, privKey) }