diff --git a/cmd/notary/keys.go b/cmd/notary/keys.go index 60df7e3c1d..f8ec61bcf7 100644 --- a/cmd/notary/keys.go +++ b/cmd/notary/keys.go @@ -113,7 +113,7 @@ func keysRemove(cmd *cobra.Command, args []string) { } // Remove all the keys under the Global Unique Name - err = privKeyStore.RemoveAll(gunOrID) + err = privKeyStore.RemoveDir(gunOrID) if err != nil { fatalf("failed to remove all Private keys under Global Unique Name: %s", gunOrID) } diff --git a/cmd/notary/main.go b/cmd/notary/main.go index 347ac29f4d..a8bf9b61a1 100644 --- a/cmd/notary/main.go +++ b/cmd/notary/main.go @@ -25,7 +25,7 @@ const tufDir string = configPath + "tuf/" var caStore trustmanager.X509Store var certificateStore trustmanager.X509Store -var privKeyStore *trustmanager.KeyFileStore +var privKeyStore trustmanager.EncryptedFileStore var rawOutput bool diff --git a/trustmanager/filestore.go b/trustmanager/filestore.go index a573e13b58..af22ae1ea6 100644 --- a/trustmanager/filestore.go +++ b/trustmanager/filestore.go @@ -15,14 +15,20 @@ type FileStore interface { Add(fileName string, data []byte) error Remove(fileName string) error RemoveDir(directoryName string) error - GetData(fileName string) ([]byte, error) + Get(fileName string) ([]byte, error) GetPath(fileName string) string ListAll() []string ListDir(directoryName string) []string } -// fileStore implements FileStore -type fileStore struct { +type EncryptedFileStore interface { + FileStore + AddEncrypted(fileName string, keyBytes []byte, passphrase string) error + GetDecrypted(fileName string, passphrase string) ([]byte, error) +} + +// SimpleFileStore implements FileStore +type SimpleFileStore struct { baseDir string fileExt string perms os.FileMode @@ -34,7 +40,7 @@ func NewFileStore(baseDir string, fileExt string) (FileStore, error) { return nil, err } - return &fileStore{ + return &SimpleFileStore{ baseDir: baseDir, fileExt: fileExt, perms: visible, @@ -47,7 +53,7 @@ func NewPrivateFileStore(baseDir string, fileExt string) (FileStore, error) { return nil, err } - return &fileStore{ + return &SimpleFileStore{ baseDir: baseDir, fileExt: fileExt, perms: private, @@ -55,21 +61,21 @@ func NewPrivateFileStore(baseDir string, fileExt string) (FileStore, error) { } // Add writes data to a file with a given name -func (f *fileStore) Add(name string, data []byte) error { +func (f *SimpleFileStore) Add(name string, data []byte) error { filePath := f.genFilePath(name) createDirectory(filepath.Dir(filePath), f.perms) return ioutil.WriteFile(filePath, data, f.perms) } // Remove removes a file identified by name -func (f *fileStore) Remove(name string) error { +func (f *SimpleFileStore) Remove(name string) error { // Attempt to remove filePath := f.genFilePath(name) return os.Remove(filePath) } // RemoveDir removes the directory identified by name -func (f *fileStore) RemoveDir(name string) error { +func (f *SimpleFileStore) RemoveDir(name string) error { dirPath := filepath.Join(f.baseDir, name) // Check to see if directory exists @@ -86,8 +92,8 @@ func (f *fileStore) RemoveDir(name string) error { return os.RemoveAll(dirPath) } -// GetData returns the data given a file name -func (f *fileStore) GetData(name string) ([]byte, error) { +// Get returns the data given a file name +func (f *SimpleFileStore) Get(name string) ([]byte, error) { filePath := f.genFilePath(name) data, err := ioutil.ReadFile(filePath) if err != nil { @@ -98,23 +104,23 @@ func (f *fileStore) GetData(name string) ([]byte, error) { } // GetPath returns the full final path of a file with a given name -func (f *fileStore) GetPath(name string) string { +func (f *SimpleFileStore) GetPath(name string) string { return f.genFilePath(name) } // List lists all the files inside of a store -func (f *fileStore) ListAll() []string { +func (f *SimpleFileStore) ListAll() []string { return f.list(f.baseDir) } // List lists all the files inside of a directory identified by a name -func (f *fileStore) ListDir(name string) []string { +func (f *SimpleFileStore) ListDir(name string) []string { fullPath := filepath.Join(f.baseDir, name) return f.list(fullPath) } // list lists all the files in a directory given a full path -func (f *fileStore) list(path string) []string { +func (f *SimpleFileStore) list(path string) []string { files := make([]string, 0, 0) filepath.Walk(path, func(fp string, fi os.FileInfo, err error) error { // If there are errors, ignore this particular file @@ -137,7 +143,7 @@ func (f *fileStore) list(path string) []string { } // genFilePath returns the full path with extension given a file name -func (f *fileStore) genFilePath(name string) string { +func (f *SimpleFileStore) genFilePath(name string) string { fileName := fmt.Sprintf("%s.%s", name, f.fileExt) return filepath.Join(f.baseDir, fileName) } diff --git a/trustmanager/filestore_test.go b/trustmanager/filestore_test.go index e80d91e662..2acba47fbc 100644 --- a/trustmanager/filestore_test.go +++ b/trustmanager/filestore_test.go @@ -26,8 +26,8 @@ func TestAddFile(t *testing.T) { // Since we're generating this manually we need to add the extension '.' expectedFilePath := filepath.Join(tempBaseDir, testName+"."+testExt) - // Create our FileStore - store := &fileStore{ + // Create our SimpleFileStore + store := &SimpleFileStore{ baseDir: tempBaseDir, fileExt: testExt, perms: perms, @@ -69,8 +69,8 @@ func TestRemoveFile(t *testing.T) { t.Fatalf("failed to generate random file: %v", err) } - // Create our FileStore - store := &fileStore{ + // Create our SimpleFileStore + store := &SimpleFileStore{ baseDir: tempBaseDir, fileExt: testExt, perms: perms, @@ -108,8 +108,8 @@ func TestRemoveDir(t *testing.T) { t.Fatalf("failed to generate random file: %v", err) } - // Create our FileStore - store := &fileStore{ + // Create our SimpleFileStore + store := &SimpleFileStore{ baseDir: tempBaseDir, fileExt: testExt, perms: perms, @@ -151,8 +151,8 @@ func TestListAll(t *testing.T) { } } - // Create our FileStore - store := &fileStore{ + // Create our SimpleFileStore + store := &SimpleFileStore{ baseDir: tempBaseDir, fileExt: testExt, perms: perms, @@ -188,8 +188,8 @@ func TestListDir(t *testing.T) { } } - // Create our FileStore - store := &fileStore{ + // Create our SimpleFileStore + store := &SimpleFileStore{ baseDir: tempBaseDir, fileExt: testExt, perms: perms, @@ -213,8 +213,8 @@ func TestGetPath(t *testing.T) { testExt := "crt" perms := os.FileMode(0755) - // Create our FileStore - store := &fileStore{ + // Create our SimpleFileStore + store := &SimpleFileStore{ baseDir: "", fileExt: testExt, perms: perms, @@ -249,13 +249,13 @@ func TestGetData(t *testing.T) { t.Fatalf("failed to generate random file: %v", err) } - // Create our FileStore - store := &fileStore{ + // Create our SimpleFileStore + store := &SimpleFileStore{ baseDir: tempBaseDir, fileExt: testExt, perms: perms, } - testData, err := store.GetData(testName) + testData, err := store.Get(testName) if err != nil { t.Fatalf("failed to get data from: %s", testName) diff --git a/trustmanager/keyfilestore.go b/trustmanager/keyfilestore.go index c1d3087926..6adb982430 100644 --- a/trustmanager/keyfilestore.go +++ b/trustmanager/keyfilestore.go @@ -1,10 +1,6 @@ package trustmanager -import ( - "crypto" - "errors" - "fmt" -) +import "errors" const ( keyExtension = "key" @@ -12,88 +8,49 @@ const ( // KeyFileStore persists and manages private keys on disk type KeyFileStore struct { - fingerprintMap map[string]string - fileStore FileStore + FileStore } -// NewKeyFileStore returns a new KeyFileStore. -func NewKeyFileStore(directory string) (*KeyFileStore, error) { - fileStore, err := NewPrivateFileStore(directory, keyExtension) +// NewKeyFileStore returns a new KeyFileStore creating a private directory to +// hold the keys. +func NewKeyFileStore(baseDir string) (EncryptedFileStore, error) { + fileStore, err := NewFileStore(baseDir, keyExtension) if err != nil { return nil, err } - return &KeyFileStore{ - fingerprintMap: make(map[string]string), - fileStore: fileStore, - }, nil + return &KeyFileStore{fileStore}, nil } -// Add stores both the PrivateKey bytes in a file -func (s *KeyFileStore) Add(fileName string, privKey crypto.PrivateKey) error { - if privKey == nil { - return errors.New("adding nil key to keyFileStore") - } +// AddEncrypted stores the contents of a PEM-encoded private key as an encrypted PEM block +func (s *KeyFileStore) AddEncrypted(fileName string, pemKey []byte, passphrase string) error { - pemKey, err := KeyToPEM(privKey) + privKey, err := ParsePEMPrivateKey(pemKey) if err != nil { return err } - return s.fileStore.Add(fileName, pemKey) -} - -// Get returns a PrivateKey given a filename -func (s *KeyFileStore) Get(fileName string) (crypto.PrivateKey, error) { - keyBytes, err := s.fileStore.GetData(fileName) - if err != nil { - return nil, errors.New("Could not retrieve private key material") - } - - return ParseRawPrivateKey(keyBytes) -} - -// AddEncrypted stores the contents of the private key as an encrypted PEM block -func (s *KeyFileStore) AddEncrypted(fileName string, privKey crypto.PrivateKey, passphrase string) error { - if privKey == nil { - return errors.New("adding nil key to keyFileStore") - } - - encryptedKey, err := KeyToEncryptedPEM(privKey, passphrase) + encryptedKey, err := EncryptPrivateKey(privKey, passphrase) if err != nil { return err } - fmt.Println(string(encryptedKey)) - return s.fileStore.Add(fileName, encryptedKey) + return s.Add(fileName, encryptedKey) } -// GetDecrypted decrypts and returns the private key -func (s *KeyFileStore) GetDecrypted(fileName string, passphrase string) (crypto.PrivateKey, error) { - keyBytes, err := s.fileStore.GetData(fileName) +// GetDecrypted decrypts and returns the PEM Encoded private key given a flename +// and a passphrase +func (s *KeyFileStore) GetDecrypted(fileName string, passphrase string) ([]byte, error) { + keyBytes, err := s.Get(fileName) if err != nil { return nil, errors.New("could not retrieve private key material") } - return ParseRawEncryptedPrivateKey(keyBytes, passphrase) -} + // Gets an unencrypted PrivateKey. + privKey, err := ParsePEMEncryptedPrivateKey(keyBytes, passphrase) + if err != nil { + return nil, err + } -// Remove removes a key from a store -func (s *KeyFileStore) Remove(fileName string) error { - return s.fileStore.Remove(fileName) -} - -// RemoveAll removes all the keys under a directory -func (s *KeyFileStore) RemoveAll(directoryName string) error { - return s.fileStore.RemoveDir(directoryName) -} - -// List returns a list of all the keys the store is currently managing -func (s *KeyFileStore) ListAll() []string { - return s.fileStore.ListAll() -} - -// List returns a list of all the keys the store is currently managing -func (s *KeyFileStore) ListDir(directoryName string) []string { - return s.fileStore.ListDir(directoryName) + return KeyToPEM(privKey) } diff --git a/trustmanager/keyfilestore_test.go b/trustmanager/keyfilestore_test.go index 046fbf09df..f76283d455 100644 --- a/trustmanager/keyfilestore_test.go +++ b/trustmanager/keyfilestore_test.go @@ -35,8 +35,14 @@ func TestAddKey(t *testing.T) { t.Fatalf("could not generate private key: %v", err) } + // Get the PEM for the key + pemKey, err := KeyToPEM(key) + if err != nil { + t.Fatalf("failed to convert private key to PEM: %v", err) + } + // Call the Add function - err = store.Add(testName, key) + err = store.Add(testName, pemKey) if err != nil { t.Fatalf("failed to add file to store: %v", err) } @@ -106,16 +112,11 @@ EMl3eFOJXjIch/wIesRSN+2dGOsl7neercjMh1i9RvpCwHDx/E0= } // Call the Get function - privKey, err := store.Get(testName) + pemKey, err := store.Get(testName) if err != nil { t.Fatalf("failed to get file from store: %v", err) } - pemKey, err := KeyToPEM(privKey) - if err != nil { - t.Fatalf("failed to convert key to PEM: %v", err) - } - if !bytes.Equal(testData, pemKey) { t.Fatalf("unexpected content in the file: %s", filePath) } @@ -146,8 +147,14 @@ func TestAddEncryptedAndGetDecrypted(t *testing.T) { t.Fatalf("could not generate private key: %v", err) } + // Get PEM encodedd key + pemKey, err := KeyToPEM(key) + if err != nil { + t.Fatalf("Could not encode key to PEM: %v", err) + } + // Call the Add function - err = store.AddEncrypted(testName, key, "diogomonica") + err = store.AddEncrypted(testName, pemKey, "diogomonica") if err != nil { t.Fatalf("failed to add file to store: %v", err) } @@ -157,17 +164,7 @@ func TestAddEncryptedAndGetDecrypted(t *testing.T) { t.Fatalf("could not decrypt private key: %v", err) } - pemKey, err := KeyToPEM(key) - if err != nil { - t.Fatalf("could not convert private key to PEM: %v", err) - } - - decryptedPemKey, err := KeyToPEM(pemPrivKey) - if err != nil { - t.Fatalf("could not convert private key to PEM: %v", err) - } - - if !strings.Contains(string(pemKey), string(decryptedPemKey)) { + if !strings.Contains(string(pemKey), string(pemPrivKey)) { t.Fatalf("expected private key content in the file: %s", expectedFilePath) } } @@ -197,8 +194,13 @@ func TestGetDecryptedWithTamperedCipherText(t *testing.T) { t.Fatalf("could not generate private key: %v", err) } + // Get PEM encodedd key + pemKey, err := KeyToPEM(key) + if err != nil { + t.Fatalf("Could not encode key to PEM: %v", err) + } // Call the Add function - err = store.AddEncrypted(testName, key, "diogomonica") + err = store.AddEncrypted(testName, pemKey, "diogomonica") if err != nil { t.Fatalf("failed to add file to store: %v", err) } @@ -240,8 +242,13 @@ func TestGetDecryptedWithInvalidPassphrase(t *testing.T) { t.Fatalf("could not generate private key: %v", err) } + // Get PEM encodedd key + pemKey, err := KeyToPEM(key) + if err != nil { + t.Fatalf("Could not encode key to PEM: %v", err) + } // Call the Add function - err = store.AddEncrypted(testName, key, "diogomonica") + err = store.AddEncrypted(testName, pemKey, "diogomonica") if err != nil { t.Fatalf("failed to add file to stoAFre: %v", err) } diff --git a/trustmanager/x509filestore.go b/trustmanager/x509filestore.go index 8b26b1c98a..a9128b0cf1 100644 --- a/trustmanager/x509filestore.go +++ b/trustmanager/x509filestore.go @@ -76,7 +76,7 @@ func (s X509FileStore) addNamedCert(cert *x509.Certificate) error { } // Convert certificate to PEM - certBytes := ToPEM(cert) + certBytes := CertToPEM(cert) // Compute FileName fileName := fileName(cert) diff --git a/trustmanager/x509utils.go b/trustmanager/x509utils.go index 2c0798ff69..9e2eed0c8d 100644 --- a/trustmanager/x509utils.go +++ b/trustmanager/x509utils.go @@ -49,14 +49,14 @@ func GetCertFromURL(urlStr string) (*x509.Certificate, error) { return cert, nil } -// ToPEM is an utility function returns a PEM encoded x509 Certificate -func ToPEM(cert *x509.Certificate) []byte { +// CertToPEM is an utility function returns a PEM encoded x509 Certificate +func CertToPEM(cert *x509.Certificate) []byte { pemCert := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}) return pemCert } -// KeyToPEM is an utility function returns a PEM encoded Key +// KeyToPEM returns a PEM encoded key from a crypto.PrivateKey func KeyToPEM(key crypto.PrivateKey) ([]byte, error) { rsaKey, ok := key.(*rsa.PrivateKey) if !ok { @@ -67,8 +67,9 @@ func KeyToPEM(key crypto.PrivateKey) ([]byte, error) { return pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: keyBytes}), nil } -// KeyToEncryptedPEM is an utility function returns a PEM encoded Key -func KeyToEncryptedPEM(key crypto.PrivateKey, passphrase string) ([]byte, error) { +// EncryptPrivateKey returns an encrypted PEM encoded key given a Private key +// and a passphrase +func EncryptPrivateKey(key crypto.PrivateKey, passphrase string) ([]byte, error) { rsaKey, ok := key.(*rsa.PrivateKey) if !ok { return nil, errors.New("only RSA keys are currently supported") @@ -76,7 +77,6 @@ func KeyToEncryptedPEM(key crypto.PrivateKey, passphrase string) ([]byte, error) keyBytes := x509.MarshalPKCS1PrivateKey(rsaKey) - //TODO(diogo): if we do keystretching, where do we keep the salt + params? password := []byte(passphrase) cipherType := x509.PEMCipherAES256 blockType := "RSA PRIVATE KEY" @@ -168,16 +168,16 @@ func LoadKeyFromFile(filename string) (crypto.PrivateKey, error) { return nil, err } - key, err := ParseRawPrivateKey(pemBytes) + key, err := ParsePEMPrivateKey(pemBytes) if err != nil { return nil, err } return key, nil } -// ParseRawPrivateKey returns a private key from a PEM encoded private key. It +// ParsePEMPrivateKey returns a private key from a PEM encoded private key. It // only supports RSA (PKCS#1). -func ParseRawPrivateKey(pemBytes []byte) (crypto.PrivateKey, error) { +func ParsePEMPrivateKey(pemBytes []byte) (crypto.PrivateKey, error) { block, _ := pem.Decode(pemBytes) if block == nil { return nil, errors.New("no valid key found") @@ -191,9 +191,9 @@ func ParseRawPrivateKey(pemBytes []byte) (crypto.PrivateKey, error) { } } -// ParseRawEncryptedPrivateKey returns a private key from a PEM encrypted private key. It +// ParsePEMEncryptedPrivateKey returns a private key from a PEM encrypted private key. It // only supports RSA (PKCS#1). -func ParseRawEncryptedPrivateKey(pemBytes []byte, passphrase string) (crypto.PrivateKey, error) { +func ParsePEMEncryptedPrivateKey(pemBytes []byte, passphrase string) (crypto.PrivateKey, error) { block, _ := pem.Decode(pemBytes) if block == nil { return nil, errors.New("no valid private key found")