diff --git a/client/repo.go b/client/repo.go index 35820d09a1..c2b0b20278 100644 --- a/client/repo.go +++ b/client/repo.go @@ -9,7 +9,7 @@ import ( "github.com/docker/notary/cryptoservice" "github.com/docker/notary/keystoremanager" - "github.com/docker/notary/pkg/passphrase" + "github.com/docker/notary/passphrase" "github.com/docker/notary/trustmanager" "github.com/docker/notary/tuf/store" ) diff --git a/client/repo_pkcs11.go b/client/repo_pkcs11.go index 45c91cf0e2..8186e1a497 100644 --- a/client/repo_pkcs11.go +++ b/client/repo_pkcs11.go @@ -9,7 +9,7 @@ import ( "github.com/docker/notary/cryptoservice" "github.com/docker/notary/keystoremanager" - "github.com/docker/notary/pkg/passphrase" + "github.com/docker/notary/passphrase" "github.com/docker/notary/signer/api" "github.com/docker/notary/trustmanager" "github.com/docker/notary/tuf/store" diff --git a/cmd/notary-signer/main.go b/cmd/notary-signer/main.go index 5b16c622fa..fdaa91e824 100644 --- a/cmd/notary-signer/main.go +++ b/cmd/notary-signer/main.go @@ -131,20 +131,6 @@ func main() { cryptoServices := make(signer.CryptoServiceIndex) - pin := mainViper.GetString(pinCode) - pkcs11Lib := mainViper.GetString("crypto.pkcs11lib") - if pkcs11Lib != "" { - if pin == "" { - log.Fatalf("Using PIN is mandatory with pkcs11") - } - - ctx, session := SetupHSMEnv(pkcs11Lib, pin) - - defer cleanup(ctx, session) - - cryptoServices[data.RSAKey] = api.NewRSAHardwareCryptoService(ctx, session) - } - configDBType := strings.ToLower(mainViper.GetString("storage.backend")) dbURL := mainViper.GetString("storage.db_url") if configDBType != dbType || dbURL == "" { diff --git a/cmd/notary/keys.go b/cmd/notary/keys.go index 7cc2297e48..f239c6ebbc 100644 --- a/cmd/notary/keys.go +++ b/cmd/notary/keys.go @@ -9,10 +9,12 @@ import ( "strings" notaryclient "github.com/docker/notary/client" + "github.com/docker/notary/cryptoservice" "github.com/docker/notary/keystoremanager" - "github.com/docker/notary/pkg/passphrase" + "github.com/docker/notary/passphrase" "github.com/docker/notary/trustmanager" + "github.com/docker/notary/tuf/data" "github.com/spf13/cobra" ) @@ -117,10 +119,7 @@ func keysRemoveKey(cmd *cobra.Command, args []string) { if err != nil { fatalf("failed to create private key store in directory: %s", keysPath) } - keyStoreManager, err := keystoremanager.NewKeyStoreManager(trustDir, fileKeyStore) - if err != nil { - fatalf("failed to create a new truststore manager with directory: %s", trustDir) - } + cs := cryptoservice.NewCryptoService("", fileKeyStore) keyID := args[0] @@ -141,25 +140,8 @@ func keysRemoveKey(cmd *cobra.Command, args []string) { } } - // Choose the correct filestore to remove the key from - keyMap := keyStoreManager.KeyStore.ListKeys() - - // Attempt to find the full GUN to the key in the map - // This is irrelevant for removing root keys, but does no harm - var keyWithGUN string - for k := range keyMap { - if filepath.Base(k) == keyID { - keyWithGUN = k - } - } - - // If empty, we didn't find any matches - if keyWithGUN == "" { - fatalf("key with key ID: %s not found\n", keyID) - } - // Attempt to remove the key - err = keyStoreManager.KeyStore.RemoveKey(keyWithGUN) + err = cs.RemoveKey(keyID) if err != nil { fatalf("failed to remove key with key ID: %s, %v", keyID, err) } @@ -178,13 +160,10 @@ func keysList(cmd *cobra.Command, args []string) { if err != nil { fatalf("failed to create private key store in directory: %s", keysPath) } - keyStoreManager, err := keystoremanager.NewKeyStoreManager(trustDir, fileKeyStore) - if err != nil { - fatalf("failed to create a new truststore manager with directory: %s", trustDir) - } + cs := cryptoservice.NewCryptoService("", fileKeyStore) // Get a map of all the keys/roles - keysMap := keyStoreManager.KeyStore.ListKeys() + keysMap := cs.ListAllKeys() fmt.Println("") fmt.Println("# Root keys: ") @@ -236,17 +215,14 @@ func keysGenerateRootKey(cmd *cobra.Command, args []string) { if err != nil { fatalf("failed to create private key store in directory: %s", keysPath) } - keyStoreManager, err := keystoremanager.NewKeyStoreManager(trustDir, fileKeyStore) - if err != nil { - fatalf("failed to create a new truststore manager with directory: %s", trustDir) - } + cs := cryptoservice.NewCryptoService("", fileKeyStore) - keyID, err := keyStoreManager.GenRootKey(algorithm) + pubKey, err := cs.Create(data.CanonicalRootRole, algorithm) if err != nil { fatalf("failed to create a new root key: %v", err) } - fmt.Printf("Generated new %s key with keyID: %s\n", algorithm, keyID) + fmt.Printf("Generated new %s key with keyID: %s\n", algorithm, pubKey.ID()) } // keysExport exports a collection of keys to a ZIP file @@ -265,10 +241,7 @@ func keysExport(cmd *cobra.Command, args []string) { if err != nil { fatalf("failed to create private key store in directory: %s", keysPath) } - keyStoreManager, err := keystoremanager.NewKeyStoreManager(trustDir, fileKeyStore) - if err != nil { - fatalf("failed to create a new truststore manager with directory: %s", trustDir) - } + cs := cryptoservice.NewCryptoService("", fileKeyStore) exportFile, err := os.Create(exportFilename) if err != nil { @@ -279,9 +252,9 @@ func keysExport(cmd *cobra.Command, args []string) { // unlocking passphrase and reusing that. exportRetriever := passphrase.PromptRetriever() if keysExportGUN != "" { - err = keyStoreManager.ExportKeysByGUN(exportFile, keysExportGUN, exportRetriever) + err = cs.ExportKeysByGUN(exportFile, keysExportGUN, exportRetriever) } else { - err = keyStoreManager.ExportAllKeys(exportFile, exportRetriever) + err = cs.ExportAllKeys(exportFile, exportRetriever) } exportFile.Close() @@ -313,10 +286,7 @@ func keysExportRoot(cmd *cobra.Command, args []string) { if err != nil { fatalf("failed to create private key store in directory: %s", keysPath) } - keyStoreManager, err := keystoremanager.NewKeyStoreManager(trustDir, fileKeyStore) - if err != nil { - fatalf("failed to create a new truststore manager with directory: %s", trustDir) - } + cs := cryptoservice.NewCryptoService("", fileKeyStore) exportFile, err := os.Create(exportFilename) if err != nil { @@ -326,9 +296,9 @@ func keysExportRoot(cmd *cobra.Command, args []string) { // Must use a different passphrase retriever to avoid caching the // unlocking passphrase and reusing that. exportRetriever := passphrase.PromptRetriever() - err = keyStoreManager.ExportRootKeyReencrypt(exportFile, keyID, exportRetriever) + err = cs.ExportRootKeyReencrypt(exportFile, keyID, exportRetriever) } else { - err = keyStoreManager.ExportRootKey(exportFile, keyID) + err = cs.ExportRootKey(exportFile, keyID) } exportFile.Close() if err != nil { @@ -353,10 +323,7 @@ func keysImport(cmd *cobra.Command, args []string) { if err != nil { fatalf("failed to create private key store in directory: %s", keysPath) } - keyStoreManager, err := keystoremanager.NewKeyStoreManager(trustDir, fileKeyStore) - if err != nil { - fatalf("failed to create a new truststore manager with directory: %s", trustDir) - } + cs := cryptoservice.NewCryptoService("", fileKeyStore) zipReader, err := zip.OpenReader(importFilename) if err != nil { @@ -364,7 +331,7 @@ func keysImport(cmd *cobra.Command, args []string) { } defer zipReader.Close() - err = keyStoreManager.ImportKeysZip(zipReader.Reader) + err = cs.ImportKeysZip(zipReader.Reader) if err != nil { fatalf("error importing keys: %v", err) @@ -387,10 +354,7 @@ func keysImportRoot(cmd *cobra.Command, args []string) { if err != nil { fatalf("failed to create private key store in directory: %s", keysPath) } - keyStoreManager, err := keystoremanager.NewKeyStoreManager(trustDir, fileKeyStore) - if err != nil { - fatalf("failed to create a new truststore manager with directory: %s", trustDir) - } + cs := cryptoservice.NewCryptoService("", fileKeyStore) importFile, err := os.Open(importFilename) if err != nil { @@ -398,7 +362,7 @@ func keysImportRoot(cmd *cobra.Command, args []string) { } defer importFile.Close() - err = keyStoreManager.ImportRootKey(importFile) + err = cs.ImportRootKey(importFile) if err != nil { fatalf("error importing root key: %v", err) diff --git a/cmd/notary/main.go b/cmd/notary/main.go index 4311f7d9a4..668dd2377b 100644 --- a/cmd/notary/main.go +++ b/cmd/notary/main.go @@ -7,7 +7,7 @@ import ( "strings" "github.com/Sirupsen/logrus" - "github.com/docker/notary/pkg/passphrase" + "github.com/docker/notary/passphrase" "github.com/docker/notary/version" homedir "github.com/mitchellh/go-homedir" "github.com/spf13/cobra" diff --git a/cryptoservice/crypto_service.go b/cryptoservice/crypto_service.go index 61030af0d0..8b07a77d91 100644 --- a/cryptoservice/crypto_service.go +++ b/cryptoservice/crypto_service.go @@ -27,7 +27,7 @@ func NewCryptoService(gun string, keyStores ...trustmanager.KeyStore) *CryptoSer } // Create is used to generate keys for targets, snapshots and timestamps -func (ccs *CryptoService) Create(role, algorithm string) (data.PublicKey, error) { +func (cs *CryptoService) Create(role, algorithm string) (data.PublicKey, error) { var privKey data.PrivateKey var err error @@ -119,7 +119,7 @@ func (ccs *CryptoService) RemoveKey(keyID string) (err error) { // Sign returns the signatures for the payload with a set of keyIDs. It ignores // errors to sign and expects the called to validate if the number of returned // signatures is adequate. -func (ccs *CryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signature, error) { +func (cs *CryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signature, error) { signatures := make([]data.Signature, 0, len(keyIDs)) for _, keyid := range keyIDs { keyName := keyid @@ -152,9 +152,9 @@ func (ccs *CryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signatur } // ListKeys returns a list of key IDs valid for the given role -func (ccs *CryptoService) ListKeys(role string) []string { +func (cs *CryptoService) ListKeys(role string) []string { var res []string - for _, ks := range ccs.keyStores { + for _, ks := range cs.keyStores { for k, r := range ks.ListKeys() { if r == role { res = append(res, k) @@ -163,3 +163,14 @@ func (ccs *CryptoService) ListKeys(role string) []string { } return res } + +// ListKeys returns a list of key IDs valid for the given role +func (cs *CryptoService) ListAllKeys() map[string]string { + res := make(map[string]string) + for _, ks := range cs.keyStores { + for k, r := range ks.ListKeys() { + res[k] = r // keys are content addressed so don't care about overwrites + } + } + return res +} diff --git a/keystoremanager/import_export.go b/cryptoservice/import_export.go similarity index 74% rename from keystoremanager/import_export.go rename to cryptoservice/import_export.go index 6fd4735e3e..ddc3e3b899 100644 --- a/keystoremanager/import_export.go +++ b/cryptoservice/import_export.go @@ -1,4 +1,4 @@ -package keystoremanager +package cryptoservice import ( "archive/zip" @@ -11,7 +11,8 @@ import ( "path/filepath" "strings" - "github.com/docker/notary/pkg/passphrase" + "github.com/docker/notary/keystoremanager" + "github.com/docker/notary/passphrase" "github.com/docker/notary/trustmanager" ) @@ -33,11 +34,22 @@ var ( // ExportRootKey exports the specified root key to an io.Writer in PEM format. // The key's existing encryption is preserved. -func (km *KeyStoreManager) ExportRootKey(dest io.Writer, keyID string) error { - pemBytes, err := km.KeyStore.ExportKey(keyID) +func (cs *CryptoService) ExportRootKey(dest io.Writer, keyID string) error { + var ( + pemBytes []byte + err error + ) + + for _, ks := range cs.keyStores { + pemBytes, err = ks.ExportKey(keyID) + if err != nil { + continue + } + } if err != nil { return err } + nBytes, err := dest.Write(pemBytes) if err != nil { return err @@ -50,8 +62,8 @@ func (km *KeyStoreManager) ExportRootKey(dest io.Writer, keyID string) error { // ExportRootKeyReencrypt exports the specified root key to an io.Writer in // PEM format. The key is reencrypted with a new passphrase. -func (km *KeyStoreManager) ExportRootKeyReencrypt(dest io.Writer, keyID string, newPassphraseRetriever passphrase.Retriever) error { - privateKey, alias, err := km.KeyStore.GetKey(keyID) +func (cs *CryptoService) ExportRootKeyReencrypt(dest io.Writer, keyID string, newPassphraseRetriever passphrase.Retriever) error { + privateKey, role, err := cs.GetPrivateKey(keyID) if err != nil { return err } @@ -60,13 +72,13 @@ func (km *KeyStoreManager) ExportRootKeyReencrypt(dest io.Writer, keyID string, tempBaseDir, err := ioutil.TempDir("", "notary-key-export-") defer os.RemoveAll(tempBaseDir) - tempKeysPath := filepath.Join(tempBaseDir, PrivDir) + tempKeysPath := filepath.Join(tempBaseDir, keystoremanager.PrivDir) tempKeyStore, err := trustmanager.NewKeyFileStore(tempKeysPath, newPassphraseRetriever) if err != nil { return err } - err = tempKeyStore.AddKey(keyID, alias, privateKey) + err = tempKeyStore.AddKey(keyID, role, privateKey) if err != nil { return err } @@ -85,25 +97,10 @@ func (km *KeyStoreManager) ExportRootKeyReencrypt(dest io.Writer, keyID string, return nil } -// checkRootKeyIsEncrypted makes sure the root key is encrypted. We have -// internal assumptions that depend on this. -func checkRootKeyIsEncrypted(pemBytes []byte) error { - block, _ := pem.Decode(pemBytes) - if block == nil { - return ErrNoValidPrivateKey - } - - if !x509.IsEncryptedPEMBlock(block) { - return ErrRootKeyNotEncrypted - } - - return nil -} - // ImportRootKey imports a root in PEM format key from an io.Reader // It prompts for the key's passphrase to verify the data and to determine // the key ID. -func (km *KeyStoreManager) ImportRootKey(source io.Reader) error { +func (cs *CryptoService) ImportRootKey(source io.Reader) error { pemBytes, err := ioutil.ReadAll(source) if err != nil { return err @@ -113,14 +110,158 @@ func (km *KeyStoreManager) ImportRootKey(source io.Reader) error { return err } - if err = km.KeyStore.ImportKey(pemBytes, "root"); err != nil { - return err + for _, ks := range cs.keyStores { + // don't redeclare err, we want the value carried out of the loop + if err = ks.ImportKey(pemBytes, "root"); err != nil { + continue + } } return err } -func moveKeys(oldKeyStore, newKeyStore *trustmanager.KeyFileStore) error { +// ExportAllKeys exports all keys to an io.Writer in zip format. +// newPassphraseRetriever will be used to obtain passphrases to use to encrypt the existing keys. +func (cs *CryptoService) ExportAllKeys(dest io.Writer, newPassphraseRetriever passphrase.Retriever) error { + tempBaseDir, err := ioutil.TempDir("", "notary-key-export-") + defer os.RemoveAll(tempBaseDir) + + // Create temporary keystore to use as a staging area + tempKeysPath := filepath.Join(tempBaseDir, keystoremanager.PrivDir) + tempKeyStore, err := trustmanager.NewKeyFileStore(tempKeysPath, newPassphraseRetriever) + if err != nil { + return err + } + + for _, ks := range cs.keyStores { + if err := moveKeys(ks, tempKeyStore); err != nil { + return err + } + } + + zipWriter := zip.NewWriter(dest) + + if err := addKeysToArchive(zipWriter, tempKeyStore, keystoremanager.PrivDir); err != nil { + return err + } + + zipWriter.Close() + + return nil +} + +// ImportKeysZip imports keys from a zip file provided as an zip.Reader. The +// keys in the root_keys directory are left encrypted, but the other keys are +// decrypted with the specified passphrase. +func (cs *CryptoService) ImportKeysZip(zipReader zip.Reader) error { + // Temporarily store the keys in maps, so we can bail early if there's + // an error (for example, wrong passphrase), without leaving the key + // store in an inconsistent state + newKeys := make(map[string][]byte) + + // Iterate through the files in the archive. Don't add the keys + for _, f := range zipReader.File { + fNameTrimmed := strings.TrimSuffix(f.Name, filepath.Ext(f.Name)) + + rc, err := f.Open() + if err != nil { + return err + } + defer rc.Close() + + fileBytes, err := ioutil.ReadAll(rc) + if err != nil { + return nil + } + + // 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 fNameTrimmed[len(fNameTrimmed)-5:] == "_root" { + if err = checkRootKeyIsEncrypted(fileBytes); err != nil { + return err + } + } + keyName := strings.TrimPrefix(fNameTrimmed, keystoremanager.PrivDir) + newKeys[keyName] = fileBytes + } else { + // This path inside the zip archive doesn't look like a + // root key, non-root key, or alias. To avoid adding a file + // to the filestore that we won't be able to use, skip + // this file in the import. + continue + } + } + + for keyName, pemBytes := range newKeys { + parts := strings.Split(keyName, "_") + for _, ks := range cs.keyStores { + if err := ks.ImportKey(pemBytes, parts[1]); err != nil { + return err + } + } + } + + return nil +} + +// ExportKeysByGUN exports all keys associated with a specified GUN to an +// io.Writer in zip format. passphraseRetriever is used to select new passphrases to use to +// encrypt the keys. +func (cs *CryptoService) ExportKeysByGUN(dest io.Writer, gun string, passphraseRetriever passphrase.Retriever) error { + tempBaseDir, err := ioutil.TempDir("", "notary-key-export-") + defer os.RemoveAll(tempBaseDir) + + // Create temporary keystore to use as a staging area + tempKeysPath := filepath.Join(tempBaseDir, keystoremanager.PrivDir) + tempKeyStore, err := trustmanager.NewKeyFileStore(tempKeysPath, passphraseRetriever) + if err != nil { + return err + } + + for _, ks := range cs.keyStores { + if err := moveKeysByGUN(ks, tempKeyStore, gun); err != nil { + return err + } + } + + zipWriter := zip.NewWriter(dest) + + if len(tempKeyStore.ListKeys()) == 0 { + return ErrNoKeysFoundForGUN + } + + if err := addKeysToArchive(zipWriter, tempKeyStore, keystoremanager.PrivDir); err != nil { + return err + } + + zipWriter.Close() + + return nil +} + +func moveKeysByGUN(oldKeyStore, newKeyStore trustmanager.KeyStore, gun string) error { + for relKeyPath := range oldKeyStore.ListKeys() { + // Skip keys that aren't associated with this GUN + if !strings.HasPrefix(relKeyPath, filepath.FromSlash(gun)) { + continue + } + + privKey, alias, err := oldKeyStore.GetKey(relKeyPath) + if err != nil { + return err + } + + err = newKeyStore.AddKey(relKeyPath, alias, privKey) + if err != nil { + return err + } + } + + return nil +} + +func moveKeys(oldKeyStore, newKeyStore trustmanager.KeyStore) error { for f := range oldKeyStore.ListKeys() { privateKey, alias, err := oldKeyStore.GetKey(f) if err != nil { @@ -171,139 +312,17 @@ func addKeysToArchive(zipWriter *zip.Writer, newKeyStore *trustmanager.KeyFileSt return nil } -// ExportAllKeys exports all keys to an io.Writer in zip format. -// newPassphraseRetriever will be used to obtain passphrases to use to encrypt the existing keys. -func (km *KeyStoreManager) ExportAllKeys(dest io.Writer, newPassphraseRetriever passphrase.Retriever) error { - tempBaseDir, err := ioutil.TempDir("", "notary-key-export-") - defer os.RemoveAll(tempBaseDir) - - // Create temporary keystore to use as a staging area - tempKeysPath := filepath.Join(tempBaseDir, PrivDir) - tempKeyStore, err := trustmanager.NewKeyFileStore(tempKeysPath, newPassphraseRetriever) - if err != nil { - return err +// checkRootKeyIsEncrypted makes sure the root key is encrypted. We have +// internal assumptions that depend on this. +func checkRootKeyIsEncrypted(pemBytes []byte) error { + block, _ := pem.Decode(pemBytes) + if block == nil { + return ErrNoValidPrivateKey } - if err := moveKeys(km.KeyStore, tempKeyStore); err != nil { - return err - } - - zipWriter := zip.NewWriter(dest) - - if err := addKeysToArchive(zipWriter, tempKeyStore, PrivDir); err != nil { - return err - } - - zipWriter.Close() - - return nil -} - -// ImportKeysZip imports keys from a zip file provided as an zip.Reader. The -// keys in the root_keys directory are left encrypted, but the other keys are -// decrypted with the specified passphrase. -func (km *KeyStoreManager) ImportKeysZip(zipReader zip.Reader) error { - // Temporarily store the keys in maps, so we can bail early if there's - // an error (for example, wrong passphrase), without leaving the key - // store in an inconsistent state - newKeys := make(map[string][]byte) - - // Iterate through the files in the archive. Don't add the keys - for _, f := range zipReader.File { - fNameTrimmed := strings.TrimSuffix(f.Name, filepath.Ext(f.Name)) - - rc, err := f.Open() - if err != nil { - return err - } - - fileBytes, err := ioutil.ReadAll(rc) - if err != nil { - return nil - } - - // Note that using / as a separator is okay here - the zip - // package guarantees that the separator will be / - if strings.HasPrefix(fNameTrimmed, PrivDir) { - if fNameTrimmed[len(fNameTrimmed)-5:] == "_root" { - if err = checkRootKeyIsEncrypted(fileBytes); err != nil { - rc.Close() - return err - } - } - keyName := strings.TrimPrefix(fNameTrimmed, PrivDir) - newKeys[keyName] = fileBytes - } else { - // This path inside the zip archive doesn't look like a - // root key, non-root key, or alias. To avoid adding a file - // to the filestore that we won't be able to use, skip - // this file in the import. - rc.Close() - continue - } - - rc.Close() - } - - for keyName, pemBytes := range newKeys { - if err := km.KeyStore.Add(keyName, pemBytes); err != nil { - return err - } + if !x509.IsEncryptedPEMBlock(block) { + return ErrRootKeyNotEncrypted } return nil } - -func moveKeysByGUN(oldKeyStore, newKeyStore *trustmanager.KeyFileStore, gun string) error { - for relKeyPath := range oldKeyStore.ListKeys() { - // Skip keys that aren't associated with this GUN - if !strings.HasPrefix(relKeyPath, filepath.FromSlash(gun)) { - continue - } - - privKey, alias, err := oldKeyStore.GetKey(relKeyPath) - if err != nil { - return err - } - - err = newKeyStore.AddKey(relKeyPath, alias, privKey) - if err != nil { - return err - } - } - - return nil -} - -// ExportKeysByGUN exports all keys associated with a specified GUN to an -// io.Writer in zip format. passphraseRetriever is used to select new passphrases to use to -// encrypt the keys. -func (km *KeyStoreManager) ExportKeysByGUN(dest io.Writer, gun string, passphraseRetriever passphrase.Retriever) error { - tempBaseDir, err := ioutil.TempDir("", "notary-key-export-") - defer os.RemoveAll(tempBaseDir) - - // Create temporary keystore to use as a staging area - tempKeysPath := filepath.Join(tempBaseDir, PrivDir) - tempKeyStore, err := trustmanager.NewKeyFileStore(tempKeysPath, passphraseRetriever) - if err != nil { - return err - } - - if err := moveKeysByGUN(km.KeyStore, tempKeyStore, gun); err != nil { - return err - } - - zipWriter := zip.NewWriter(dest) - - if len(tempKeyStore.ListKeys()) == 0 { - return ErrNoKeysFoundForGUN - } - - if err := addKeysToArchive(zipWriter, tempKeyStore, PrivDir); err != nil { - return err - } - - zipWriter.Close() - - return nil -} diff --git a/cryptoservice/import_export_test.go b/cryptoservice/import_export_test.go new file mode 100644 index 0000000000..1db796696d --- /dev/null +++ b/cryptoservice/import_export_test.go @@ -0,0 +1,447 @@ +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()) +//} diff --git a/keystoremanager/import_export_test.go b/keystoremanager/import_export_test.go deleted file mode 100644 index afbf2e8c8e..0000000000 --- a/keystoremanager/import_export_test.go +++ /dev/null @@ -1,447 +0,0 @@ -package keystoremanager_test - -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()) -} diff --git a/keystoremanager/keystoremanager_test.go b/keystoremanager/keystoremanager_test.go index bae645bdac..da19c08ad5 100644 --- a/keystoremanager/keystoremanager_test.go +++ b/keystoremanager/keystoremanager_test.go @@ -1,435 +1,435 @@ 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/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) +//} diff --git a/pkg/passphrase/passphrase.go b/passphrase/passphrase.go similarity index 96% rename from pkg/passphrase/passphrase.go rename to passphrase/passphrase.go index b00368fa8e..6381b2af09 100644 --- a/pkg/passphrase/passphrase.go +++ b/passphrase/passphrase.go @@ -113,22 +113,23 @@ func PromptRetrieverWithInOut(in io.Reader, out io.Writer, aliasMap map[string]s indexOfLastSeparator = 0 } + var shortName string if len(keyName) > indexOfLastSeparator+idBytesToDisplay { if indexOfLastSeparator > 0 { keyNamePrefix := keyName[:indexOfLastSeparator] keyNameID := keyName[indexOfLastSeparator+1 : indexOfLastSeparator+idBytesToDisplay+1] - keyName = keyNamePrefix + " (" + keyNameID + ")" + shortName = keyNamePrefix + " (" + keyNameID + ")" } else { - keyName = keyName[indexOfLastSeparator : indexOfLastSeparator+idBytesToDisplay] + shortName = keyName[indexOfLastSeparator : indexOfLastSeparator+idBytesToDisplay] } } if createNew { - fmt.Fprintf(out, "Enter passphrase for new %s key with id %s: ", displayAlias, keyName) + fmt.Fprintf(out, "Enter passphrase for new %s key with id %s: ", displayAlias, shortName) } else if displayAlias == "yubikey" { fmt.Fprintf(out, "Enter the %s for the attached Yubikey: ", keyName) } else { - fmt.Fprintf(out, "Enter passphrase for %s key with id %s: ", displayAlias, keyName) + fmt.Fprintf(out, "Enter passphrase for %s key with id %s: ", displayAlias, shortName) } passphrase, err := stdin.ReadBytes('\n') diff --git a/signer/api/api_test.go b/signer/api/api_test.go index 729a3aaee7..17c598f6a7 100644 --- a/signer/api/api_test.go +++ b/signer/api/api_test.go @@ -15,6 +15,7 @@ import ( "github.com/docker/notary/signer/api" "github.com/docker/notary/trustmanager" "github.com/docker/notary/tuf/data" + "github.com/docker/notary/tuf/signed" "github.com/stretchr/testify/assert" pb "github.com/docker/notary/proto" @@ -131,7 +132,7 @@ func TestHSMCreateKeyHandler(t *testing.T) { defer ctx.CloseSession(session) defer ctx.Logout(session) - cryptoService := api.NewRSAHardwareCryptoService(ctx, session) + cryptoService := signed.NewEd25519() setup(signer.CryptoServiceIndex{data.RSAKey: cryptoService}) createKeyURL := fmt.Sprintf("%s/%s", createKeyBaseURL, data.RSAKey) @@ -182,7 +183,7 @@ func TestHSMSignHandler(t *testing.T) { defer ctx.CloseSession(session) defer ctx.Logout(session) - cryptoService := api.NewRSAHardwareCryptoService(ctx, session) + cryptoService := signed.NewEd25519() setup(signer.CryptoServiceIndex{data.RSAKey: cryptoService}) tufKey, _ := cryptoService.Create("", data.RSAKey) diff --git a/signer/api/ecdsa_hardware_crypto_service.go b/signer/api/ecdsa_hardware_crypto_service.go index 8da450e7d6..443c3563cc 100644 --- a/signer/api/ecdsa_hardware_crypto_service.go +++ b/signer/api/ecdsa_hardware_crypto_service.go @@ -15,7 +15,7 @@ import ( "math/big" "github.com/Sirupsen/logrus" - "github.com/docker/notary/pkg/passphrase" + "github.com/docker/notary/passphrase" "github.com/docker/notary/trustmanager" "github.com/docker/notary/tuf/data" "github.com/miekg/pkcs11" diff --git a/signer/api/rpc_api_test.go b/signer/api/rpc_api_test.go index c3c20152e9..2650cce806 100644 --- a/signer/api/rpc_api_test.go +++ b/signer/api/rpc_api_test.go @@ -7,7 +7,7 @@ import ( "testing" "github.com/docker/notary/cryptoservice" - "github.com/docker/notary/pkg/passphrase" + "github.com/docker/notary/passphrase" "github.com/docker/notary/signer" "github.com/docker/notary/signer/api" "github.com/docker/notary/trustmanager" diff --git a/signer/api/rsa_hardware_crypto_service.go b/signer/api/rsa_hardware_crypto_service.go deleted file mode 100644 index 640f2891d4..0000000000 --- a/signer/api/rsa_hardware_crypto_service.go +++ /dev/null @@ -1,212 +0,0 @@ -// +build pkcs11 - -package api - -import ( - "crypto" - "crypto/rand" - "crypto/rsa" - "crypto/sha256" - "crypto/x509" - "errors" - "log" - "math/big" - - "github.com/docker/notary/signer/keys" - "github.com/docker/notary/tuf/data" - "github.com/miekg/pkcs11" -) - -// RSAHardwareCryptoService is an implementation of SigningService -type RSAHardwareCryptoService struct { - keys map[string]*keys.HSMRSAKey - context *pkcs11.Ctx - session pkcs11.SessionHandle -} - -// ListKeys not implemented yet -func (s *RSAHardwareCryptoService) ListKeys(role string) []string { - return []string{} -} - -// Create creates a key and returns its public components -func (s *RSAHardwareCryptoService) Create(role, algo string) (data.PublicKey, error) { - // For now generate random labels for keys - // (diogo): add link between keyID and label in database so we can support multiple keys - randomLabel := make([]byte, 32) - _, err := rand.Read(randomLabel) - if err != nil { - return nil, errors.New("Could not generate a random key label.") - } - - // Set the public key template - // CKA_TOKEN: Guarantees key persistence in hardware - // CKA_LABEL: Identifies this specific key inside of the HSM - publicKeyTemplate := []*pkcs11.Attribute{ - pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true), - pkcs11.NewAttribute(pkcs11.CKA_PUBLIC_EXPONENT, []byte{3}), - pkcs11.NewAttribute(pkcs11.CKA_MODULUS_BITS, 2048), - pkcs11.NewAttribute(pkcs11.CKA_LABEL, string(randomLabel)), - } - privateKeyTemplate := []*pkcs11.Attribute{ - pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true), - pkcs11.NewAttribute(pkcs11.CKA_PRIVATE, true), - pkcs11.NewAttribute(pkcs11.CKA_SIGN, true), - pkcs11.NewAttribute(pkcs11.CKA_LABEL, string(randomLabel)), - } - - // Generate a new RSA private/public keypair inside of the HSM - pub, priv, err := s.context.GenerateKeyPair(s.session, - []*pkcs11.Mechanism{pkcs11.NewMechanism(pkcs11.CKM_RSA_PKCS_KEY_PAIR_GEN, nil)}, - publicKeyTemplate, privateKeyTemplate) - if err != nil { - return nil, errors.New("Could not generate a new key inside of the HSM.") - } - - // (diogo): This template is used for the GetAttribute - template := []*pkcs11.Attribute{ - pkcs11.NewAttribute(pkcs11.CKA_PUBLIC_EXPONENT, nil), - pkcs11.NewAttribute(pkcs11.CKA_MODULUS_BITS, nil), - pkcs11.NewAttribute(pkcs11.CKA_MODULUS, nil), - } - - // Retrieve the public-key material to be able to create a new HSMRSAKey - attr, err := s.context.GetAttributeValue(s.session, pub, template) - if err != nil { - return nil, errors.New("Failed to get Attribute value.") - } - - // We're going to store the elements of the RSA Public key, exponent and Modulus inside of exp and mod - var exp int - mod := big.NewInt(0) - - // Iterate through all the attributes of this key and saves CKA_PUBLIC_EXPONENT and CKA_MODULUS. Removes ordering specific issues. - for _, a := range attr { - if a.Type == pkcs11.CKA_PUBLIC_EXPONENT { - exp, _ = readInt(a.Value) - } - - if a.Type == pkcs11.CKA_MODULUS { - mod.SetBytes(a.Value) - } - } - - rsaPublicKey := rsa.PublicKey{N: mod, E: exp} - // Using x509 to Marshal the Public key into der encoding - pubBytes, err := x509.MarshalPKIXPublicKey(&rsaPublicKey) - if err != nil { - return nil, errors.New("Failed to Marshal public key.") - } - - // (diogo): Ideally I would like to return base64 PEM encoded public keys to the client - k := keys.NewHSMRSAKey(pubBytes, priv) - - keyID := k.ID() - - s.keys[keyID] = k - - return k, nil -} - -// RemoveKey removes a key from the key database -func (s *RSAHardwareCryptoService) RemoveKey(keyID string) error { - if _, ok := s.keys[keyID]; !ok { - return keys.ErrInvalidKeyID - } - - delete(s.keys, keyID) - return nil -} - -// GetKey returns the public components of a particular key -func (s *RSAHardwareCryptoService) GetKey(keyID string) data.PublicKey { - key, ok := s.keys[keyID] - if !ok { - return nil - } - return key -} - -// GetPrivateKey is not implemented -func (s *RSAHardwareCryptoService) GetPrivateKey(keyID string) (data.PrivateKey, string, error) { - return nil, "", errors.New("Not yet implemented") -} - -// Sign returns a signature for a given signature request -func (s *RSAHardwareCryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signature, error) { - signatures := make([]data.Signature, 0, len(keyIDs)) - for _, keyid := range keyIDs { - privateKey, present := s.keys[keyid] - if !present { - // We skip keys that aren't found - continue - } - - priv := privateKey.PKCS11ObjectHandle() - var sig []byte - var err error - for i := 0; i < 3; i++ { - s.context.SignInit(s.session, []*pkcs11.Mechanism{pkcs11.NewMechanism(pkcs11.CKM_SHA256_RSA_PKCS, nil)}, priv) - - sig, err = s.context.Sign(s.session, payload) - if err != nil { - log.Printf("Error while signing: %s", err) - continue - } - - digest := sha256.Sum256(payload) - pub, err := x509.ParsePKIXPublicKey(privateKey.Public()) - if err != nil { - log.Printf("Failed to parse public key: %s\n", err) - return nil, err - } - - rsaPub, ok := pub.(*rsa.PublicKey) - if !ok { - log.Printf("Value returned from ParsePKIXPublicKey was not an RSA public key") - return nil, err - } - - err = rsa.VerifyPKCS1v15(rsaPub, crypto.SHA256, digest[:], sig) - if err != nil { - log.Printf("Failed verification. Retrying: %s", err) - continue - } - break - } - - if sig == nil { - return nil, errors.New("Failed to create signature") - } - - signatures = append(signatures, data.Signature{ - KeyID: keyid, - Method: data.RSAPKCS1v15Signature, - Signature: sig[:], - }) - } - - return signatures, nil -} - -// NewRSAHardwareCryptoService returns an instance of RSAHardwareCryptoService -func NewRSAHardwareCryptoService(ctx *pkcs11.Ctx, session pkcs11.SessionHandle) *RSAHardwareCryptoService { - return &RSAHardwareCryptoService{ - keys: make(map[string]*keys.HSMRSAKey), - context: ctx, - session: session, - } -} - -// readInt converts a []byte into an int. It is used to convert the RSA Public key exponent into an int to create a crypto.PublicKey -func readInt(data []byte) (int, error) { - var ret int - if len(data) > 4 { - return 0, errors.New("Cannot convert byte array due to size") - } - - for i, a := range data { - ret |= (int(a) << uint(i*8)) - } - return ret, nil -} diff --git a/signer/keydbstore/keydbstore.go b/signer/keydbstore/keydbstore.go index f99d3e31c3..c3e89a190c 100644 --- a/signer/keydbstore/keydbstore.go +++ b/signer/keydbstore/keydbstore.go @@ -6,7 +6,7 @@ import ( "fmt" "sync" - "github.com/docker/notary/pkg/passphrase" + "github.com/docker/notary/passphrase" "github.com/docker/notary/trustmanager" "github.com/docker/notary/tuf/data" jose "github.com/dvsekhvalnov/jose2go" diff --git a/signer/signer_trust.go b/signer/signer_trust.go index e8a3963888..200eb5f583 100644 --- a/signer/signer_trust.go +++ b/signer/signer_trust.go @@ -105,6 +105,11 @@ func (trust *NotarySigner) ListKeys(role string) []string { return []string{} } +// ListAllKeys not supported for NotarySigner +func (trust *NotarySigner) ListAllKeys() map[string]string { + return map[string]string{} +} + // CheckHealth checks the health of one of the clients, since both clients run // from the same GRPC server. func (trust *NotarySigner) CheckHealth(timeout time.Duration) error { diff --git a/trustmanager/keyfilestore.go b/trustmanager/keyfilestore.go index af938b6eb9..aa3db3743b 100644 --- a/trustmanager/keyfilestore.go +++ b/trustmanager/keyfilestore.go @@ -5,7 +5,7 @@ import ( "strings" "sync" - "github.com/docker/notary/pkg/passphrase" + "github.com/docker/notary/passphrase" "github.com/docker/notary/tuf/data" ) diff --git a/trustmanager/keyfilestore_test.go b/trustmanager/keyfilestore_test.go index c499bb56ca..fa418bf5a6 100644 --- a/trustmanager/keyfilestore_test.go +++ b/trustmanager/keyfilestore_test.go @@ -8,7 +8,7 @@ import ( "path/filepath" "testing" - "github.com/docker/notary/pkg/passphrase" + "github.com/docker/notary/passphrase" "github.com/docker/notary/tuf/data" "github.com/stretchr/testify/assert" ) diff --git a/tuf/signed/ed25519.go b/tuf/signed/ed25519.go index fa16430193..1b9536b163 100644 --- a/tuf/signed/ed25519.go +++ b/tuf/signed/ed25519.go @@ -8,22 +8,30 @@ import ( "github.com/docker/notary/tuf/data" ) +type edCryptoKey struct { + role string + privKey data.PrivateKey +} + // Ed25519 implements a simple in memory cryptosystem for ED25519 keys type Ed25519 struct { - keys map[string]data.PrivateKey + keys map[string]edCryptoKey } // NewEd25519 initializes a new empty Ed25519 CryptoService that operates // entirely in memory func NewEd25519() *Ed25519 { return &Ed25519{ - make(map[string]data.PrivateKey), + make(map[string]edCryptoKey), } } // addKey allows you to add a private key -func (e *Ed25519) addKey(k data.PrivateKey) { - e.keys[k.ID()] = k +func (e *Ed25519) addKey(role string, k data.PrivateKey) { + e.keys[k.ID()] = edCryptoKey{ + role: role, + privKey: k, + } } // RemoveKey deletes a key from the signer @@ -41,12 +49,21 @@ func (e *Ed25519) ListKeys(role string) []string { return keyIDs } +// ListKeys returns the list of keys IDs for the role +func (e *Ed25519) ListAllKeys() map[string]string { + keys := make(map[string]string) + for id, edKey := range e.keys { + keys[id] = edKey.role + } + return keys +} + // Sign generates an Ed25519 signature over the data func (e *Ed25519) Sign(keyIDs []string, toSign []byte) ([]data.Signature, error) { signatures := make([]data.Signature, 0, len(keyIDs)) for _, keyID := range keyIDs { priv := [ed25519.PrivateKeySize]byte{} - copy(priv[:], e.keys[keyID].Private()) + copy(priv[:], e.keys[keyID].privKey.Private()) sig := ed25519.Sign(&priv, toSign) signatures = append(signatures, data.Signature{ KeyID: keyID, @@ -74,7 +91,7 @@ func (e *Ed25519) Create(role, algorithm string) (data.PublicKey, error) { return nil, err } - e.addKey(private) + e.addKey(role, private) return public, nil } @@ -83,8 +100,8 @@ func (e *Ed25519) Create(role, algorithm string) (data.PublicKey, error) { func (e *Ed25519) PublicKeys(keyIDs ...string) (map[string]data.PublicKey, error) { k := make(map[string]data.PublicKey) for _, keyID := range keyIDs { - if key, ok := e.keys[keyID]; ok { - k[keyID] = data.PublicKeyFromPrivate(key) + if edKey, ok := e.keys[keyID]; ok { + k[keyID] = data.PublicKeyFromPrivate(edKey.privKey) } } return k, nil @@ -92,10 +109,10 @@ func (e *Ed25519) PublicKeys(keyIDs ...string) (map[string]data.PublicKey, error // GetKey returns a single public key based on the ID func (e *Ed25519) GetKey(keyID string) data.PublicKey { - return data.PublicKeyFromPrivate(e.keys[keyID]) + return data.PublicKeyFromPrivate(e.keys[keyID].privKey) } // GetPrivateKey returns a single private key based on the ID func (e *Ed25519) GetPrivateKey(keyID string) (data.PrivateKey, string, error) { - return e.keys[keyID], "", nil + return e.keys[keyID].privKey, "", nil } diff --git a/tuf/signed/interface.go b/tuf/signed/interface.go index fc72adbe6f..e9db58c492 100644 --- a/tuf/signed/interface.go +++ b/tuf/signed/interface.go @@ -32,8 +32,11 @@ type KeyService interface { // RemoveKey deletes the specified key RemoveKey(keyID string) error - // ListKeys returns a map of IDs to role + // ListKeys returns a list of key IDs for the role ListKeys(role string) []string + + // ListAllKeys returns a map of all available signing key IDs to role + ListAllKeys() map[string]string } // CryptoService defines a unified Signing and Key Service as this diff --git a/tuf/signed/sign_test.go b/tuf/signed/sign_test.go index 7a62c0abda..f7228b5acf 100644 --- a/tuf/signed/sign_test.go +++ b/tuf/signed/sign_test.go @@ -38,6 +38,15 @@ func (mts *FailingCryptoService) ListKeys(role string) []string { return []string{mts.testKey.ID()} } +func (mts *FailingCryptoService) ListAllKeys() map[string]string { + return map[string]string{ + mts.testKey.ID(): "root", + mts.testKey.ID(): "targets", + mts.testKey.ID(): "snapshot", + mts.testKey.ID(): "timestamp", + } +} + func (mts *FailingCryptoService) GetKey(keyID string) data.PublicKey { if keyID == "testID" { return mts.testKey @@ -80,6 +89,15 @@ func (mts *MockCryptoService) ListKeys(role string) []string { return []string{mts.testKey.ID()} } +func (mts *MockCryptoService) ListAllKeys() map[string]string { + return map[string]string{ + mts.testKey.ID(): "root", + mts.testKey.ID(): "targets", + mts.testKey.ID(): "snapshot", + mts.testKey.ID(): "timestamp", + } +} + func (mts *MockCryptoService) GetPrivateKey(keyID string) (data.PrivateKey, string, error) { return nil, "", errors.New("Not implemented") } @@ -115,6 +133,15 @@ func (mts *StrictMockCryptoService) ListKeys(role string) []string { return []string{mts.testKey.ID()} } +func (mts *StrictMockCryptoService) ListAllKeys() map[string]string { + return map[string]string{ + mts.testKey.ID(): "root", + mts.testKey.ID(): "targets", + mts.testKey.ID(): "snapshot", + mts.testKey.ID(): "timestamp", + } +} + // Test signing and ensure the expected signature is added func TestBasicSign(t *testing.T) { testKey, _ := pem.Decode([]byte(testKeyPEM1))