def test_on_decrypt_every_keyring_called_when_data_encryption_key_not_added( mock_generator, mock_child_1, mock_child_2): mock_generator.on_decrypt.side_effect = ( lambda decryption_materials, encrypted_data_keys: get_decryption_materials_without_data_key()) mock_child_1.on_decrypt.return_value = get_decryption_materials_without_data_key( ) mock_child_2.on_decrypt.return_value = get_decryption_materials_without_data_key( ) test_multi_keyring = MultiKeyring(generator=mock_generator, children=[mock_child_1, mock_child_2]) test_multi_keyring.on_decrypt( decryption_materials=get_decryption_materials_without_data_key(), encrypted_data_keys=[]) for keyring in test_multi_keyring._decryption_keyrings: assert keyring.on_decrypt.called
def test_on_decrypt_when_data_encryption_key_given(mock_generator, mock_child_1, mock_child_2): test_multi_keyring = MultiKeyring(generator=mock_generator, children=[mock_child_1, mock_child_2]) initial_materials = get_decryption_materials_with_data_key() new_materials = test_multi_keyring.on_decrypt( decryption_materials=initial_materials, encrypted_data_keys=[]) assert new_materials is initial_materials for keyring in test_multi_keyring._decryption_keyrings: assert not keyring.on_decrypt.called
def test_no_keyring_called_after_data_encryption_key_added_when_data_encryption_key_not_given( mock_generator, mock_child_1, mock_child_2, mock_child_3): mock_generator.on_decrypt.side_effect = ( lambda decryption_materials, encrypted_data_keys: get_decryption_materials_without_data_key()) test_multi_keyring = MultiKeyring( generator=mock_generator, children=[mock_child_3, mock_child_1, mock_child_2]) initial_materials = get_decryption_materials_without_data_key() new_materials = test_multi_keyring.on_decrypt( decryption_materials=initial_materials, encrypted_data_keys=[]) assert new_materials is not initial_materials assert mock_generator.on_decrypt.called assert mock_child_3.on_decrypt.called assert not mock_child_1.called assert not mock_child_2.called
class AwsKmsKeyring(Keyring): """Keyring that uses AWS Key Management Service (KMS) Customer Master Keys (CMKs) to manage wrapping keys. Set ``generator_key_id`` to require that the keyring use that CMK to generate the data key. If you do not set ``generator_key_id``, the keyring will not generate a data key. Set ``key_ids`` to specify additional CMKs that the keyring will use to encrypt the data key. The keyring will attempt to use any CMKs identified by CMK ARN in either ``generator_key_id`` or ``key_ids`` on decrypt. You can identify CMKs by any `valid key ID`_ for the keyring to use on encrypt, but for the keyring to attempt to use them on decrypt you MUST specify the CMK ARN. If you specify ``is_discovery=True`` the keyring will be a KMS discovery keyring, doing nothing on encrypt and attempting to decrypt any AWS KMS-encrypted data key on decrypt. .. note:: You must either set ``is_discovery=True`` or provide key IDs. You can use the :class:`ClientSupplier` to customize behavior further, such as to provide different credentials for different regions or to restrict which regions are allowed. See the `AWS KMS Keyring specification`_ for more details. .. _AWS KMS Keyring specification: https://github.com/awslabs/aws-encryption-sdk-specification/blob/master/framework/kms-keyring.md .. _valid key ID: https://docs.aws.amazon.com/kms/latest/APIReference/API_GenerateDataKey.html#API_GenerateDataKey_RequestSyntax .. _discovery mode: https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/choose-keyring.html#kms-keyring-discovery .. versionadded:: 1.5.0 :param ClientSupplier client_supplier: Client supplier that provides AWS KMS clients (optional) :param bool is_discovery: Should this be a discovery keyring (optional) :param str generator_key_id: Key ID of AWS KMS CMK to use when generating data keys (optional) :param List[str] key_ids: Key IDs that will be used to encrypt and decrypt data keys (optional) :param List[str] grant_tokens: AWS KMS grant tokens to include in requests (optional) """ _client_supplier = attr.ib(default=attr.Factory(DefaultClientSupplier), validator=is_callable()) _is_discovery = attr.ib(default=False, validator=instance_of(bool)) _generator_key_id = attr.ib(default=None, validator=optional(instance_of(six.string_types))) _key_ids = attr.ib( default=attr.Factory(tuple), validator=(deep_iterable(member_validator=instance_of(six.string_types)), value_is_not_a_string), ) _grant_tokens = attr.ib( default=attr.Factory(tuple), validator=(deep_iterable(member_validator=instance_of(six.string_types)), value_is_not_a_string), ) def __attrs_post_init__(self): """Configure internal keyring.""" key_ids_provided = self._generator_key_id is not None or self._key_ids both = key_ids_provided and self._is_discovery neither = not key_ids_provided and not self._is_discovery if both: raise TypeError("is_discovery cannot be True if key IDs are provided") if neither: raise TypeError("is_discovery cannot be False if no key IDs are provided") if self._is_discovery: self._inner_keyring = _AwsKmsDiscoveryKeyring( client_supplier=self._client_supplier, grant_tokens=self._grant_tokens ) return if self._generator_key_id is None: generator_keyring = None else: generator_keyring = _AwsKmsSingleCmkKeyring( key_id=self._generator_key_id, client_supplier=self._client_supplier, grant_tokens=self._grant_tokens ) child_keyrings = [ _AwsKmsSingleCmkKeyring( key_id=key_id, client_supplier=self._client_supplier, grant_tokens=self._grant_tokens ) for key_id in self._key_ids ] self._inner_keyring = MultiKeyring(generator=generator_keyring, children=child_keyrings) def on_encrypt(self, encryption_materials): # type: (EncryptionMaterials) -> EncryptionMaterials """Generate a data key using generator keyring and encrypt it using any available wrapping key in any child keyring. :param EncryptionMaterials encryption_materials: Encryption materials for keyring to modify. :returns: Optionally modified encryption materials. :rtype: EncryptionMaterials :raises EncryptKeyError: if unable to encrypt data key. """ return self._inner_keyring.on_encrypt(encryption_materials=encryption_materials) def on_decrypt(self, decryption_materials, encrypted_data_keys): # type: (DecryptionMaterials, Iterable[EncryptedDataKey]) -> DecryptionMaterials """Attempt to decrypt the encrypted data keys. :param DecryptionMaterials decryption_materials: Decryption materials for keyring to modify. :param List[EncryptedDataKey] encrypted_data_keys: List of encrypted data keys. :returns: Optionally modified decryption materials. :rtype: DecryptionMaterials """ return self._inner_keyring.on_decrypt( decryption_materials=decryption_materials, encrypted_data_keys=encrypted_data_keys )