From d5c7c40955d86ea000bb464eb4e14a08312613f0 Mon Sep 17 00:00:00 2001 From: Aaron Lehmann Date: Tue, 14 Jul 2015 11:08:48 -0700 Subject: [PATCH] Introduce a KeyStoreManager to abstract management of root and non-root key storage This structure encapsulates what used to be "rootKeyStore" and "privKeyStore". These are being moved out of NotaryRepository, so that operations like listing keys, importing keys, and exporting keys aren't tied to a NotaryRepository structure. Signed-off-by: Aaron Lehmann --- client/client.go | 50 ++++++++++------------------- client/client_test.go | 4 +-- cmd/notary/tuf.go | 2 +- keystoremanager/keystoremanager.go | 51 ++++++++++++++++++++++++++++++ 4 files changed, 71 insertions(+), 36 deletions(-) create mode 100644 keystoremanager/keystoremanager.go diff --git a/client/client.go b/client/client.go index f25c86e53d..d58896da7c 100644 --- a/client/client.go +++ b/client/client.go @@ -20,6 +20,7 @@ import ( "github.com/Sirupsen/logrus" "github.com/docker/notary/client/changelist" + "github.com/docker/notary/keystoremanager" "github.com/docker/notary/trustmanager" "github.com/endophage/gotuf" tufclient "github.com/endophage/gotuf/client" @@ -44,9 +45,7 @@ func (err *ErrRepoNotInitialized) Error() string { // Default paths should end with a '/' so directory creation works correctly const ( trustDir string = "/trusted_certificates/" - privDir string = "/private/" tufDir string = "/tuf/" - rootKeysDir string = privDir + "/root_keys/" rsaKeySize int = 2048 // Used for snapshots and targets keys rsaRootKeySize int = 4096 // Used for new root keys ) @@ -75,9 +74,8 @@ type NotaryRepository struct { fileStore store.MetadataStore cryptoService signed.CryptoService tufRepo *tuf.TufRepo - privKeyStore *trustmanager.KeyFileStore - rootKeyStore *trustmanager.KeyFileStore roundTrip http.RoundTripper + KeyStoreManager *keystoremanager.KeyStoreManager } // Target represents a simplified version of the data TUF operates on, so external @@ -108,26 +106,25 @@ func NewTarget(targetName string, targetPath string) (*Target, error) { // (usually ~/.docker/trust/). func NewNotaryRepository(baseDir, gun, baseURL string, rt http.RoundTripper) (*NotaryRepository, error) { trustDir := filepath.Join(baseDir, trustDir) - rootKeysDir := filepath.Join(baseDir, rootKeysDir) - privKeyStore, err := trustmanager.NewKeyFileStore(filepath.Join(baseDir, privDir)) + keyStoreManager, err := keystoremanager.NewKeyStoreManager(baseDir) if err != nil { return nil, err } - cryptoService := NewCryptoService(gun, privKeyStore, "") + cryptoService := NewCryptoService(gun, keyStoreManager.NonRootKeyStore(), "") nRepo := &NotaryRepository{ - gun: gun, - baseDir: baseDir, - baseURL: baseURL, - tufRepoPath: filepath.Join(baseDir, tufDir, gun), - cryptoService: cryptoService, - privKeyStore: privKeyStore, - roundTrip: rt, + gun: gun, + baseDir: baseDir, + baseURL: baseURL, + tufRepoPath: filepath.Join(baseDir, tufDir, gun), + cryptoService: cryptoService, + roundTrip: rt, + KeyStoreManager: keyStoreManager, } - if err := nRepo.loadKeys(trustDir, rootKeysDir); err != nil { + if err := nRepo.loadKeys(trustDir); err != nil { return nil, err } @@ -166,7 +163,7 @@ func (r *NotaryRepository) Initialize(uCryptoService *UnlockedCryptoService) err // is associated with. This is used to be able to retrieve the root private key // associated with a particular certificate logrus.Debugf("Linking %s to %s.", rootKey.ID(), uCryptoService.ID()) - err = r.rootKeyStore.Link(uCryptoService.ID(), rootKey.ID()) + err = r.KeyStoreManager.RootKeyStore().Link(uCryptoService.ID(), rootKey.ID()) if err != nil { return err } @@ -643,12 +640,6 @@ func (r *NotaryRepository) bootstrapClient() (*tufclient.Client, error) { ), nil } -// ListRootKeys returns the IDs for all of the root keys. It ignores symlinks -// if any exist. -func (r *NotaryRepository) ListRootKeys() []string { - return r.rootKeyStore.ListKeys() -} - // GenRootKey generates a new root key protected by a given passphrase // TODO(diogo): show not create keys manually, should use a cryptoservice instead func (r *NotaryRepository) GenRootKey(algorithm, passphrase string) (string, error) { @@ -672,26 +663,26 @@ func (r *NotaryRepository) GenRootKey(algorithm, passphrase string) (string, err } // Changing the root - r.rootKeyStore.AddEncryptedKey(privKey.ID(), privKey, passphrase) + r.KeyStoreManager.RootKeyStore().AddEncryptedKey(privKey.ID(), privKey, passphrase) return privKey.ID(), nil } // GetRootCryptoService retreives a root key and a cryptoservice to use with it func (r *NotaryRepository) GetRootCryptoService(rootKeyID, passphrase string) (*UnlockedCryptoService, error) { - privKey, err := r.rootKeyStore.GetDecryptedKey(rootKeyID, passphrase) + privKey, err := r.KeyStoreManager.RootKeyStore().GetDecryptedKey(rootKeyID, passphrase) if err != nil { return nil, fmt.Errorf("could not get decrypted root key with keyID: %s, %v", rootKeyID, err) } - cryptoService := NewCryptoService("", r.rootKeyStore, passphrase) + cryptoService := NewCryptoService("", r.KeyStoreManager.RootKeyStore(), passphrase) return &UnlockedCryptoService{ privKey: privKey, cryptoService: cryptoService}, nil } -func (r *NotaryRepository) loadKeys(trustDir, rootKeysDir string) error { +func (r *NotaryRepository) loadKeys(trustDir string) error { // Load all CAs that aren't expired and don't use SHA1 caStore, err := trustmanager.NewX509FilteredFileStore(trustDir, func(cert *x509.Certificate) bool { return cert.IsCA && cert.BasicConstraintsValid && cert.SubjectKeyId != nil && @@ -716,15 +707,8 @@ func (r *NotaryRepository) loadKeys(trustDir, rootKeysDir string) error { return err } - // Load the keystore that will hold all of our encrypted Root Private Keys - rootKeyStore, err := trustmanager.NewKeyFileStore(rootKeysDir) - if err != nil { - return err - } - r.caStore = caStore r.certificateStore = certificateStore - r.rootKeyStore = rootKeyStore return nil } diff --git a/client/client_test.go b/client/client_test.go index dd74ed0cbd..fc76d95c22 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -91,7 +91,7 @@ func testInitRepo(t *testing.T, rootType data.KeyAlgorithm) { // Look for keys in private. The filenames should match the key IDs // in the private key store. - privKeyList := repo.privKeyStore.ListFiles(true) + privKeyList := repo.KeyStoreManager.NonRootKeyStore().ListFiles(true) for _, privKeyName := range privKeyList { _, err := os.Stat(privKeyName) assert.NoError(t, err, "missing private key: %s", privKeyName) @@ -309,7 +309,7 @@ func testAddListTarget(t *testing.T, rootType data.KeyAlgorithm) { var tempKey data.PrivateKey json.Unmarshal([]byte(timestampECDSAKeyJSON), &tempKey) - repo.privKeyStore.AddKey(filepath.Join(gun, tempKey.ID()), &tempKey) + repo.KeyStoreManager.NonRootKeyStore().AddKey(filepath.Join(gun, tempKey.ID()), &tempKey) mux.HandleFunc("/v2/docker.com/notary/_trust/tuf/root.json", func(w http.ResponseWriter, r *http.Request) { rootJSONFile := filepath.Join(tempBaseDir, "tuf", gun, "metadata", "root.json") diff --git a/cmd/notary/tuf.go b/cmd/notary/tuf.go index c90a0610b7..3016b266f4 100644 --- a/cmd/notary/tuf.go +++ b/cmd/notary/tuf.go @@ -110,7 +110,7 @@ func tufInit(cmd *cobra.Command, args []string) { fatalf(err.Error()) } - keysList := nRepo.ListRootKeys() + keysList := nRepo.KeyStoreManager.RootKeyStore().ListKeys() var passphrase string var rootKeyID string if len(keysList) < 1 { diff --git a/keystoremanager/keystoremanager.go b/keystoremanager/keystoremanager.go new file mode 100644 index 0000000000..96f2009d50 --- /dev/null +++ b/keystoremanager/keystoremanager.go @@ -0,0 +1,51 @@ +package keystoremanager + +import ( + "path/filepath" + + "github.com/docker/notary/trustmanager" +) + +// KeyStoreManager is an abstraction around the root and non-root key stores +type KeyStoreManager struct { + rootKeyStore *trustmanager.KeyFileStore + nonRootKeyStore *trustmanager.KeyFileStore +} + +const ( + privDir = "private" + rootKeysSubdir = "root_keys" +) + +// NewKeyStoreManager returns an initialized KeyStoreManager, or an error +// if it fails to create the KeyFileStores +func NewKeyStoreManager(baseDir string) (*KeyStoreManager, error) { + nonRootKeyStore, err := trustmanager.NewKeyFileStore(filepath.Join(baseDir, privDir)) + if err != nil { + return nil, err + } + + // Load the keystore that will hold all of our encrypted Root Private Keys + rootKeysPath := filepath.Join(baseDir, privDir, rootKeysSubdir) + rootKeyStore, err := trustmanager.NewKeyFileStore(rootKeysPath) + if err != nil { + return nil, err + } + + return &KeyStoreManager{ + rootKeyStore: rootKeyStore, + nonRootKeyStore: nonRootKeyStore, + }, nil +} + +// RootKeyStore returns the root key store being managed by this +// KeyStoreManager +func (km *KeyStoreManager) RootKeyStore() *trustmanager.KeyFileStore { + return km.rootKeyStore +} + +// NonRootKeyStore returns the non-root key store being managed by this +// KeyStoreManager +func (km *KeyStoreManager) NonRootKeyStore() *trustmanager.KeyFileStore { + return km.nonRootKeyStore +}