Example #1
0
// ImportRoleKey imports a private key in PEM format key from a byte array
// It prompts for the key's passphrase to verify the data and to determine
// the key ID.
func (cs *CryptoService) ImportRoleKey(pemBytes []byte, role string, newPassphraseRetriever passphrase.Retriever) error {
	var alias string
	var err error
	if role == data.CanonicalRootRole {
		alias = role
		if err = checkRootKeyIsEncrypted(pemBytes); err != nil {
			return err
		}
	} else {
		// Parse the private key to get the key ID so that we can import it to the correct location
		privKey, err := trustmanager.ParsePEMPrivateKey(pemBytes, "")
		if err != nil {
			privKey, _, err = trustmanager.GetPasswdDecryptBytes(newPassphraseRetriever, pemBytes, role, string(role))
			if err != nil {
				return err
			}
		}
		// Since we're importing a non-root role, we need to pass the path as an alias
		alias = filepath.Join(notary.NonRootKeysSubdir, cs.gun, privKey.ID())
		// We also need to ensure that the role is properly set in the PEM headers
		pemBytes, err = trustmanager.KeyToPEM(privKey, role)
		if 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, alias); err == nil {
			return nil //bail on the first keystore we import to
		}
	}

	return err
}
Example #2
0
// 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, retriever notary.PassRetriever) 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 fNameTrimmed[len(fNameTrimmed)-5:] == "_root" {
			if err = CheckRootKeyIsEncrypted(fileBytes); err != nil {
				return err
			}
		}
		newKeys[fNameTrimmed] = fileBytes
	}

	for keyName, pemBytes := range newKeys {
		// Get the key role information as well as its data.PrivateKey representation
		_, keyInfo, err := trustmanager.KeyInfoFromPEM(pemBytes, keyName)
		if err != nil {
			return err
		}
		privKey, err := trustmanager.ParsePEMPrivateKey(pemBytes, "")
		if err != nil {
			privKey, _, err = trustmanager.GetPasswdDecryptBytes(retriever, pemBytes, "", "imported "+keyInfo.Role)
			if err != nil {
				return err
			}
		}
		// Add the key to our cryptoservice, will add to the first successful keystore
		if err = cs.AddKey(keyInfo.Role, keyInfo.Gun, privKey); err != nil {
			return err
		}
	}

	return nil
}
Example #3
0
func moveKeysByGUN(oldKeyStore, newKeyStore *trustmanager.KeyFileStore, gun, outputPassphrase string) error {
	// List all files but no symlinks
	for _, f := range oldKeyStore.ListFiles(false) {
		fullKeyPath := strings.TrimSpace(strings.TrimSuffix(f, filepath.Ext(f)))
		relKeyPath := strings.TrimPrefix(fullKeyPath, oldKeyStore.BaseDir())
		relKeyPath = strings.TrimPrefix(relKeyPath, string(filepath.Separator))

		// Skip keys that aren't associated with this GUN
		if !strings.HasPrefix(relKeyPath, filepath.FromSlash(gun)) {
			continue
		}

		pemBytes, err := oldKeyStore.Get(relKeyPath)
		if err != nil {
			return err
		}

		block, _ := pem.Decode(pemBytes)
		if block == nil {
			return ErrNoValidPrivateKey
		}

		if x509.IsEncryptedPEMBlock(block) {
			return ErrNonRootKeyEncrypted
		}

		// Key is not encrypted. Parse it, and add it
		// to the temporary store as an encrypted key.
		privKey, err := trustmanager.ParsePEMPrivateKey(pemBytes, "")
		if err != nil {
			return err
		}
		err = newKeyStore.AddEncryptedKey(relKeyPath, privKey, outputPassphrase)
		if err != nil {
			return err
		}
	}

	return nil
}
Example #4
0
func moveKeysWithNewPassphrase(oldKeyStore, newKeyStore *trustmanager.KeyFileStore, outputPassphrase string) error {
	// List all files but no symlinks
	for _, f := range oldKeyStore.ListFiles(false) {
		fullKeyPath := strings.TrimSpace(strings.TrimSuffix(f, filepath.Ext(f)))
		relKeyPath := strings.TrimPrefix(fullKeyPath, oldKeyStore.BaseDir())
		relKeyPath = strings.TrimPrefix(relKeyPath, string(filepath.Separator))

		pemBytes, err := oldKeyStore.Get(relKeyPath)
		if err != nil {
			return err
		}

		block, _ := pem.Decode(pemBytes)
		if block == nil {
			return ErrNoValidPrivateKey
		}

		if !x509.IsEncryptedPEMBlock(block) {
			// Key is not encrypted. Parse it, and add it
			// to the temporary store as an encrypted key.
			privKey, err := trustmanager.ParsePEMPrivateKey(pemBytes, "")
			if err != nil {
				return err
			}
			err = newKeyStore.AddEncryptedKey(relKeyPath, privKey, outputPassphrase)
		} else {
			// Encrypted key - pass it through without
			// decrypting
			err = newKeyStore.Add(relKeyPath, pemBytes)
		}

		if err != nil {
			return err
		}
	}

	return nil
}
Example #5
0
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.String())
	assert.NoError(t, err, "error generating root key: %s", err)

	rootCryptoService, err := repo.KeyStoreManager.GetRootCryptoService(rootKeyID)
	assert.NoError(t, err, "error retrieving root key: %s", err)

	err = repo.Initialize(rootCryptoService)
	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.NonRootKeyStore().ListKeys()
	for privKeyName := range privKeyMap {
		_, alias, err := repo.KeyStoreManager.NonRootKeyStore().GetKey(privKeyName)
		assert.NoError(t, err, "privKey %s has no alias", privKeyName)

		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", rootCryptoService.ID()+"_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 {
		if keystoremanager.IsZipSymlink(f) {
			continue
		}

		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.String())
	assert.NoError(t, err, "error generating root key: %s", err)

	rootCryptoService2, err := repo2.KeyStoreManager.GetRootCryptoService(rootKeyID2)
	assert.NoError(t, err, "error retrieving root key: %s", err)

	err = repo2.Initialize(rootCryptoService2)
	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.NonRootKeyStore().GetKey(privKeyName)
		assert.NoError(t, err, "privKey %s has no alias", privKeyName)

		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 := rootCryptoService.ID() + "_root.key"
	_, err = os.Stat(filepath.Join(tempBaseDir2, "private", "root_keys", rootKeyFilename))
	assert.NoError(t, err, "missing root key")

	// Look for symlinks in the new repo matching symlinks in the old repo
	numSymlinks := 0

	oldRootKeyStore := repo.KeyStoreManager.RootKeyStore()
	newRootKeyStore := repo2.KeyStoreManager.RootKeyStore()

	for _, relKeyPath := range oldRootKeyStore.ListFiles(true) {
		fullKeyPath := filepath.Join(oldRootKeyStore.BaseDir(), relKeyPath)

		fi, err := os.Lstat(fullKeyPath)
		assert.NoError(t, err, "lstat failed")

		if (fi.Mode() & os.ModeSymlink) != 0 {
			numSymlinks++

			oldTarget, err := os.Readlink(fullKeyPath)
			assert.NoError(t, err, "readlink failed on old repo")

			newTarget, err := os.Readlink(filepath.Join(newRootKeyStore.BaseDir(), relKeyPath))
			assert.NoError(t, err, "symlink not found in new repo")

			assert.Equal(t, oldTarget, newTarget, "symlink targets do not match")
		}
	}

	if numSymlinks == 0 {
		t.Fatal("no symlinks found in original repo")
	}
}
Example #6
0
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.String())
	assert.NoError(t, err, "error generating root key: %s", err)

	rootCryptoService, err := repo.KeyStoreManager.GetRootCryptoService(rootKeyID)
	assert.NoError(t, err, "error retrieving root key: %s", err)

	err = repo.Initialize(rootCryptoService)
	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.String())
	assert.NoError(t, err, "error generating root key: %s", err)

	rootCryptoService2, err := repo2.KeyStoreManager.GetRootCryptoService(rootKeyID2)
	assert.NoError(t, err, "error retrieving root key: %s", err)

	err = repo2.Initialize(rootCryptoService2)
	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, rootKeyID)
	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), rootKeyID)
	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"), rootKeyID)
	assert.EqualError(t, err, keystoremanager.ErrNoValidPrivateKey.Error())

	// Should be able to unlock the root key with the old password
	key, alias, err := repo2.KeyStoreManager.RootKeyStore().GetKey(rootKeyID)
	assert.NoError(t, err, "could not unlock root key")
	assert.Equal(t, "root", alias)
	assert.Equal(t, rootKeyID, key.ID())
}
Example #7
0
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.String())
	assert.NoError(t, err, "error generating root key: %s", err)

	rootCryptoService, err := repo.KeyStoreManager.GetRootCryptoService(rootKeyID)
	assert.NoError(t, err, "error retrieving root key: %s", err)

	err = repo.Initialize(rootCryptoService)
	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.NonRootKeyStore().ListKeys()
	for privKeyName := range privKeyMap {
		_, alias, err := repo.KeyStoreManager.NonRootKeyStore().GetKey(privKeyName)
		if err != nil {
			t.Fatalf("privKey %s has no alias", privKeyName)
		}
		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.String())
	assert.NoError(t, err, "error generating root key: %s", err)

	rootCryptoService2, err := repo2.KeyStoreManager.GetRootCryptoService(rootKeyID2)
	assert.NoError(t, err, "error retrieving root key: %s", err)

	err = repo2.Initialize(rootCryptoService2)
	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.NonRootKeyStore().GetKey(privKeyName)
		if err != nil {
			t.Fatalf("privKey %s has no alias", privKeyName)
		}
		relKeyPath := filepath.Join("private", "tuf_keys", privKeyName+"_"+alias+".key")
		privKeyFileName := filepath.Join(tempBaseDir2, relKeyPath)
		_, err = os.Stat(privKeyFileName)
	}
}
Example #8
0
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)
	require.NoError(t, err, "failed to create a temporary directory: %s", err)

	fileStore, err := trustmanager.NewKeyFileStore(tempBaseDir, newPassphraseRetriever)
	cs := NewCryptoService(fileStore)
	pubKey, err := cs.Create(data.CanonicalRootRole, gun, data.ECDSAKey)
	require.NoError(t, err)

	rootKeyID := pubKey.ID()

	tempZipFile, err := ioutil.TempFile("", "notary-test-export-")
	tempZipFilePath := tempZipFile.Name()
	defer os.Remove(tempZipFilePath)

	err = cs.ExportAllKeys(tempZipFile, newPassphraseRetriever)
	tempZipFile.Close()
	require.NoError(t, err)

	// Reopen the zip file for importing
	zipReader, err := zip.OpenReader(tempZipFilePath)
	require.NoError(t, err, "could not open zip file")

	// Map of files to expect in the zip file, with the passphrases
	passphraseByFile := make(map[string]string)

	// Add non-root keys to the map. These should use the new passphrase
	// because the passwords were chosen by the newPassphraseRetriever.
	privKeyMap := cs.ListAllKeys()
	for privKeyName := range privKeyMap {
		_, alias, err := cs.GetPrivateKey(privKeyName)
		require.NoError(t, err, "privKey %s has no alias", privKeyName)

		if alias == data.CanonicalRootRole {
			continue
		}
		relKeyPath := filepath.Join(notary.NonRootKeysSubdir, privKeyName+".key")
		passphraseByFile[relKeyPath] = exportPassphrase
	}

	// Add root key to the map. This will use the export passphrase because it
	// will be reencrypted.
	relRootKey := filepath.Join(notary.RootKeysSubdir, rootKeyID+".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]
		require.True(t, present, "unexpected file %s in zip file", f.Name)

		delete(passphraseByFile, f.Name)

		rc, err := f.Open()
		require.NoError(t, err, "could not open file inside zip archive")

		pemBytes, err := ioutil.ReadAll(rc)
		require.NoError(t, err, "could not read file from zip")

		_, err = trustmanager.ParsePEMPrivateKey(pemBytes, expectedPassphrase)
		require.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?
	require.Len(t, passphraseByFile, 0)

	// Create new repo to test import
	tempBaseDir2, err := ioutil.TempDir("", "notary-test-")
	defer os.RemoveAll(tempBaseDir2)
	require.NoError(t, err, "failed to create a temporary directory: %s", err)

	fileStore2, err := trustmanager.NewKeyFileStore(tempBaseDir2, newPassphraseRetriever)
	require.NoError(t, err)
	cs2 := NewCryptoService(fileStore2)

	// Reopen the zip file for importing
	zipReader, err = zip.OpenReader(tempZipFilePath)
	require.NoError(t, err, "could not open zip file")

	// Now try with a valid passphrase. This time it should succeed.
	err = cs2.ImportKeysZip(zipReader.Reader, newPassphraseRetriever)
	require.NoError(t, err)
	zipReader.Close()

	// Look for keys in private. The filenames should match the key IDs
	// in the repo's private key store.
	for privKeyName := range privKeyMap {
		_, alias, err := cs2.GetPrivateKey(privKeyName)
		require.NoError(t, err, "privKey %s has no alias", privKeyName)

		if alias == data.CanonicalRootRole {
			continue
		}
		relKeyPath := filepath.Join(notary.NonRootKeysSubdir, privKeyName+".key")
		privKeyFileName := filepath.Join(tempBaseDir2, notary.PrivDir, relKeyPath)
		_, err = os.Stat(privKeyFileName)
		require.NoError(t, err, "missing private key for role %s: %s", alias, privKeyName)
	}

	// Look for keys in root_keys
	// There should be a file named after the key ID of the root key we
	// passed in.
	rootKeyFilename := rootKeyID + ".key"
	_, err = os.Stat(filepath.Join(tempBaseDir2, notary.PrivDir, notary.RootKeysSubdir, rootKeyFilename))
	require.NoError(t, err, "missing root key")
}
Example #9
0
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)
	require.NoError(t, err, "failed to create a temporary directory: %s", err)

	fileStore, err := trustmanager.NewKeyFileStore(tempBaseDir, newPassphraseRetriever)
	cs := NewCryptoService(fileStore)
	_, err = cs.Create(data.CanonicalRootRole, gun, data.ECDSAKey)
	_, err = cs.Create(data.CanonicalTargetsRole, gun, data.ECDSAKey)
	_, err = cs.Create(data.CanonicalSnapshotRole, gun, data.ECDSAKey)
	require.NoError(t, err)

	tempZipFile, err := ioutil.TempFile("", "notary-test-export-")
	tempZipFilePath := tempZipFile.Name()
	defer os.Remove(tempZipFilePath)

	err = cs.ExportKeysByGUN(tempZipFile, gun, newPassphraseRetriever)
	require.NoError(t, err)

	// With an invalid GUN, this should return an error
	err = cs.ExportKeysByGUN(tempZipFile, "does.not.exist/in/repository", newPassphraseRetriever)
	require.EqualError(t, err, ErrNoKeysFoundForGUN.Error())

	tempZipFile.Close()

	// Reopen the zip file for importing
	zipReader, err := zip.OpenReader(tempZipFilePath)
	require.NoError(t, err, "could not open zip file")

	// Map of files to expect in the zip file, with the passphrases
	passphraseByFile := make(map[string]string)

	// Add keys non-root keys to the map. These should use the new passphrase
	// because they were formerly unencrypted.
	privKeyMap := cs.ListAllKeys()
	for privKeyName := range privKeyMap {
		_, alias, err := cs.GetPrivateKey(privKeyName)
		require.NoError(t, err, "privKey %s has no alias", privKeyName)
		if alias == data.CanonicalRootRole {
			continue
		}
		relKeyPath := filepath.Join(notary.NonRootKeysSubdir, gun, privKeyName+".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]
		require.True(t, present, "unexpected file %s in zip file", f.Name)

		delete(passphraseByFile, f.Name)

		rc, err := f.Open()
		require.NoError(t, err, "could not open file inside zip archive")

		pemBytes, err := ioutil.ReadAll(rc)
		require.NoError(t, err, "could not read file from zip")

		_, err = trustmanager.ParsePEMPrivateKey(pemBytes, expectedPassphrase)
		require.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?
	require.Len(t, passphraseByFile, 0)

	// Create new repo to test import
	tempBaseDir2, err := ioutil.TempDir("", "notary-test-")
	defer os.RemoveAll(tempBaseDir2)
	require.NoError(t, err, "failed to create a temporary directory: %s", err)

	fileStore2, err := trustmanager.NewKeyFileStore(tempBaseDir2, newPassphraseRetriever)
	cs2 := NewCryptoService(fileStore2)

	// Reopen the zip file for importing
	zipReader, err = zip.OpenReader(tempZipFilePath)
	require.NoError(t, err, "could not open zip file")

	// Now try with a valid passphrase. This time it should succeed.
	err = cs2.ImportKeysZip(zipReader.Reader, newPassphraseRetriever)
	require.NoError(t, err)
	zipReader.Close()

	// Look for keys in private. The filenames should match the key IDs
	// in the repo's private key store.
	for privKeyName, role := range privKeyMap {
		if role == data.CanonicalRootRole {
			continue
		}
		_, alias, err := cs2.GetPrivateKey(privKeyName)
		require.NoError(t, err, "privKey %s has no alias", privKeyName)
		if alias == data.CanonicalRootRole {
			continue
		}
		relKeyPath := filepath.Join(notary.NonRootKeysSubdir, gun, privKeyName+".key")
		privKeyFileName := filepath.Join(tempBaseDir2, notary.PrivDir, relKeyPath)
		_, err = os.Stat(privKeyFileName)
		require.NoError(t, err)
	}
}
Example #10
0
func TestImportExportRootKey(t *testing.T) {
	gun := "docker.com/notary"

	// Temporary directory where test files will be created
	tempBaseDir, err := ioutil.TempDir("", "notary-test-")
	defer os.RemoveAll(tempBaseDir)
	assert.NoError(t, err, "failed to create a temporary directory: %s", err)

	fileStore, err := trustmanager.NewKeyFileStore(tempBaseDir, oldPassphraseRetriever)
	cs := NewCryptoService(gun, fileStore)
	pubKey, err := cs.Create(data.CanonicalRootRole, data.ECDSAKey)
	assert.NoError(t, err)

	rootKeyID := pubKey.ID()

	tempKeyFile, err := ioutil.TempFile("", "notary-test-export-")
	tempKeyFilePath := tempKeyFile.Name()
	defer os.Remove(tempKeyFilePath)

	err = cs.ExportRootKey(tempKeyFile, rootKeyID)
	assert.NoError(t, err)
	tempKeyFile.Close()

	// Create new repo to test import
	tempBaseDir2, err := ioutil.TempDir("", "notary-test-")
	defer os.RemoveAll(tempBaseDir2)
	assert.NoError(t, err, "failed to create a temporary directory: %s", err)

	fileStore2, err := trustmanager.NewKeyFileStore(tempBaseDir2, oldPassphraseRetriever)
	cs2 := NewCryptoService(gun, fileStore2)

	keyReader, err := os.Open(tempKeyFilePath)
	assert.NoError(t, err, "could not open key file")

	err = cs2.ImportRootKey(keyReader)
	assert.NoError(t, err)
	keyReader.Close()

	// Look for repo's root key in repo2
	// There should be a file named after the key ID of the root key we
	// imported.
	rootKeyFilename := rootKeyID + ".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, "root")
	assert.NoError(t, err, "could not convert key to PEM")

	err = cs2.ImportRootKey(bytes.NewReader(decryptedPEMBytes))
	assert.EqualError(t, err, ErrRootKeyNotEncrypted.Error())

	// Try to import garbage and make sure it doesn't succeed
	err = cs2.ImportRootKey(strings.NewReader("this is not PEM"))
	assert.EqualError(t, err, ErrNoValidPrivateKey.Error())

	// Should be able to unlock the root key with the old password
	key, alias, err := cs2.GetPrivateKey(rootKeyID)
	assert.NoError(t, err, "could not unlock root key")
	assert.Equal(t, "root", alias)
	assert.Equal(t, rootKeyID, key.ID())
}
Example #11
0
// keysImport imports a private key from a PEM file for a role
func (k *keyCommander) keysImport(cmd *cobra.Command, args []string) error {
	if len(args) != 1 {
		cmd.Usage()
		return fmt.Errorf("Must specify input filename for import")
	}

	config, err := k.configGetter()
	if err != nil {
		return err
	}
	ks, err := k.getKeyStores(config, true, false)
	if err != nil {
		return err
	}

	importFilename := args[0]

	importFile, err := os.Open(importFilename)
	if err != nil {
		return fmt.Errorf("Opening file for import: %v", err)
	}
	defer importFile.Close()

	pemBytes, err := ioutil.ReadAll(importFile)
	if err != nil {
		return fmt.Errorf("Error reading input file: %v", err)
	}

	pemRole := trustmanager.ReadRoleFromPEM(pemBytes)

	// If the PEM key doesn't have a role in it, we must have --role set
	if pemRole == "" && k.keysImportRole == "" {
		return fmt.Errorf("Could not infer role, and no role was specified for key")
	}

	// If both  PEM role and a --role are provided and they don't match, error
	if pemRole != "" && k.keysImportRole != "" && pemRole != k.keysImportRole {
		return fmt.Errorf("Specified role %s does not match role %s in PEM headers", k.keysImportRole, pemRole)
	}

	// Determine which role to add to between PEM headers and --role flag:
	var importRole string
	if k.keysImportRole != "" {
		importRole = k.keysImportRole
	} else {
		importRole = pemRole
	}

	// If we're importing to targets or snapshot, we need a GUN
	if (importRole == data.CanonicalTargetsRole || importRole == data.CanonicalSnapshotRole) && k.keysImportGUN == "" {
		return fmt.Errorf("Must specify GUN for %s key", importRole)
	}

	// Root keys must be encrypted
	if importRole == data.CanonicalRootRole {
		if err = cryptoservice.CheckRootKeyIsEncrypted(pemBytes); err != nil {
			return err
		}
	}

	cs := cryptoservice.NewCryptoService(ks...)
	// Convert to a data.PrivateKey, potentially decrypting the key
	privKey, err := trustmanager.ParsePEMPrivateKey(pemBytes, "")
	if err != nil {
		privKey, _, err = trustmanager.GetPasswdDecryptBytes(k.getRetriever(), pemBytes, "", "imported "+importRole)
		if err != nil {
			return err
		}
	}
	err = cs.AddKey(importRole, k.keysImportGUN, privKey)
	if err != nil {
		return fmt.Errorf("Error importing key: %v", err)
	}
	return nil
}
Example #12
0
func TestValidateRootWithPinnedCA(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)
	require.NoError(t, err, "failed to create a temporary directory: %s", err)

	templ, _ := template.New("SignedRSARootTemplate").Parse(signedRSARootTemplate)
	templ.Execute(&signedRootBytes, SignedRSARootTemplate{RootPem: validPEMEncodedRSARoot})
	// Unmarshal our signedRoot
	json.Unmarshal(signedRootBytes.Bytes(), &testSignedRoot)
	typedSignedRoot, err := data.RootFromSigned(&testSignedRoot)
	require.NoError(t, err)

	// This call to trustpinning.ValidateRoot will fail because we have an invalid path for the CA
	_, err = trustpinning.ValidateRoot(nil, &testSignedRoot, "docker.com/notary", trustpinning.TrustPinConfig{CA: map[string]string{"docker.com/notary": filepath.Join(tempBaseDir, "nonexistent")}})
	require.Error(t, err)

	// This call to trustpinning.ValidateRoot will fail because we have no valid GUNs to use, and TOFUS is disabled
	_, err = trustpinning.ValidateRoot(nil, &testSignedRoot, "docker.com/notary", trustpinning.TrustPinConfig{CA: map[string]string{"othergun": filepath.Join(tempBaseDir, "nonexistent")}, DisableTOFU: true})
	require.Error(t, err)

	// This call to trustpinning.ValidateRoot will succeed because we have no valid GUNs to use and we fall back to enabled TOFUS
	validatedRoot, err := trustpinning.ValidateRoot(nil, &testSignedRoot, "docker.com/notary", trustpinning.TrustPinConfig{CA: map[string]string{"othergun": filepath.Join(tempBaseDir, "nonexistent")}, DisableTOFU: false})
	require.NoError(t, err)
	generateRootKeyIDs(typedSignedRoot)
	require.Equal(t, typedSignedRoot, validatedRoot)

	// Write an invalid CA cert (not even a PEM) to the tempDir and ensure validation fails when using it
	invalidCAFilepath := filepath.Join(tempBaseDir, "invalid.ca")
	require.NoError(t, ioutil.WriteFile(invalidCAFilepath, []byte("ABSOLUTELY NOT A PEM"), 0644))

	// Using this invalid CA cert should fail on trustpinning.ValidateRoot
	_, err = trustpinning.ValidateRoot(nil, &testSignedRoot, "docker.com/notary", trustpinning.TrustPinConfig{CA: map[string]string{"docker.com/notary": invalidCAFilepath}, DisableTOFU: true})
	require.Error(t, err)

	validCAFilepath := "../fixtures/root-ca.crt"

	// If we pass an invalid Certs entry in addition to this valid CA entry, since Certs has priority for pinning we will fail
	_, err = trustpinning.ValidateRoot(nil, &testSignedRoot, "docker.com/notary", trustpinning.TrustPinConfig{Certs: map[string][]string{"docker.com/notary": {"invalidID"}}, CA: map[string]string{"docker.com/notary": validCAFilepath}, DisableTOFU: true})
	require.Error(t, err)

	// Now construct a new root with a valid cert chain, such that signatures are correct over the 'notary-signer' GUN.  Pin the root-ca and validate
	leafCert, err := trustmanager.LoadCertFromFile("../fixtures/notary-signer.crt")
	require.NoError(t, err)

	intermediateCert, err := trustmanager.LoadCertFromFile("../fixtures/intermediate-ca.crt")
	require.NoError(t, err)

	pemChainBytes, err := trustmanager.CertChainToPEM([]*x509.Certificate{leafCert, intermediateCert})
	require.NoError(t, err)

	newRootKey := data.NewPublicKey(data.RSAx509Key, pemChainBytes)

	rootRole, err := data.NewRole(data.CanonicalRootRole, 1, []string{newRootKey.ID()}, nil)
	require.NoError(t, err)

	testRoot, err := data.NewRoot(
		map[string]data.PublicKey{newRootKey.ID(): newRootKey},
		map[string]*data.RootRole{
			data.CanonicalRootRole:      &rootRole.RootRole,
			data.CanonicalTimestampRole: &rootRole.RootRole,
			data.CanonicalTargetsRole:   &rootRole.RootRole,
			data.CanonicalSnapshotRole:  &rootRole.RootRole},
		false,
	)
	testRoot.Signed.Version = 1
	require.NoError(t, err, "Failed to create new root")

	keyReader, err := os.Open("../fixtures/notary-signer.key")
	require.NoError(t, err, "could not open key file")
	pemBytes, err := ioutil.ReadAll(keyReader)
	require.NoError(t, err, "could not read key file")
	privKey, err := trustmanager.ParsePEMPrivateKey(pemBytes, "")
	require.NoError(t, err)

	store, err := trustmanager.NewKeyFileStore(tempBaseDir, passphraseRetriever)
	require.NoError(t, err)
	cs := cryptoservice.NewCryptoService(store)

	err = store.AddKey(trustmanager.KeyInfo{Role: data.CanonicalRootRole, Gun: "notary-signer"}, privKey)
	require.NoError(t, err)

	newTestSignedRoot, err := testRoot.ToSigned()
	require.NoError(t, err)

	err = signed.Sign(cs, newTestSignedRoot, []data.PublicKey{newRootKey}, 1, nil)
	require.NoError(t, err)

	newTypedSignedRoot, err := data.RootFromSigned(newTestSignedRoot)
	require.NoError(t, err)

	// Check that we validate correctly against a pinned CA and provided bundle
	validatedRoot, err = trustpinning.ValidateRoot(nil, newTestSignedRoot, "notary-signer", trustpinning.TrustPinConfig{CA: map[string]string{"notary-signer": validCAFilepath}, DisableTOFU: true})
	require.NoError(t, err)
	generateRootKeyIDs(newTypedSignedRoot)
	require.Equal(t, newTypedSignedRoot, validatedRoot)

	// Add an expired CA for the same gun to our previous pinned bundle, ensure that we still validate correctly
	goodRootCABundle, err := trustmanager.LoadCertBundleFromFile(validCAFilepath)
	require.NoError(t, err)
	memKeyStore := trustmanager.NewKeyMemoryStore(passphraseRetriever)
	cryptoService := cryptoservice.NewCryptoService(memKeyStore)
	testPubKey, err := cryptoService.Create("root", "notary-signer", data.ECDSAKey)
	require.NoError(t, err)
	testPrivKey, _, err := memKeyStore.GetKey(testPubKey.ID())
	require.NoError(t, err)
	expiredCert, err := generateExpiredTestingCertificate(testPrivKey, "notary-signer")
	require.NoError(t, err)
	bundleWithExpiredCert, err := trustmanager.CertChainToPEM(append(goodRootCABundle, expiredCert))
	require.NoError(t, err)
	bundleWithExpiredCertPath := filepath.Join(tempBaseDir, "bundle_with_expired_cert.pem")
	require.NoError(t, ioutil.WriteFile(bundleWithExpiredCertPath, bundleWithExpiredCert, 0644))

	// Check that we validate correctly against a pinned CA and provided bundle
	validatedRoot, err = trustpinning.ValidateRoot(nil, newTestSignedRoot, "notary-signer", trustpinning.TrustPinConfig{CA: map[string]string{"notary-signer": bundleWithExpiredCertPath}, DisableTOFU: true})
	require.NoError(t, err)
	require.Equal(t, newTypedSignedRoot, validatedRoot)

	testPubKey2, err := cryptoService.Create("root", "notary-signer", data.ECDSAKey)
	require.NoError(t, err)
	testPrivKey2, _, err := memKeyStore.GetKey(testPubKey2.ID())
	require.NoError(t, err)
	expiredCert2, err := generateExpiredTestingCertificate(testPrivKey2, "notary-signer")
	require.NoError(t, err)
	allExpiredCertBundle, err := trustmanager.CertChainToPEM([]*x509.Certificate{expiredCert, expiredCert2})
	require.NoError(t, err)
	allExpiredCertPath := filepath.Join(tempBaseDir, "all_expired_cert.pem")
	require.NoError(t, ioutil.WriteFile(allExpiredCertPath, allExpiredCertBundle, 0644))
	// Now only use expired certs in the bundle, we should fail
	_, err = trustpinning.ValidateRoot(nil, newTestSignedRoot, "notary-signer", trustpinning.TrustPinConfig{CA: map[string]string{"notary-signer": allExpiredCertPath}, DisableTOFU: true})
	require.Error(t, err)

	// Add a CA cert for a that won't validate against the root leaf certificate
	testPubKey3, err := cryptoService.Create("root", "notary-signer", data.ECDSAKey)
	require.NoError(t, err)
	testPrivKey3, _, err := memKeyStore.GetKey(testPubKey3.ID())
	require.NoError(t, err)
	validCert, err := cryptoservice.GenerateCertificate(testPrivKey3, "notary-signer", time.Now(), time.Now().AddDate(1, 0, 0))
	require.NoError(t, err)
	bundleWithWrongCert, err := trustmanager.CertChainToPEM([]*x509.Certificate{validCert})
	require.NoError(t, err)
	bundleWithWrongCertPath := filepath.Join(tempBaseDir, "bundle_with_expired_cert.pem")
	require.NoError(t, ioutil.WriteFile(bundleWithWrongCertPath, bundleWithWrongCert, 0644))
	_, err = trustpinning.ValidateRoot(nil, newTestSignedRoot, "notary-signer", trustpinning.TrustPinConfig{CA: map[string]string{"notary-signer": bundleWithWrongCertPath}, DisableTOFU: true})
	require.Error(t, err)
}
func TestImportExportZip(t *testing.T) {
	gun := "docker.com/notary"
	oldPassphrase := "oldPassphrase"
	exportPassphrase := "exportPassphrase"

	// 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)
	assert.NoError(t, err, "error creating repo: %s", err)

	rootKeyID, err := repo.KeyStoreManager.GenRootKey(data.ECDSAKey.String(), oldPassphrase)
	assert.NoError(t, err, "error generating root key: %s", err)

	rootCryptoService, err := repo.KeyStoreManager.GetRootCryptoService(rootKeyID, oldPassphrase)
	assert.NoError(t, err, "error retrieving root key: %s", err)

	err = repo.Initialize(rootCryptoService)
	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, exportPassphrase)
	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 they were formerly unencrypted.
	privKeyList := repo.KeyStoreManager.NonRootKeyStore().ListFiles(false)
	for _, privKeyName := range privKeyList {
		relName := strings.TrimPrefix(privKeyName, tempBaseDir+string(filepath.Separator))
		passphraseByFile[relName] = exportPassphrase
	}

	// Add root key to the map. This will use the old passphrase because it
	// won't be reencrypted.
	relRootKey := filepath.Join("private", "root_keys", rootCryptoService.ID()+".key")
	passphraseByFile[relRootKey] = oldPassphrase

	// 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)
	assert.NoError(t, err, "error creating repo: %s", err)

	rootKeyID2, err := repo2.KeyStoreManager.GenRootKey(data.ECDSAKey.String(), "oldPassphrase")
	assert.NoError(t, err, "error generating root key: %s", err)

	rootCryptoService2, err := repo2.KeyStoreManager.GetRootCryptoService(rootKeyID2, "oldPassphrase")
	assert.NoError(t, err, "error retrieving root key: %s", err)

	err = repo2.Initialize(rootCryptoService2)
	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")

	// First try with an incorrect passphrase
	err = repo2.KeyStoreManager.ImportKeysZip(zipReader.Reader, "wrongpassphrase")
	// Don't use EqualError here because occasionally decrypting with the
	// wrong passphrase returns a parse error
	assert.Error(t, err)
	zipReader.Close()

	// 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, exportPassphrase)
	assert.NoError(t, err)
	zipReader.Close()

	// Look for repo's keys in repo2

	// Look for keys in private. The filenames should match the key IDs
	// in the repo's private key store.
	for _, privKeyName := range privKeyList {
		privKeyRel := strings.TrimPrefix(privKeyName, tempBaseDir)
		_, err := os.Stat(filepath.Join(tempBaseDir2, privKeyRel))
		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 := rootCryptoService.ID() + ".key"
	_, err = os.Stat(filepath.Join(tempBaseDir2, "private", "root_keys", rootKeyFilename))
	assert.NoError(t, err, "missing root key")
}
Example #14
0
// ImportKeysZip imports keys from a zip file provided as an io.ReaderAt. 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, passphrase string) 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
	newRootKeys := make(map[string][]byte)
	newNonRootKeys := make(map[string]*data.PrivateKey)

	// Note that using / as a separator is okay here - the zip package
	// guarantees that the separator will be /
	rootKeysPrefix := privDir + "/" + rootKeysSubdir + "/"
	nonRootKeysPrefix := privDir + "/" + nonRootKeysSubdir + "/"

	// 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
		}

		pemBytes, err := ioutil.ReadAll(rc)
		if err != nil {
			return nil
		}

		// Is this in the root_keys directory?
		// Note that using / as a separator is okay here - the zip
		// package guarantees that the separator will be /
		if strings.HasPrefix(fNameTrimmed, rootKeysPrefix) {
			if err = checkRootKeyIsEncrypted(pemBytes); err != nil {
				rc.Close()
				return err
			}
			// Root keys are preserved without decrypting
			keyName := strings.TrimPrefix(fNameTrimmed, rootKeysPrefix)
			newRootKeys[keyName] = pemBytes
		} else if strings.HasPrefix(fNameTrimmed, nonRootKeysPrefix) {
			// Non-root keys need to be decrypted
			key, err := trustmanager.ParsePEMPrivateKey(pemBytes, passphrase)
			if err != nil {
				rc.Close()
				return err
			}
			keyName := strings.TrimPrefix(fNameTrimmed, nonRootKeysPrefix)
			newNonRootKeys[keyName] = key
		} else {
			// This path inside the zip archive doesn't look like a
			// root key or a non-root key. To avoid adding a file
			// to the filestore that we won't be able to use, skip
			// this file in the import.
			logrus.Warnf("skipping import of key with a path that doesn't begin with %s or %s: %s", rootKeysPrefix, nonRootKeysPrefix, f.Name)
			rc.Close()
			continue
		}

		rc.Close()
	}

	for keyName, pemBytes := range newRootKeys {
		if err := km.rootKeyStore.Add(keyName, pemBytes); err != nil {
			return err
		}
	}

	for keyName, privKey := range newNonRootKeys {
		if err := km.nonRootKeyStore.AddKey(keyName, privKey); err != nil {
			return err
		}
	}

	return nil
}