cryptoservices can abstract multiple keystores

Signed-off-by: David Lawrence <david.lawrence@docker.com> (github: endophage)
This commit is contained in:
David Lawrence 2015-10-30 11:04:37 -07:00
parent e5c388d470
commit f791c01974
6 changed files with 86 additions and 16 deletions

View File

@ -17,13 +17,13 @@ const (
// CryptoService implements Sign and Create, holding a specific GUN and keystore to
// operate on
type CryptoService struct {
gun string
keyStore trustmanager.KeyStore
gun string
keyStores []trustmanager.KeyStore
}
// NewCryptoService returns an instance of CryptoService
func NewCryptoService(gun string, keyStore trustmanager.KeyStore) *CryptoService {
return &CryptoService{gun: gun, keyStore: keyStore}
func NewCryptoService(gun string, keyStores ...trustmanager.KeyStore) *CryptoService {
return &CryptoService{gun: gun, keyStores: keyStores}
}
// Create is used to generate keys for targets, snapshots and timestamps
@ -53,30 +53,53 @@ func (ccs *CryptoService) Create(role, algorithm string) (data.PublicKey, error)
logrus.Debugf("generated new %s key for role: %s and keyID: %s", algorithm, role, privKey.ID())
// Store the private key into our keystore with the name being: /GUN/ID.key with an alias of role
err = ccs.keyStore.AddKey(filepath.Join(ccs.gun, privKey.ID()), role, privKey)
for _, ks := range ccs.keyStores {
err = ks.AddKey(filepath.Join(ccs.gun, privKey.ID()), role, privKey)
if err == nil {
return data.PublicKeyFromPrivate(privKey), nil
}
}
if err != nil {
return nil, fmt.Errorf("failed to add key to filestore: %v", err)
}
return data.PublicKeyFromPrivate(privKey), nil
return nil, fmt.Errorf("keystores would not accept new private keys for unknown reasons")
}
// GetPrivateKey returns a private key by ID
func (ccs *CryptoService) GetPrivateKey(keyID string) (data.PrivateKey, string, error) {
return ccs.keyStore.GetKey(keyID)
func (ccs *CryptoService) GetPrivateKey(keyID string) (k data.PrivateKey, id string, err error) {
for _, ks := range ccs.keyStores {
k, id, err = ks.GetKey(keyID)
if k == nil || err != nil {
continue
}
return
}
return // returns whatever the final values were
}
// GetKey returns a key by ID
func (ccs *CryptoService) GetKey(keyID string) data.PublicKey {
key, _, err := ccs.keyStore.GetKey(keyID)
if err != nil {
return nil
for _, ks := range ccs.keyStores {
k, _, err := ks.GetKey(keyID)
if k == nil || err != nil {
continue
}
return data.PublicKeyFromPrivate(k)
}
return data.PublicKeyFromPrivate(key)
return nil // returns whatever the final values were
}
// RemoveKey deletes a key by ID
func (ccs *CryptoService) RemoveKey(keyID string) error {
return ccs.keyStore.RemoveKey(keyID)
func (ccs *CryptoService) RemoveKey(keyID string) (err error) {
for _, ks := range ccs.keyStores {
e := ks.RemoveKey(keyID)
if e != nil {
err = e
}
}
return // returns last error if any
}
// Sign returns the signatures for the payload with a set of keyIDs. It ignores
@ -90,10 +113,10 @@ func (ccs *CryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signatur
// Try to get the key first without a GUN (in which case it's a root
// key). If that fails, try to get the key with the GUN (non-root
// key). If that fails, then we don't have the key.
privKey, _, err := ccs.keyStore.GetKey(keyName)
privKey, _, err := ccs.GetPrivateKey(keyName)
if err != nil {
keyName = filepath.Join(ccs.gun, keyid)
privKey, _, err = ccs.keyStore.GetKey(keyName)
privKey, _, err = ccs.GetPrivateKey(keyName)
if err != nil {
logrus.Debugf("error attempting to retrieve key ID: %s, %v", keyid, err)
return nil, err
@ -128,3 +151,16 @@ func (ccs *CryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signatur
return signatures, nil
}
// ListKeys returns a list of key IDs valid for the given role
func (ccs *CryptoService) ListKeys(role string) []string {
var res []string
for _, ks := range ccs.keyStores {
for k, r := range ks.ListKeys() {
if r == role {
res = append(res, k)
}
}
}
return res
}

View File

@ -22,6 +22,11 @@ type RSAHardwareCryptoService struct {
session pkcs11.SessionHandle
}
// ListKeys not implemented yet
func (s *RSAHardwareCryptoService) ListKeys(role string) []string {
return []string{}
}
// Create creates a key and returns its public components
func (s *RSAHardwareCryptoService) Create(role, algo string) (data.PublicKey, error) {
// For now generate random labels for keys

View File

@ -100,6 +100,11 @@ func (trust *NotarySigner) GetPrivateKey(keyid string) (data.PrivateKey, string,
return nil, "", errors.New("Private key access not permitted.")
}
// ListKeys not supported for NotarySigner
func (trust *NotarySigner) ListKeys(role string) []string {
return []string{}
}
// CheckHealth checks the health of one of the clients, since both clients run
// from the same GRPC server.
func (trust *NotarySigner) CheckHealth(timeout time.Duration) error {

View File

@ -32,6 +32,15 @@ func (e *Ed25519) RemoveKey(keyID string) error {
return nil
}
// ListKeys returns the list of keys IDs for the role
func (e *Ed25519) ListKeys(role string) []string {
keyIDs := make([]string, 0, len(e.keys))
for id := range e.keys {
keyIDs = append(keyIDs, id)
}
return keyIDs
}
// Sign generates an Ed25519 signature over the data
func (e *Ed25519) Sign(keyIDs []string, toSign []byte) ([]data.Signature, error) {
signatures := make([]data.Signature, 0, len(keyIDs))

View File

@ -31,6 +31,9 @@ type KeyService interface {
// RemoveKey deletes the specified key
RemoveKey(keyID string) error
// ListKeys returns a map of IDs to role
ListKeys(role string) []string
}
// CryptoService defines a unified Signing and Key Service as this

View File

@ -34,6 +34,10 @@ func (mts *FailingCryptoService) Create(_, _ string) (data.PublicKey, error) {
return mts.testKey, nil
}
func (mts *FailingCryptoService) ListKeys(role string) []string {
return []string{mts.testKey.ID()}
}
func (mts *FailingCryptoService) GetKey(keyID string) data.PublicKey {
if keyID == "testID" {
return mts.testKey
@ -72,6 +76,10 @@ func (mts *MockCryptoService) GetKey(keyID string) data.PublicKey {
return nil
}
func (mts *MockCryptoService) ListKeys(role string) []string {
return []string{mts.testKey.ID()}
}
func (mts *MockCryptoService) GetPrivateKey(keyID string) (data.PrivateKey, string, error) {
return nil, "", errors.New("Not implemented")
}
@ -103,6 +111,10 @@ func (mts *StrictMockCryptoService) GetKey(keyID string) data.PublicKey {
return nil
}
func (mts *StrictMockCryptoService) ListKeys(role string) []string {
return []string{mts.testKey.ID()}
}
// Test signing and ensure the expected signature is added
func TestBasicSign(t *testing.T) {
testKey, _ := pem.Decode([]byte(testKeyPEM1))