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
err = privKeyStore.RemoveAll(gunOrID)
err = privKeyStore.RemoveDir(gunOrID)
if err != nil {
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 certificateStore trustmanager.X509Store
var privKeyStore *trustmanager.KeyFileStore
var privKeyStore trustmanager.EncryptedFileStore
var rawOutput bool

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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