forked from toolshed/abra
		
	
		
			
				
	
	
		
			263 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			263 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package trustmanager
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"path/filepath"
 | |
| 	"strings"
 | |
| 	"sync"
 | |
| 
 | |
| 	"github.com/sirupsen/logrus"
 | |
| 	"github.com/theupdateframework/notary"
 | |
| 	store "github.com/theupdateframework/notary/storage"
 | |
| 	"github.com/theupdateframework/notary/tuf/data"
 | |
| 	"github.com/theupdateframework/notary/tuf/utils"
 | |
| )
 | |
| 
 | |
| type keyInfoMap map[string]KeyInfo
 | |
| 
 | |
| type cachedKey struct {
 | |
| 	role data.RoleName
 | |
| 	key  data.PrivateKey
 | |
| }
 | |
| 
 | |
| // GenericKeyStore is a wrapper for Storage instances that provides
 | |
| // translation between the []byte form and Public/PrivateKey objects
 | |
| type GenericKeyStore struct {
 | |
| 	store Storage
 | |
| 	sync.Mutex
 | |
| 	notary.PassRetriever
 | |
| 	cachedKeys map[string]*cachedKey
 | |
| 	keyInfoMap
 | |
| }
 | |
| 
 | |
| // NewKeyFileStore returns a new KeyFileStore creating a private directory to
 | |
| // hold the keys.
 | |
| func NewKeyFileStore(baseDir string, p notary.PassRetriever) (*GenericKeyStore, error) {
 | |
| 	fileStore, err := store.NewPrivateKeyFileStorage(baseDir, notary.KeyExtension)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	return NewGenericKeyStore(fileStore, p), nil
 | |
| }
 | |
| 
 | |
| // NewKeyMemoryStore returns a new KeyMemoryStore which holds keys in memory
 | |
| func NewKeyMemoryStore(p notary.PassRetriever) *GenericKeyStore {
 | |
| 	memStore := store.NewMemoryStore(nil)
 | |
| 	return NewGenericKeyStore(memStore, p)
 | |
| }
 | |
| 
 | |
| // NewGenericKeyStore creates a GenericKeyStore wrapping the provided
 | |
| // Storage instance, using the PassRetriever to enc/decrypt keys
 | |
| func NewGenericKeyStore(s Storage, p notary.PassRetriever) *GenericKeyStore {
 | |
| 	ks := GenericKeyStore{
 | |
| 		store:         s,
 | |
| 		PassRetriever: p,
 | |
| 		cachedKeys:    make(map[string]*cachedKey),
 | |
| 		keyInfoMap:    make(keyInfoMap),
 | |
| 	}
 | |
| 	ks.loadKeyInfo()
 | |
| 	return &ks
 | |
| }
 | |
| 
 | |
| func generateKeyInfoMap(s Storage) map[string]KeyInfo {
 | |
| 	keyInfoMap := make(map[string]KeyInfo)
 | |
| 	for _, keyPath := range s.ListFiles() {
 | |
| 		d, err := s.Get(keyPath)
 | |
| 		if err != nil {
 | |
| 			logrus.Error(err)
 | |
| 			continue
 | |
| 		}
 | |
| 		keyID, keyInfo, err := KeyInfoFromPEM(d, keyPath)
 | |
| 		if err != nil {
 | |
| 			logrus.Error(err)
 | |
| 			continue
 | |
| 		}
 | |
| 		keyInfoMap[keyID] = keyInfo
 | |
| 	}
 | |
| 	return keyInfoMap
 | |
| }
 | |
| 
 | |
| func (s *GenericKeyStore) loadKeyInfo() {
 | |
| 	s.keyInfoMap = generateKeyInfoMap(s.store)
 | |
| }
 | |
| 
 | |
| // GetKeyInfo returns the corresponding gun and role key info for a keyID
 | |
| func (s *GenericKeyStore) GetKeyInfo(keyID string) (KeyInfo, error) {
 | |
| 	if info, ok := s.keyInfoMap[keyID]; ok {
 | |
| 		return info, nil
 | |
| 	}
 | |
| 	return KeyInfo{}, fmt.Errorf("Could not find info for keyID %s", keyID)
 | |
| }
 | |
| 
 | |
| // AddKey stores the contents of a PEM-encoded private key as a PEM block
 | |
| func (s *GenericKeyStore) AddKey(keyInfo KeyInfo, privKey data.PrivateKey) error {
 | |
| 	var (
 | |
| 		chosenPassphrase string
 | |
| 		giveup           bool
 | |
| 		err              error
 | |
| 		pemPrivKey       []byte
 | |
| 	)
 | |
| 	s.Lock()
 | |
| 	defer s.Unlock()
 | |
| 	if keyInfo.Role == data.CanonicalRootRole || data.IsDelegation(keyInfo.Role) || !data.ValidRole(keyInfo.Role) {
 | |
| 		keyInfo.Gun = ""
 | |
| 	}
 | |
| 	keyID := privKey.ID()
 | |
| 	for attempts := 0; ; attempts++ {
 | |
| 		chosenPassphrase, giveup, err = s.PassRetriever(keyID, keyInfo.Role.String(), true, attempts)
 | |
| 		if err == nil {
 | |
| 			break
 | |
| 		}
 | |
| 		if giveup || attempts > 10 {
 | |
| 			return ErrAttemptsExceeded{}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	pemPrivKey, err = utils.ConvertPrivateKeyToPKCS8(privKey, keyInfo.Role, keyInfo.Gun, chosenPassphrase)
 | |
| 
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	s.cachedKeys[keyID] = &cachedKey{role: keyInfo.Role, key: privKey}
 | |
| 	err = s.store.Set(keyID, pemPrivKey)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	s.keyInfoMap[privKey.ID()] = keyInfo
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // GetKey returns the PrivateKey given a KeyID
 | |
| func (s *GenericKeyStore) GetKey(keyID string) (data.PrivateKey, data.RoleName, error) {
 | |
| 	s.Lock()
 | |
| 	defer s.Unlock()
 | |
| 
 | |
| 	cachedKeyEntry, ok := s.cachedKeys[keyID]
 | |
| 	if ok {
 | |
| 		return cachedKeyEntry.key, cachedKeyEntry.role, nil
 | |
| 	}
 | |
| 
 | |
| 	role, err := getKeyRole(s.store, keyID)
 | |
| 	if err != nil {
 | |
| 		return nil, "", err
 | |
| 	}
 | |
| 
 | |
| 	keyBytes, err := s.store.Get(keyID)
 | |
| 	if err != nil {
 | |
| 		return nil, "", err
 | |
| 	}
 | |
| 
 | |
| 	// See if the key is encrypted. If its encrypted we'll fail to parse the private key
 | |
| 	privKey, err := utils.ParsePEMPrivateKey(keyBytes, "")
 | |
| 	if err != nil {
 | |
| 		privKey, _, err = GetPasswdDecryptBytes(s.PassRetriever, keyBytes, keyID, string(role))
 | |
| 		if err != nil {
 | |
| 			return nil, "", err
 | |
| 		}
 | |
| 	}
 | |
| 	s.cachedKeys[keyID] = &cachedKey{role: role, key: privKey}
 | |
| 	return privKey, role, nil
 | |
| }
 | |
| 
 | |
| // ListKeys returns a list of unique PublicKeys present on the KeyFileStore, by returning a copy of the keyInfoMap
 | |
| func (s *GenericKeyStore) ListKeys() map[string]KeyInfo {
 | |
| 	return copyKeyInfoMap(s.keyInfoMap)
 | |
| }
 | |
| 
 | |
| // RemoveKey removes the key from the keyfilestore
 | |
| func (s *GenericKeyStore) RemoveKey(keyID string) error {
 | |
| 	s.Lock()
 | |
| 	defer s.Unlock()
 | |
| 	delete(s.cachedKeys, keyID)
 | |
| 
 | |
| 	err := s.store.Remove(keyID)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	delete(s.keyInfoMap, keyID)
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // Name returns a user friendly name for the location this store
 | |
| // keeps its data
 | |
| func (s *GenericKeyStore) Name() string {
 | |
| 	return s.store.Location()
 | |
| }
 | |
| 
 | |
| // copyKeyInfoMap returns a deep copy of the passed-in keyInfoMap
 | |
| func copyKeyInfoMap(keyInfoMap map[string]KeyInfo) map[string]KeyInfo {
 | |
| 	copyMap := make(map[string]KeyInfo)
 | |
| 	for keyID, keyInfo := range keyInfoMap {
 | |
| 		copyMap[keyID] = KeyInfo{Role: keyInfo.Role, Gun: keyInfo.Gun}
 | |
| 	}
 | |
| 	return copyMap
 | |
| }
 | |
| 
 | |
| // KeyInfoFromPEM attempts to get a keyID and KeyInfo from the filename and PEM bytes of a key
 | |
| func KeyInfoFromPEM(pemBytes []byte, filename string) (string, KeyInfo, error) {
 | |
| 	var keyID string
 | |
| 	keyID = filepath.Base(filename)
 | |
| 	role, gun, err := utils.ExtractPrivateKeyAttributes(pemBytes)
 | |
| 	if err != nil {
 | |
| 		return "", KeyInfo{}, err
 | |
| 	}
 | |
| 	return keyID, KeyInfo{Gun: gun, Role: role}, nil
 | |
| }
 | |
| 
 | |
| // getKeyRole finds the role for the given keyID. It attempts to look
 | |
| // both in the newer format PEM headers, and also in the legacy filename
 | |
| // format. It returns: the role, and an error
 | |
| func getKeyRole(s Storage, keyID string) (data.RoleName, error) {
 | |
| 	name := strings.TrimSpace(strings.TrimSuffix(filepath.Base(keyID), filepath.Ext(keyID)))
 | |
| 
 | |
| 	for _, file := range s.ListFiles() {
 | |
| 		filename := filepath.Base(file)
 | |
| 		if strings.HasPrefix(filename, name) {
 | |
| 			d, err := s.Get(file)
 | |
| 			if err != nil {
 | |
| 				return "", err
 | |
| 			}
 | |
| 
 | |
| 			role, _, err := utils.ExtractPrivateKeyAttributes(d)
 | |
| 			if err != nil {
 | |
| 				return "", err
 | |
| 			}
 | |
| 			return role, nil
 | |
| 		}
 | |
| 	}
 | |
| 	return "", ErrKeyNotFound{KeyID: keyID}
 | |
| }
 | |
| 
 | |
| // GetPasswdDecryptBytes gets the password to decrypt the given pem bytes.
 | |
| // Returns the password and private key
 | |
| func GetPasswdDecryptBytes(passphraseRetriever notary.PassRetriever, pemBytes []byte, name, alias string) (data.PrivateKey, string, error) {
 | |
| 	var (
 | |
| 		passwd  string
 | |
| 		privKey data.PrivateKey
 | |
| 	)
 | |
| 	for attempts := 0; ; attempts++ {
 | |
| 		var (
 | |
| 			giveup bool
 | |
| 			err    error
 | |
| 		)
 | |
| 		if attempts > 10 {
 | |
| 			return nil, "", ErrAttemptsExceeded{}
 | |
| 		}
 | |
| 		passwd, giveup, err = passphraseRetriever(name, alias, false, attempts)
 | |
| 		// Check if the passphrase retriever got an error or if it is telling us to give up
 | |
| 		if giveup || err != nil {
 | |
| 			return nil, "", ErrPasswordInvalid{}
 | |
| 		}
 | |
| 
 | |
| 		// Try to convert PEM encoded bytes back to a PrivateKey using the passphrase
 | |
| 		privKey, err = utils.ParsePEMPrivateKey(pemBytes, passwd)
 | |
| 		if err == nil {
 | |
| 			// We managed to parse the PrivateKey. We've succeeded!
 | |
| 			break
 | |
| 		}
 | |
| 	}
 | |
| 	return privKey, passwd, nil
 | |
| }
 |