mirror of
				https://github.com/minio/minio.git
				synced 2025-10-29 15:55:00 -04:00 
			
		
		
		
	add UpdateKey method to KMS interface (#7974)
				
					
				
			This commit adds a new method `UpdateKey` to the KMS interface. The purpose of `UpdateKey` is to re-wrap an encrypted data key (the key generated & encrypted with a master key by e.g. Vault). For example, consider Vault with a master key ID: `master-key-1` and an encrypted data key `E(dk)` for a particular object. The data key `dk` has been generated randomly when the object was created. Now, the KMS operator may "rotate" the master key `master-key-1`. However, the KMS cannot forget the "old" value of that master key since there is still an object that requires `dk`, and therefore, the `D(E(dk))`. With the `UpdateKey` method call MinIO can ask the KMS to decrypt `E(dk)` with the old key (internally) and re-encrypted `dk` with the new master key value: `E'(dk)`. However, this operation only works for the same master key ID. When rotating the data key (replacing it with a new one) then we perform a `UnsealKey` operation with the 1st master key ID and then a `GenerateKey` operation with the 2nd master key ID. This commit also updates the KMS documentation and removes the `encrypt` policy entry (we don't use `encrypt`) and add a policy entry for `rewarp`.
This commit is contained in:
		
							parent
							
								
									dfa8835720
								
							
						
					
					
						commit
						a6f4cf61f2
					
				| @ -62,6 +62,8 @@ var ( | ||||
| 
 | ||||
| 	errInvalidInternalIV            = Error{"The internal encryption IV is malformed"} | ||||
| 	errInvalidInternalSealAlgorithm = Error{"The internal seal algorithm is invalid and not supported"} | ||||
| 
 | ||||
| 	errMissingUpdatedKey = Error{"The key update returned no error but also no sealed key"} | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
|  | ||||
| @ -86,6 +86,19 @@ type KMS interface { | ||||
| 	// referenced by the keyID. The provided context must | ||||
| 	// match the context used to generate the sealed key. | ||||
| 	UnsealKey(keyID string, sealedKey []byte, context Context) (key [32]byte, err error) | ||||
| 
 | ||||
| 	// UpdateKey re-wraps the sealedKey if the master key, referenced by | ||||
| 	// `keyID`, has changed in the meantime. This usually happens when the | ||||
| 	// KMS operator performs a key-rotation operation of the master key. | ||||
| 	// UpdateKey fails if the provided sealedKey cannot be decrypted using | ||||
| 	// the master key referenced by keyID. | ||||
| 	// | ||||
| 	// UpdateKey makes no guarantees whatsoever about whether the returned | ||||
| 	// rotatedKey is actually different from the sealedKey. If nothing has | ||||
| 	// changed at the KMS or if the KMS does not support updating generated | ||||
| 	// keys this method may behave like a NOP and just return the sealedKey | ||||
| 	// itself. | ||||
| 	UpdateKey(keyID string, sealedKey []byte, context Context) (rotatedKey []byte, err error) | ||||
| } | ||||
| 
 | ||||
| type masterKeyKMS struct { | ||||
| @ -126,6 +139,13 @@ func (kms *masterKeyKMS) UnsealKey(keyID string, sealedKey []byte, ctx Context) | ||||
| 	return key, nil | ||||
| } | ||||
| 
 | ||||
| func (kms *masterKeyKMS) UpdateKey(keyID string, sealedKey []byte, ctx Context) ([]byte, error) { | ||||
| 	if _, err := kms.UnsealKey(keyID, sealedKey, ctx); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return sealedKey, nil // The master key cannot update data keys -> Do nothing. | ||||
| } | ||||
| 
 | ||||
| func (kms *masterKeyKMS) deriveKey(keyID string, context Context) (key [32]byte) { | ||||
| 	if context == nil { | ||||
| 		context = Context{} | ||||
|  | ||||
| @ -51,11 +51,20 @@ func TestMasterKeyKMS(t *testing.T) { | ||||
| 			t.Errorf("Test %d: KMS failed to unseal the generated key: %v", i, err) | ||||
| 		} | ||||
| 		if err == nil && test.ShouldFail { | ||||
| 			t.Errorf("Test %d: KMS unsealed the generated successfully but should have failed", i) | ||||
| 			t.Errorf("Test %d: KMS unsealed the generated key successfully but should have failed", i) | ||||
| 		} | ||||
| 		if !test.ShouldFail && !bytes.Equal(key[:], unsealedKey[:]) { | ||||
| 			t.Errorf("Test %d: The generated and unsealed key differ", i) | ||||
| 		} | ||||
| 
 | ||||
| 		rotatedKey, err := kms.UpdateKey(test.UnsealKeyID, sealedKey, test.UnsealContext) | ||||
| 		if err == nil && test.ShouldFail { | ||||
| 			t.Errorf("Test %d: KMS updated the generated key successfully but should have failed", i) | ||||
| 		} | ||||
| 		if !test.ShouldFail && !bytes.Equal(rotatedKey, sealedKey[:]) { | ||||
| 			t.Errorf("Test %d: The updated and sealed key differ", i) | ||||
| 		} | ||||
| 
 | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -250,3 +250,30 @@ func (v *vaultService) UnsealKey(keyID string, sealedKey []byte, ctx Context) (k | ||||
| 	copy(key[:], []byte(plainKey)) | ||||
| 	return key, nil | ||||
| } | ||||
| 
 | ||||
| // UpdateKey re-wraps the sealedKey if the master key referenced by the keyID | ||||
| // has been changed by the KMS operator - i.e. the master key has been rotated. | ||||
| // If the master key hasn't changed since the sealedKey has been created / updated | ||||
| // it may return the same sealedKey as rotatedKey. | ||||
| // | ||||
| // The context must be same context as the one provided while | ||||
| // generating the plaintext key / sealedKey. | ||||
| func (v *vaultService) UpdateKey(keyID string, sealedKey []byte, ctx Context) (rotatedKey []byte, err error) { | ||||
| 	var contextStream bytes.Buffer | ||||
| 	ctx.WriteTo(&contextStream) | ||||
| 
 | ||||
| 	payload := map[string]interface{}{ | ||||
| 		"ciphertext": string(sealedKey), | ||||
| 		"context":    base64.StdEncoding.EncodeToString(contextStream.Bytes()), | ||||
| 	} | ||||
| 	s, err := v.client.Logical().Write(fmt.Sprintf("/transit/rewrap/%s", keyID), payload) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	ciphertext, ok := s.Data["ciphertext"] | ||||
| 	if !ok { | ||||
| 		return nil, errMissingUpdatedKey | ||||
| 	} | ||||
| 	rotatedKey = ciphertext.([]byte) | ||||
| 	return rotatedKey, nil | ||||
| } | ||||
|  | ||||
| @ -141,8 +141,8 @@ path "transit/datakey/plaintext/my-minio-key" { | ||||
| path "transit/decrypt/my-minio-key" { | ||||
|   capabilities = [ "read", "update"] | ||||
| } | ||||
| path "transit/encrypt/my-minio-key" { | ||||
|   capabilities = [ "read", "update"] | ||||
| path "transit/rewrap/my-minio-key" { | ||||
|   capabilities = ["update"] | ||||
| } | ||||
| 
 | ||||
| EOF | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user