Adding EncryptedFileStore and changing interfaces

Signed-off-by: Diogo Monica <diogo@docker.com>
This commit is contained in:
Diogo Monica 2015-07-05 21:02:16 -07:00
parent fd8471038c
commit d5cdeb93bb
8 changed files with 100 additions and 130 deletions

View File

@ -113,7 +113,7 @@ func keysRemove(cmd *cobra.Command, args []string) {
} }
// Remove all the keys under the Global Unique Name // Remove all the keys under the Global Unique Name
err = privKeyStore.RemoveAll(gunOrID) err = privKeyStore.RemoveDir(gunOrID)
if err != nil { if err != nil {
fatalf("failed to remove all Private keys under Global Unique Name: %s", gunOrID) fatalf("failed to remove all Private keys under Global Unique Name: %s", gunOrID)
} }

View File

@ -25,7 +25,7 @@ const tufDir string = configPath + "tuf/"
var caStore trustmanager.X509Store var caStore trustmanager.X509Store
var certificateStore trustmanager.X509Store var certificateStore trustmanager.X509Store
var privKeyStore *trustmanager.KeyFileStore var privKeyStore trustmanager.EncryptedFileStore
var rawOutput bool var rawOutput bool

View File

@ -15,14 +15,20 @@ type FileStore interface {
Add(fileName string, data []byte) error Add(fileName string, data []byte) error
Remove(fileName string) error Remove(fileName string) error
RemoveDir(directoryName string) error RemoveDir(directoryName string) error
GetData(fileName string) ([]byte, error) Get(fileName string) ([]byte, error)
GetPath(fileName string) string GetPath(fileName string) string
ListAll() []string ListAll() []string
ListDir(directoryName string) []string ListDir(directoryName string) []string
} }
// fileStore implements FileStore type EncryptedFileStore interface {
type fileStore struct { FileStore
AddEncrypted(fileName string, keyBytes []byte, passphrase string) error
GetDecrypted(fileName string, passphrase string) ([]byte, error)
}
// SimpleFileStore implements FileStore
type SimpleFileStore struct {
baseDir string baseDir string
fileExt string fileExt string
perms os.FileMode perms os.FileMode
@ -34,7 +40,7 @@ func NewFileStore(baseDir string, fileExt string) (FileStore, error) {
return nil, err return nil, err
} }
return &fileStore{ return &SimpleFileStore{
baseDir: baseDir, baseDir: baseDir,
fileExt: fileExt, fileExt: fileExt,
perms: visible, perms: visible,
@ -47,7 +53,7 @@ func NewPrivateFileStore(baseDir string, fileExt string) (FileStore, error) {
return nil, err return nil, err
} }
return &fileStore{ return &SimpleFileStore{
baseDir: baseDir, baseDir: baseDir,
fileExt: fileExt, fileExt: fileExt,
perms: private, perms: private,
@ -55,21 +61,21 @@ func NewPrivateFileStore(baseDir string, fileExt string) (FileStore, error) {
} }
// Add writes data to a file with a given name // 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) filePath := f.genFilePath(name)
createDirectory(filepath.Dir(filePath), f.perms) createDirectory(filepath.Dir(filePath), f.perms)
return ioutil.WriteFile(filePath, data, f.perms) return ioutil.WriteFile(filePath, data, f.perms)
} }
// Remove removes a file identified by name // Remove removes a file identified by name
func (f *fileStore) Remove(name string) error { func (f *SimpleFileStore) Remove(name string) error {
// Attempt to remove // Attempt to remove
filePath := f.genFilePath(name) filePath := f.genFilePath(name)
return os.Remove(filePath) return os.Remove(filePath)
} }
// RemoveDir removes the directory identified by name // 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) dirPath := filepath.Join(f.baseDir, name)
// Check to see if directory exists // Check to see if directory exists
@ -86,8 +92,8 @@ func (f *fileStore) RemoveDir(name string) error {
return os.RemoveAll(dirPath) return os.RemoveAll(dirPath)
} }
// GetData returns the data given a file name // Get returns the data given a file name
func (f *fileStore) GetData(name string) ([]byte, error) { func (f *SimpleFileStore) Get(name string) ([]byte, error) {
filePath := f.genFilePath(name) filePath := f.genFilePath(name)
data, err := ioutil.ReadFile(filePath) data, err := ioutil.ReadFile(filePath)
if err != nil { 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 // 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) return f.genFilePath(name)
} }
// List lists all the files inside of a store // List lists all the files inside of a store
func (f *fileStore) ListAll() []string { func (f *SimpleFileStore) ListAll() []string {
return f.list(f.baseDir) return f.list(f.baseDir)
} }
// List lists all the files inside of a directory identified by a name // 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) fullPath := filepath.Join(f.baseDir, name)
return f.list(fullPath) return f.list(fullPath)
} }
// list lists all the files in a directory given a full path // 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) files := make([]string, 0, 0)
filepath.Walk(path, func(fp string, fi os.FileInfo, err error) error { filepath.Walk(path, func(fp string, fi os.FileInfo, err error) error {
// If there are errors, ignore this particular file // 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 // 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) fileName := fmt.Sprintf("%s.%s", name, f.fileExt)
return filepath.Join(f.baseDir, fileName) return filepath.Join(f.baseDir, fileName)
} }

View File

@ -26,8 +26,8 @@ func TestAddFile(t *testing.T) {
// Since we're generating this manually we need to add the extension '.' // Since we're generating this manually we need to add the extension '.'
expectedFilePath := filepath.Join(tempBaseDir, testName+"."+testExt) expectedFilePath := filepath.Join(tempBaseDir, testName+"."+testExt)
// Create our FileStore // Create our SimpleFileStore
store := &fileStore{ store := &SimpleFileStore{
baseDir: tempBaseDir, baseDir: tempBaseDir,
fileExt: testExt, fileExt: testExt,
perms: perms, perms: perms,
@ -69,8 +69,8 @@ func TestRemoveFile(t *testing.T) {
t.Fatalf("failed to generate random file: %v", err) t.Fatalf("failed to generate random file: %v", err)
} }
// Create our FileStore // Create our SimpleFileStore
store := &fileStore{ store := &SimpleFileStore{
baseDir: tempBaseDir, baseDir: tempBaseDir,
fileExt: testExt, fileExt: testExt,
perms: perms, perms: perms,
@ -108,8 +108,8 @@ func TestRemoveDir(t *testing.T) {
t.Fatalf("failed to generate random file: %v", err) t.Fatalf("failed to generate random file: %v", err)
} }
// Create our FileStore // Create our SimpleFileStore
store := &fileStore{ store := &SimpleFileStore{
baseDir: tempBaseDir, baseDir: tempBaseDir,
fileExt: testExt, fileExt: testExt,
perms: perms, perms: perms,
@ -151,8 +151,8 @@ func TestListAll(t *testing.T) {
} }
} }
// Create our FileStore // Create our SimpleFileStore
store := &fileStore{ store := &SimpleFileStore{
baseDir: tempBaseDir, baseDir: tempBaseDir,
fileExt: testExt, fileExt: testExt,
perms: perms, perms: perms,
@ -188,8 +188,8 @@ func TestListDir(t *testing.T) {
} }
} }
// Create our FileStore // Create our SimpleFileStore
store := &fileStore{ store := &SimpleFileStore{
baseDir: tempBaseDir, baseDir: tempBaseDir,
fileExt: testExt, fileExt: testExt,
perms: perms, perms: perms,
@ -213,8 +213,8 @@ func TestGetPath(t *testing.T) {
testExt := "crt" testExt := "crt"
perms := os.FileMode(0755) perms := os.FileMode(0755)
// Create our FileStore // Create our SimpleFileStore
store := &fileStore{ store := &SimpleFileStore{
baseDir: "", baseDir: "",
fileExt: testExt, fileExt: testExt,
perms: perms, perms: perms,
@ -249,13 +249,13 @@ func TestGetData(t *testing.T) {
t.Fatalf("failed to generate random file: %v", err) t.Fatalf("failed to generate random file: %v", err)
} }
// Create our FileStore // Create our SimpleFileStore
store := &fileStore{ store := &SimpleFileStore{
baseDir: tempBaseDir, baseDir: tempBaseDir,
fileExt: testExt, fileExt: testExt,
perms: perms, perms: perms,
} }
testData, err := store.GetData(testName) testData, err := store.Get(testName)
if err != nil { if err != nil {
t.Fatalf("failed to get data from: %s", testName) t.Fatalf("failed to get data from: %s", testName)

View File

@ -1,10 +1,6 @@
package trustmanager package trustmanager
import ( import "errors"
"crypto"
"errors"
"fmt"
)
const ( const (
keyExtension = "key" keyExtension = "key"
@ -12,88 +8,49 @@ const (
// KeyFileStore persists and manages private keys on disk // KeyFileStore persists and manages private keys on disk
type KeyFileStore struct { type KeyFileStore struct {
fingerprintMap map[string]string FileStore
fileStore FileStore
} }
// NewKeyFileStore returns a new KeyFileStore. // NewKeyFileStore returns a new KeyFileStore creating a private directory to
func NewKeyFileStore(directory string) (*KeyFileStore, error) { // hold the keys.
fileStore, err := NewPrivateFileStore(directory, keyExtension) func NewKeyFileStore(baseDir string) (EncryptedFileStore, error) {
fileStore, err := NewFileStore(baseDir, keyExtension)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &KeyFileStore{ return &KeyFileStore{fileStore}, nil
fingerprintMap: make(map[string]string),
fileStore: fileStore,
}, nil
} }
// Add stores both the PrivateKey bytes in a file // AddEncrypted stores the contents of a PEM-encoded private key as an encrypted PEM block
func (s *KeyFileStore) Add(fileName string, privKey crypto.PrivateKey) error { func (s *KeyFileStore) AddEncrypted(fileName string, pemKey []byte, passphrase string) error {
if privKey == nil {
return errors.New("adding nil key to keyFileStore")
}
pemKey, err := KeyToPEM(privKey) privKey, err := ParsePEMPrivateKey(pemKey)
if err != nil { if err != nil {
return err return err
} }
return s.fileStore.Add(fileName, pemKey) encryptedKey, err := EncryptPrivateKey(privKey, passphrase)
}
// 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)
if err != nil { if err != nil {
return err return err
} }
fmt.Println(string(encryptedKey)) return s.Add(fileName, encryptedKey)
return s.fileStore.Add(fileName, encryptedKey)
} }
// GetDecrypted decrypts and returns the private key // GetDecrypted decrypts and returns the PEM Encoded private key given a flename
func (s *KeyFileStore) GetDecrypted(fileName string, passphrase string) (crypto.PrivateKey, error) { // and a passphrase
keyBytes, err := s.fileStore.GetData(fileName) func (s *KeyFileStore) GetDecrypted(fileName string, passphrase string) ([]byte, error) {
keyBytes, err := s.Get(fileName)
if err != nil { if err != nil {
return nil, errors.New("could not retrieve private key material") 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 return KeyToPEM(privKey)
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)
} }

View File

@ -35,8 +35,14 @@ func TestAddKey(t *testing.T) {
t.Fatalf("could not generate private key: %v", err) 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 // Call the Add function
err = store.Add(testName, key) err = store.Add(testName, pemKey)
if err != nil { if err != nil {
t.Fatalf("failed to add file to store: %v", err) t.Fatalf("failed to add file to store: %v", err)
} }
@ -106,16 +112,11 @@ EMl3eFOJXjIch/wIesRSN+2dGOsl7neercjMh1i9RvpCwHDx/E0=
} }
// Call the Get function // Call the Get function
privKey, err := store.Get(testName) pemKey, err := store.Get(testName)
if err != nil { if err != nil {
t.Fatalf("failed to get file from store: %v", err) 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) { if !bytes.Equal(testData, pemKey) {
t.Fatalf("unexpected content in the file: %s", filePath) 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) 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 // Call the Add function
err = store.AddEncrypted(testName, key, "diogomonica") err = store.AddEncrypted(testName, pemKey, "diogomonica")
if err != nil { if err != nil {
t.Fatalf("failed to add file to store: %v", err) 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) t.Fatalf("could not decrypt private key: %v", err)
} }
pemKey, err := KeyToPEM(key) if !strings.Contains(string(pemKey), string(pemPrivKey)) {
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)) {
t.Fatalf("expected private key content in the file: %s", expectedFilePath) 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) 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 // Call the Add function
err = store.AddEncrypted(testName, key, "diogomonica") err = store.AddEncrypted(testName, pemKey, "diogomonica")
if err != nil { if err != nil {
t.Fatalf("failed to add file to store: %v", err) 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) 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 // Call the Add function
err = store.AddEncrypted(testName, key, "diogomonica") err = store.AddEncrypted(testName, pemKey, "diogomonica")
if err != nil { if err != nil {
t.Fatalf("failed to add file to stoAFre: %v", err) t.Fatalf("failed to add file to stoAFre: %v", err)
} }

View File

@ -76,7 +76,7 @@ func (s X509FileStore) addNamedCert(cert *x509.Certificate) error {
} }
// Convert certificate to PEM // Convert certificate to PEM
certBytes := ToPEM(cert) certBytes := CertToPEM(cert)
// Compute FileName // Compute FileName
fileName := fileName(cert) fileName := fileName(cert)

View File

@ -49,14 +49,14 @@ func GetCertFromURL(urlStr string) (*x509.Certificate, error) {
return cert, nil return cert, nil
} }
// ToPEM is an utility function returns a PEM encoded x509 Certificate // CertToPEM is an utility function returns a PEM encoded x509 Certificate
func ToPEM(cert *x509.Certificate) []byte { func CertToPEM(cert *x509.Certificate) []byte {
pemCert := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}) pemCert := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw})
return pemCert 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) { func KeyToPEM(key crypto.PrivateKey) ([]byte, error) {
rsaKey, ok := key.(*rsa.PrivateKey) rsaKey, ok := key.(*rsa.PrivateKey)
if !ok { 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 return pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: keyBytes}), nil
} }
// KeyToEncryptedPEM is an utility function returns a PEM encoded Key // EncryptPrivateKey returns an encrypted PEM encoded key given a Private key
func KeyToEncryptedPEM(key crypto.PrivateKey, passphrase string) ([]byte, error) { // and a passphrase
func EncryptPrivateKey(key crypto.PrivateKey, passphrase string) ([]byte, error) {
rsaKey, ok := key.(*rsa.PrivateKey) rsaKey, ok := key.(*rsa.PrivateKey)
if !ok { if !ok {
return nil, errors.New("only RSA keys are currently supported") 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) keyBytes := x509.MarshalPKCS1PrivateKey(rsaKey)
//TODO(diogo): if we do keystretching, where do we keep the salt + params?
password := []byte(passphrase) password := []byte(passphrase)
cipherType := x509.PEMCipherAES256 cipherType := x509.PEMCipherAES256
blockType := "RSA PRIVATE KEY" blockType := "RSA PRIVATE KEY"
@ -168,16 +168,16 @@ func LoadKeyFromFile(filename string) (crypto.PrivateKey, error) {
return nil, err return nil, err
} }
key, err := ParseRawPrivateKey(pemBytes) key, err := ParsePEMPrivateKey(pemBytes)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return key, nil 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). // only supports RSA (PKCS#1).
func ParseRawPrivateKey(pemBytes []byte) (crypto.PrivateKey, error) { func ParsePEMPrivateKey(pemBytes []byte) (crypto.PrivateKey, error) {
block, _ := pem.Decode(pemBytes) block, _ := pem.Decode(pemBytes)
if block == nil { if block == nil {
return nil, errors.New("no valid key found") 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). // 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) block, _ := pem.Decode(pemBytes)
if block == nil { if block == nil {
return nil, errors.New("no valid private key found") return nil, errors.New("no valid private key found")