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 <aaron.lehmann@docker.com>
This commit is contained in:
Aaron Lehmann 2015-07-14 11:08:48 -07:00
parent 3cc3a0d489
commit d5c7c40955
4 changed files with 71 additions and 36 deletions

View File

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

View File

@ -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")

View File

@ -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 {

View File

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