mirror of https://github.com/docker/docs.git
				
				
				
			cryptoservices can abstract multiple keystores
Signed-off-by: David Lawrence <david.lawrence@docker.com> (github: endophage)
This commit is contained in:
		
							parent
							
								
									e5c388d470
								
							
						
					
					
						commit
						f791c01974
					
				|  | @ -17,13 +17,13 @@ const ( | ||||||
| // CryptoService implements Sign and Create, holding a specific GUN and keystore to
 | // CryptoService implements Sign and Create, holding a specific GUN and keystore to
 | ||||||
| // operate on
 | // operate on
 | ||||||
| type CryptoService struct { | type CryptoService struct { | ||||||
| 	gun      string | 	gun       string | ||||||
| 	keyStore trustmanager.KeyStore | 	keyStores []trustmanager.KeyStore | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // NewCryptoService returns an instance of CryptoService
 | // NewCryptoService returns an instance of CryptoService
 | ||||||
| func NewCryptoService(gun string, keyStore trustmanager.KeyStore) *CryptoService { | func NewCryptoService(gun string, keyStores ...trustmanager.KeyStore) *CryptoService { | ||||||
| 	return &CryptoService{gun: gun, keyStore: keyStore} | 	return &CryptoService{gun: gun, keyStores: keyStores} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Create is used to generate keys for targets, snapshots and timestamps
 | // 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()) | 	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
 | 	// 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 { | 	if err != nil { | ||||||
| 		return nil, fmt.Errorf("failed to add key to filestore: %v", err) | 		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
 | // GetPrivateKey returns a private key by ID
 | ||||||
| func (ccs *CryptoService) GetPrivateKey(keyID string) (data.PrivateKey, string, error) { | func (ccs *CryptoService) GetPrivateKey(keyID string) (k data.PrivateKey, id string, err error) { | ||||||
| 	return ccs.keyStore.GetKey(keyID) | 	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
 | // GetKey returns a key by ID
 | ||||||
| func (ccs *CryptoService) GetKey(keyID string) data.PublicKey { | func (ccs *CryptoService) GetKey(keyID string) data.PublicKey { | ||||||
| 	key, _, err := ccs.keyStore.GetKey(keyID) | 	for _, ks := range ccs.keyStores { | ||||||
| 	if err != nil { | 		k, _, err := ks.GetKey(keyID) | ||||||
| 		return nil | 		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
 | // RemoveKey deletes a key by ID
 | ||||||
| func (ccs *CryptoService) RemoveKey(keyID string) error { | func (ccs *CryptoService) RemoveKey(keyID string) (err error) { | ||||||
| 	return ccs.keyStore.RemoveKey(keyID) | 	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
 | // 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
 | 		// 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, try to get the key with the GUN (non-root
 | ||||||
| 		// key).  If that fails, then we don't have the key.
 | 		// 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 { | 		if err != nil { | ||||||
| 			keyName = filepath.Join(ccs.gun, keyid) | 			keyName = filepath.Join(ccs.gun, keyid) | ||||||
| 			privKey, _, err = ccs.keyStore.GetKey(keyName) | 			privKey, _, err = ccs.GetPrivateKey(keyName) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				logrus.Debugf("error attempting to retrieve key ID: %s, %v", keyid, err) | 				logrus.Debugf("error attempting to retrieve key ID: %s, %v", keyid, err) | ||||||
| 				return nil, err | 				return nil, err | ||||||
|  | @ -128,3 +151,16 @@ func (ccs *CryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signatur | ||||||
| 
 | 
 | ||||||
| 	return signatures, nil | 	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 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -22,6 +22,11 @@ type RSAHardwareCryptoService struct { | ||||||
| 	session pkcs11.SessionHandle | 	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
 | // Create creates a key and returns its public components
 | ||||||
| func (s *RSAHardwareCryptoService) Create(role, algo string) (data.PublicKey, error) { | func (s *RSAHardwareCryptoService) Create(role, algo string) (data.PublicKey, error) { | ||||||
| 	// For now generate random labels for keys
 | 	// For now generate random labels for keys
 | ||||||
|  |  | ||||||
|  | @ -100,6 +100,11 @@ func (trust *NotarySigner) GetPrivateKey(keyid string) (data.PrivateKey, string, | ||||||
| 	return nil, "", errors.New("Private key access not permitted.") | 	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
 | // CheckHealth checks the health of one of the clients, since both clients run
 | ||||||
| // from the same GRPC server.
 | // from the same GRPC server.
 | ||||||
| func (trust *NotarySigner) CheckHealth(timeout time.Duration) error { | func (trust *NotarySigner) CheckHealth(timeout time.Duration) error { | ||||||
|  |  | ||||||
|  | @ -32,6 +32,15 @@ func (e *Ed25519) RemoveKey(keyID string) error { | ||||||
| 	return nil | 	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
 | // Sign generates an Ed25519 signature over the data
 | ||||||
| func (e *Ed25519) Sign(keyIDs []string, toSign []byte) ([]data.Signature, error) { | func (e *Ed25519) Sign(keyIDs []string, toSign []byte) ([]data.Signature, error) { | ||||||
| 	signatures := make([]data.Signature, 0, len(keyIDs)) | 	signatures := make([]data.Signature, 0, len(keyIDs)) | ||||||
|  |  | ||||||
|  | @ -31,6 +31,9 @@ type KeyService interface { | ||||||
| 
 | 
 | ||||||
| 	// RemoveKey deletes the specified key
 | 	// RemoveKey deletes the specified key
 | ||||||
| 	RemoveKey(keyID string) error | 	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
 | // CryptoService defines a unified Signing and Key Service as this
 | ||||||
|  |  | ||||||
|  | @ -34,6 +34,10 @@ func (mts *FailingCryptoService) Create(_, _ string) (data.PublicKey, error) { | ||||||
| 	return mts.testKey, nil | 	return mts.testKey, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (mts *FailingCryptoService) ListKeys(role string) []string { | ||||||
|  | 	return []string{mts.testKey.ID()} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (mts *FailingCryptoService) GetKey(keyID string) data.PublicKey { | func (mts *FailingCryptoService) GetKey(keyID string) data.PublicKey { | ||||||
| 	if keyID == "testID" { | 	if keyID == "testID" { | ||||||
| 		return mts.testKey | 		return mts.testKey | ||||||
|  | @ -72,6 +76,10 @@ func (mts *MockCryptoService) GetKey(keyID string) data.PublicKey { | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (mts *MockCryptoService) ListKeys(role string) []string { | ||||||
|  | 	return []string{mts.testKey.ID()} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (mts *MockCryptoService) GetPrivateKey(keyID string) (data.PrivateKey, string, error) { | func (mts *MockCryptoService) GetPrivateKey(keyID string) (data.PrivateKey, string, error) { | ||||||
| 	return nil, "", errors.New("Not implemented") | 	return nil, "", errors.New("Not implemented") | ||||||
| } | } | ||||||
|  | @ -103,6 +111,10 @@ func (mts *StrictMockCryptoService) GetKey(keyID string) data.PublicKey { | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (mts *StrictMockCryptoService) ListKeys(role string) []string { | ||||||
|  | 	return []string{mts.testKey.ID()} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // Test signing and ensure the expected signature is added
 | // Test signing and ensure the expected signature is added
 | ||||||
| func TestBasicSign(t *testing.T) { | func TestBasicSign(t *testing.T) { | ||||||
| 	testKey, _ := pem.Decode([]byte(testKeyPEM1)) | 	testKey, _ := pem.Decode([]byte(testKeyPEM1)) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue