Add import/export to KeyStore interface so that the import_export code

makes use of this rather than mangle files manually to import/export
root keys.  (Regular keys it just zips up the whole directory.)

Signed-off-by: Ying Li <ying.li@docker.com>
This commit is contained in:
Ying Li 2015-10-27 16:19:14 -07:00
parent 566bd3ce67
commit 75b63b84cd
6 changed files with 333 additions and 126 deletions

View File

@ -11,7 +11,6 @@ import (
"path/filepath"
"strings"
"github.com/Sirupsen/logrus"
"github.com/docker/notary/pkg/passphrase"
"github.com/docker/notary/trustmanager"
)
@ -35,19 +34,24 @@ 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.rootKeyStore.Get(keyID + "_root")
pemBytes, err := km.KeyStore.ExportKey(keyID)
if err != nil {
return err
}
_, err = dest.Write(pemBytes)
return err
nBytes, err := dest.Write(pemBytes)
if err != nil {
return err
}
if nBytes != len(pemBytes) {
return errors.New("Unable to finish writing exported key.")
}
return nil
}
// 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.rootKeyStore.GetKey(keyID)
privateKey, alias, err := km.KeyStore.GetKey(keyID)
if err != nil {
return err
}
@ -56,25 +60,29 @@ func (km *KeyStoreManager) ExportRootKeyReencrypt(dest io.Writer, keyID string,
tempBaseDir, err := ioutil.TempDir("", "notary-key-export-")
defer os.RemoveAll(tempBaseDir)
privRootKeysSubdir := filepath.Join(privDir, rootKeysSubdir)
tempRootKeysPath := filepath.Join(tempBaseDir, privRootKeysSubdir)
tempRootKeyStore, err := trustmanager.NewKeyFileStore(tempRootKeysPath, newPassphraseRetriever)
tempKeysPath := filepath.Join(tempBaseDir, privDir)
tempKeyStore, err := trustmanager.NewKeyFileStore(tempKeysPath, newPassphraseRetriever)
if err != nil {
return err
}
err = tempRootKeyStore.AddKey(keyID, alias, privateKey)
err = tempKeyStore.AddKey(keyID, alias, privateKey)
if err != nil {
return err
}
pemBytes, err := tempRootKeyStore.Get(keyID + "_" + alias)
pemBytes, err := tempKeyStore.ExportKey(keyID)
if err != nil {
return err
}
_, err = dest.Write(pemBytes)
return err
nBytes, err := dest.Write(pemBytes)
if err != nil {
return err
}
if nBytes != len(pemBytes) {
return errors.New("Unable to finish writing exported key.")
}
return nil
}
// checkRootKeyIsEncrypted makes sure the root key is encrypted. We have
@ -106,7 +114,7 @@ func (km *KeyStoreManager) ImportRootKey(source io.Reader, keyID string) error {
return err
}
if err = km.rootKeyStore.Add(keyID+"_root", pemBytes); err != nil {
if err = km.KeyStore.ImportKey(pemBytes, "root"); err != nil {
return err
}
@ -170,35 +178,20 @@ func (km *KeyStoreManager) ExportAllKeys(dest io.Writer, newPassphraseRetriever
tempBaseDir, err := ioutil.TempDir("", "notary-key-export-")
defer os.RemoveAll(tempBaseDir)
privNonRootKeysSubdir := filepath.Join(privDir, nonRootKeysSubdir)
privRootKeysSubdir := filepath.Join(privDir, rootKeysSubdir)
// Create temporary keystores to use as a staging area
tempNonRootKeysPath := filepath.Join(tempBaseDir, privNonRootKeysSubdir)
tempNonRootKeyStore, err := trustmanager.NewKeyFileStore(tempNonRootKeysPath, newPassphraseRetriever)
// 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
}
tempRootKeysPath := filepath.Join(tempBaseDir, privRootKeysSubdir)
tempRootKeyStore, err := trustmanager.NewKeyFileStore(tempRootKeysPath, newPassphraseRetriever)
if err != nil {
return err
}
if err := moveKeys(km.rootKeyStore, tempRootKeyStore); err != nil {
return err
}
if err := moveKeys(km.nonRootKeyStore, tempNonRootKeyStore); err != nil {
if err := moveKeys(km.KeyStore, tempKeyStore); err != nil {
return err
}
zipWriter := zip.NewWriter(dest)
if err := addKeysToArchive(zipWriter, tempRootKeyStore, privRootKeysSubdir); err != nil {
return err
}
if err := addKeysToArchive(zipWriter, tempNonRootKeyStore, privNonRootKeysSubdir); err != nil {
if err := addKeysToArchive(zipWriter, tempKeyStore, privDir); err != nil {
return err
}
@ -214,13 +207,7 @@ 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
newRootKeys := make(map[string][]byte)
newNonRootKeys := make(map[string][]byte)
// Note that using / as a separator is okay here - the zip package
// guarantees that the separator will be /
rootKeysPrefix := privDir + "/" + rootKeysSubdir + "/"
nonRootKeysPrefix := privDir + "/" + nonRootKeysSubdir + "/"
newKeys := make(map[string][]byte)
// Iterate through the files in the archive. Don't add the keys
for _, f := range zipReader.File {
@ -236,29 +223,22 @@ func (km *KeyStoreManager) ImportKeysZip(zipReader zip.Reader) error {
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(fileBytes); err != nil {
rc.Close()
return err
if strings.HasPrefix(fNameTrimmed, privDir) {
if fNameTrimmed[len(fNameTrimmed)-5:] == "_root" {
if err = checkRootKeyIsEncrypted(fileBytes); err != nil {
rc.Close()
return err
}
}
// Root keys are preserved without decrypting
keyName := strings.TrimPrefix(fNameTrimmed, rootKeysPrefix)
newRootKeys[keyName] = fileBytes
} else if strings.HasPrefix(fNameTrimmed, nonRootKeysPrefix) {
// Nonroot keys are preserved without decrypting
keyName := strings.TrimPrefix(fNameTrimmed, nonRootKeysPrefix)
newNonRootKeys[keyName] = fileBytes
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.
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
}
@ -266,14 +246,8 @@ func (km *KeyStoreManager) ImportKeysZip(zipReader zip.Reader) error {
rc.Close()
}
for keyName, pemBytes := range newRootKeys {
if err := km.rootKeyStore.Add(keyName, pemBytes); err != nil {
return err
}
}
for keyName, pemBytes := range newNonRootKeys {
if err := km.nonRootKeyStore.Add(keyName, pemBytes); err != nil {
for keyName, pemBytes := range newKeys {
if err := km.KeyStore.Add(keyName, pemBytes); err != nil {
return err
}
}
@ -309,26 +283,24 @@ func (km *KeyStoreManager) ExportKeysByGUN(dest io.Writer, gun string, passphras
tempBaseDir, err := ioutil.TempDir("", "notary-key-export-")
defer os.RemoveAll(tempBaseDir)
privNonRootKeysSubdir := filepath.Join(privDir, nonRootKeysSubdir)
// Create temporary keystore to use as a staging area
tempNonRootKeysPath := filepath.Join(tempBaseDir, privNonRootKeysSubdir)
tempNonRootKeyStore, err := trustmanager.NewKeyFileStore(tempNonRootKeysPath, passphraseRetriever)
tempKeysPath := filepath.Join(tempBaseDir, privDir)
tempKeyStore, err := trustmanager.NewKeyFileStore(tempKeysPath, passphraseRetriever)
if err != nil {
return err
}
if err := moveKeysByGUN(km.nonRootKeyStore, tempNonRootKeyStore, gun); err != nil {
if err := moveKeysByGUN(km.KeyStore, tempKeyStore, gun); err != nil {
return err
}
zipWriter := zip.NewWriter(dest)
if len(tempNonRootKeyStore.ListKeys()) == 0 {
if len(tempKeyStore.ListKeys()) == 0 {
return ErrNoKeysFoundForGUN
}
if err := addKeysToArchive(zipWriter, tempNonRootKeyStore, privNonRootKeysSubdir); err != nil {
if err := addKeysToArchive(zipWriter, tempKeyStore, privDir); err != nil {
return err
}

View File

@ -83,11 +83,14 @@ func TestImportExportZip(t *testing.T) {
// 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()
privKeyMap := repo.KeyStoreManager.KeyStore.ListKeys()
for privKeyName := range privKeyMap {
_, alias, err := repo.KeyStoreManager.NonRootKeyStore().GetKey(privKeyName)
_, 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
}
@ -156,9 +159,12 @@ func TestImportExportZip(t *testing.T) {
// 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)
_, 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)
@ -219,12 +225,15 @@ func TestImportExportGUN(t *testing.T) {
// Add keys non-root keys to the map. These should use the new passphrase
// because they were formerly unencrypted.
privKeyMap := repo.KeyStoreManager.NonRootKeyStore().ListKeys()
privKeyMap := repo.KeyStoreManager.KeyStore.ListKeys()
for privKeyName := range privKeyMap {
_, alias, err := repo.KeyStoreManager.NonRootKeyStore().GetKey(privKeyName)
_, 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
@ -290,10 +299,13 @@ func TestImportExportGUN(t *testing.T) {
// 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)
_, 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)
@ -381,7 +393,7 @@ func TestImportExportRootKey(t *testing.T) {
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)
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())
@ -452,7 +464,7 @@ func TestImportExportRootKeyReencrypt(t *testing.T) {
assert.NoError(t, err, "missing root key")
// Should be able to unlock the root key with the new password
key, alias, err := repo2.KeyStoreManager.RootKeyStore().GetKey(rootKeyID)
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())

View File

@ -2,6 +2,7 @@ package signer
import (
"database/sql"
"errors"
"fmt"
"sync"
@ -196,6 +197,16 @@ func (s *KeyDBStore) RotateKeyPassphrase(name, newPassphraseAlias string) error
return nil
}
// ExportKey is currently unimplemented and will always return an error
func (s *KeyDBStore) ExportKey(name string) ([]byte, error) {
return nil, errors.New("Exporting from a KeyDBStore is not supported.")
}
// ImportKey is currently unimplemented and will always return an error
func (s *KeyDBStore) ImportKey(pemBytes []byte, alias string) error {
return errors.New("Importing into a KeyDBStore is not supported")
}
// HealthCheck verifies that DB exists and is query-able
func (s *KeyDBStore) HealthCheck() error {
dbPrivateKey := GormPrivateKey{}

View File

@ -70,6 +70,22 @@ func (s *KeyFileStore) RemoveKey(name string) error {
return removeKey(s, s.cachedKeys, name)
}
// ExportKey exportes the encrypted bytes from the keystore and writes it to
// dest.
func (s *KeyFileStore) ExportKey(name string) ([]byte, error) {
keyBytes, _, err := getRawKey(s, name)
if err != nil {
return nil, err
}
return keyBytes, nil
}
// ImportKey imports the private key in the encrypted bytes into the keystore
// with the given key ID and alias.
func (s *KeyFileStore) ImportKey(pemBytes []byte, alias string) error {
return importKey(s, s.Retriever, s.cachedKeys, alias, pemBytes)
}
// NewKeyMemoryStore returns a new KeyMemoryStore which holds keys in memory
func NewKeyMemoryStore(passphraseRetriever passphrase.Retriever) *KeyMemoryStore {
memStore := NewMemoryFileStore()
@ -106,19 +122,33 @@ func (s *KeyMemoryStore) RemoveKey(name string) error {
return removeKey(s, s.cachedKeys, name)
}
func addKey(s LimitedFileStore, passphraseRetriever passphrase.Retriever, cachedKeys map[string]*cachedKey, name, alias string, privKey data.PrivateKey) error {
pemPrivKey, err := KeyToPEM(privKey)
// ExportKey exportes the encrypted bytes from the keystore and writes it to
// dest.
func (s *KeyMemoryStore) ExportKey(name string) ([]byte, error) {
keyBytes, _, err := getRawKey(s, name)
if err != nil {
return err
return nil, err
}
return keyBytes, nil
}
attempts := 0
chosenPassphrase := ""
giveup := false
for {
// ImportKey imports the private key in the encrypted bytes into the keystore
// with the given key ID and alias.
func (s *KeyMemoryStore) ImportKey(pemBytes []byte, alias string) error {
return importKey(s, s.Retriever, s.cachedKeys, alias, pemBytes)
}
func addKey(s LimitedFileStore, passphraseRetriever passphrase.Retriever, cachedKeys map[string]*cachedKey, name, alias string, privKey data.PrivateKey) error {
var (
chosenPassphrase string
giveup bool
err error
)
for attempts := 0; ; attempts++ {
chosenPassphrase, giveup, err = passphraseRetriever(name, alias, true, attempts)
if err != nil {
attempts++
continue
}
if giveup {
@ -130,15 +160,7 @@ func addKey(s LimitedFileStore, passphraseRetriever passphrase.Retriever, cached
break
}
if chosenPassphrase != "" {
pemPrivKey, err = EncryptPrivateKey(privKey, chosenPassphrase)
if err != nil {
return err
}
}
cachedKeys[name] = &cachedKey{alias: alias, key: privKey}
return s.Add(filepath.Join(getSubdir(alias), name+"_"+alias), pemPrivKey)
return encryptAndAddKey(s, chosenPassphrase, cachedKeys, name, alias, privKey)
}
func getKeyAlias(s LimitedFileStore, keyID string) (string, error) {
@ -165,14 +187,8 @@ func getKey(s LimitedFileStore, passphraseRetriever passphrase.Retriever, cached
if ok {
return cachedKeyEntry.key, cachedKeyEntry.alias, nil
}
keyAlias, err := getKeyAlias(s, name)
if err != nil {
return nil, "", err
}
filename := name + "_" + keyAlias
var keyBytes []byte
keyBytes, err = s.Get(filepath.Join(getSubdir(keyAlias), filename))
keyBytes, keyAlias, err := getRawKey(s, name)
if err != nil {
return nil, "", err
}
@ -181,27 +197,7 @@ func getKey(s LimitedFileStore, passphraseRetriever passphrase.Retriever, cached
// See if the key is encrypted. If its encrypted we'll fail to parse the private key
privKey, err := ParsePEMPrivateKey(keyBytes, "")
if err != nil {
// We need to decrypt the key, lets get a passphrase
for attempts := 0; ; attempts++ {
passphrase, giveup, err := passphraseRetriever(name, string(keyAlias), false, attempts)
// Check if the passphrase retriever got an error or if it is telling us to give up
if giveup || err != nil {
return nil, "", ErrPasswordInvalid{}
}
if attempts > 10 {
return nil, "", ErrAttemptsExceeded{}
}
// Try to convert PEM encoded bytes back to a PrivateKey using the passphrase
privKey, err = ParsePEMPrivateKey(keyBytes, passphrase)
if err != nil {
retErr = ErrPasswordInvalid{}
} else {
// We managed to parse the PrivateKey. We've succeeded!
retErr = nil
break
}
}
privKey, _, retErr = getPasswdDecryptBytes(s, passphraseRetriever, keyBytes, name, string(keyAlias))
}
if retErr != nil {
return nil, "", retErr
@ -247,9 +243,98 @@ func removeKey(s LimitedFileStore, cachedKeys map[string]*cachedKey, name string
return nil
}
// Assumes 2 subdirectories, 1 containing root keys and 1 containing tuf keys
func getSubdir(alias string) string {
if alias == "root" {
return rootKeysSubdir
}
return nonRootKeysSubdir
}
// Given a key ID, gets the bytes and alias belonging to that key if the key
// exists
func getRawKey(s LimitedFileStore, name string) ([]byte, string, error) {
keyAlias, err := getKeyAlias(s, name)
if err != nil {
return nil, "", err
}
filename := name + "_" + keyAlias
var keyBytes []byte
keyBytes, err = s.Get(filepath.Join(getSubdir(keyAlias), filename))
if err != nil {
return nil, "", err
}
return keyBytes, keyAlias, nil
}
// Get the password to decript the given pem bytes. Return the password,
// because it is useful for importing
func getPasswdDecryptBytes(s LimitedFileStore, passphraseRetriever passphrase.Retriever, pemBytes []byte, name, alias string) (data.PrivateKey, string, error) {
var (
passwd string
retErr error
privKey data.PrivateKey
)
for attempts := 0; ; attempts++ {
var (
giveup bool
err error
)
passwd, giveup, err = passphraseRetriever(name, alias, false, attempts)
// Check if the passphrase retriever got an error or if it is telling us to give up
if giveup || err != nil {
return nil, "", ErrPasswordInvalid{}
}
if attempts > 10 {
return nil, "", ErrAttemptsExceeded{}
}
// Try to convert PEM encoded bytes back to a PrivateKey using the passphrase
privKey, err = ParsePEMPrivateKey(pemBytes, passwd)
if err != nil {
retErr = ErrPasswordInvalid{}
} else {
// We managed to parse the PrivateKey. We've succeeded!
retErr = nil
break
}
}
if retErr != nil {
return nil, "", retErr
}
return privKey, passwd, nil
}
func encryptAndAddKey(s LimitedFileStore, passwd string, cachedKeys map[string]*cachedKey, name, alias string, privKey data.PrivateKey) error {
var (
pemPrivKey []byte
err error
)
if passwd != "" {
pemPrivKey, err = EncryptPrivateKey(privKey, passwd)
} else {
pemPrivKey, err = KeyToPEM(privKey)
}
if err != nil {
return err
}
cachedKeys[name] = &cachedKey{alias: alias, key: privKey}
return s.Add(filepath.Join(getSubdir(alias), name+"_"+alias), pemPrivKey)
}
func importKey(s LimitedFileStore, passphraseRetriever passphrase.Retriever, cachedKeys map[string]*cachedKey, alias string, pemBytes []byte) error {
privKey, passphrase, err := getPasswdDecryptBytes(s, passphraseRetriever, pemBytes, "imported", alias)
if err != nil {
return err
}
return encryptAndAddKey(
s, passphrase, cachedKeys, privKey.ID(), alias, privKey)
}

View File

@ -9,15 +9,18 @@ import (
"testing"
"github.com/docker/notary/pkg/passphrase"
"github.com/endophage/gotuf/data"
"github.com/stretchr/testify/assert"
)
const cannedPassphrase = "passphrase"
var passphraseRetriever = func(keyID string, alias string, createNew bool, numAttempts int) (string, bool, error) {
if numAttempts > 5 {
giveup := true
return "", giveup, errors.New("passPhraseRetriever failed after too many requests")
}
return "passphrase", false, nil
return cannedPassphrase, false, nil
}
func TestAddKey(t *testing.T) {
@ -345,3 +348,125 @@ func TestKeysAreCached(t *testing.T) {
}
assert.Equal(t, 2, numTimesCalled, "numTimesCalled should be 2 -- no additional call to passphraseRetriever")
}
// Exporting a key is successful (it is a valid key)
func TestKeyFileStoreExportSuccess(t *testing.T) {
// Generate a new Private Key
privKey, err := GenerateECDSAKey(rand.Reader)
assert.NoError(t, err)
// Temporary directory where test files will be created
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
assert.NoError(t, err)
defer os.RemoveAll(tempBaseDir)
// Create our FileStore and add the key
store, err := NewKeyFileStore(tempBaseDir, passphraseRetriever)
assert.NoError(t, err)
err = store.AddKey(privKey.ID(), "root", privKey)
assert.NoError(t, err)
assertExportKeySuccess(t, store, privKey)
}
// Exporting a key that doesn't exist fails (it is a valid key)
func TestKeyFileStoreExportNonExistantFailure(t *testing.T) {
// Temporary directory where test files will be created
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
assert.NoError(t, err)
defer os.RemoveAll(tempBaseDir)
// Create empty FileStore
store, err := NewKeyFileStore(tempBaseDir, passphraseRetriever)
assert.NoError(t, err)
_, err = store.ExportKey("12345")
assert.Error(t, err)
}
// Exporting a key is successful (it is a valid key)
func TestKeyMemoryStoreExportSuccess(t *testing.T) {
// Generate a new Private Key
privKey, err := GenerateECDSAKey(rand.Reader)
assert.NoError(t, err)
// Create our MemoryStore and add key to it
store := NewKeyMemoryStore(passphraseRetriever)
assert.NoError(t, err)
err = store.AddKey(privKey.ID(), "root", privKey)
assert.NoError(t, err)
assertExportKeySuccess(t, store, privKey)
}
// Exporting a key that doesn't exist fails (it is a valid key)
func TestKeyMemoryStoreExportNonExistantFailure(t *testing.T) {
store := NewKeyMemoryStore(passphraseRetriever)
_, err := store.ExportKey("12345")
assert.Error(t, err)
}
// Importing a key is successful
func TestKeyFileStoreImportSuccess(t *testing.T) {
// Generate a new Private Key
privKey, err := GenerateECDSAKey(rand.Reader)
assert.NoError(t, err)
// Temporary directory where test files will be created
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
assert.NoError(t, err)
defer os.RemoveAll(tempBaseDir)
// Create our FileStore
store, err := NewKeyFileStore(tempBaseDir, passphraseRetriever)
assert.NoError(t, err)
assertImportKeySuccess(t, store, privKey)
}
// Importing a key is successful
func TestKeyMemoryStoreImportSuccess(t *testing.T) {
// Generate a new Private Key
privKey, err := GenerateECDSAKey(rand.Reader)
assert.NoError(t, err)
// Create our MemoryStore
store := NewKeyMemoryStore(passphraseRetriever)
assert.NoError(t, err)
assertImportKeySuccess(t, store, privKey)
}
// Given a keystore and expected key that is in the store, export the key
// and assert that the exported key is the same and encrypted with the right
// password.
func assertExportKeySuccess(
t *testing.T, s KeyStore, expectedKey data.PrivateKey) {
pemBytes, err := s.ExportKey(expectedKey.ID())
assert.NoError(t, err)
reparsedKey, err := ParsePEMPrivateKey(pemBytes, cannedPassphrase)
assert.NoError(t, err)
assert.Equal(t, expectedKey.Private(), reparsedKey.Private())
assert.Equal(t, expectedKey.Public(), reparsedKey.Public())
}
// Given a keystore and expected key, generate an encrypted PEM of the key
// and assert that the then imported key is the same and encrypted with the
// right password.
func assertImportKeySuccess(
t *testing.T, s KeyStore, expectedKey data.PrivateKey) {
pemBytes, err := EncryptPrivateKey(expectedKey, cannedPassphrase)
assert.NoError(t, err)
err = s.ImportKey(pemBytes, "root")
assert.NoError(t, err)
reimportedKey, reimportedAlias, err := s.GetKey(expectedKey.ID())
assert.NoError(t, err)
assert.Equal(t, "root", reimportedAlias)
assert.Equal(t, expectedKey.Private(), reimportedKey.Private())
assert.Equal(t, expectedKey.Public(), reimportedKey.Public())
}

View File

@ -44,6 +44,8 @@ type KeyStore interface {
GetKey(name string) (data.PrivateKey, string, error)
ListKeys() map[string]string
RemoveKey(name string) error
ExportKey(name string) ([]byte, error)
ImportKey(pemBytes []byte, alias string) error
}
type cachedKey struct {