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
 | ||||
| // 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 | ||||
| } | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -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 { | ||||
|  |  | |||
|  | @ -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)) | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -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)) | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue