def on_encrypt(self, encryption_materials):
        # type: (EncryptionMaterials) -> EncryptionMaterials
        trace_info = MasterKeyInfo(provider_id=KEY_NAMESPACE, key_info=self._key_id)
        new_materials = encryption_materials
        try:
            if new_materials.data_encryption_key is None:
                plaintext_key, encrypted_key = _do_aws_kms_generate_data_key(
                    client_supplier=self._client_supplier,
                    key_name=self._key_id,
                    encryption_context=new_materials.encryption_context,
                    algorithm=new_materials.algorithm,
                    grant_tokens=self._grant_tokens,
                )
                new_materials = new_materials.with_data_encryption_key(
                    data_encryption_key=plaintext_key,
                    keyring_trace=KeyringTrace(wrapping_key=trace_info, flags=_GENERATE_FLAGS),
                )
            else:
                encrypted_key = _do_aws_kms_encrypt(
                    client_supplier=self._client_supplier,
                    key_name=self._key_id,
                    plaintext_data_key=new_materials.data_encryption_key,
                    encryption_context=new_materials.encryption_context,
                    grant_tokens=self._grant_tokens,
                )
        except Exception:  # pylint: disable=broad-except
            # We intentionally WANT to catch all exceptions here
            message = "Unable to generate or encrypt data key using {}".format(trace_info)
            _LOGGER.exception(message)
            raise EncryptKeyError(message)

        return new_materials.with_encrypted_data_key(
            encrypted_data_key=encrypted_key, keyring_trace=KeyringTrace(wrapping_key=trace_info, flags=_ENCRYPT_FLAGS)
        )
def _try_aws_kms_decrypt(client_supplier, decryption_materials, grant_tokens, encrypted_data_key):
    # type: (ClientSupplierType, DecryptionMaterials, Iterable[str], EncryptedDataKey) -> DecryptionMaterials
    """Attempt to call ``kms:Decrypt`` and return the resulting plaintext data key.

    Any errors encountered are caught and logged.

    .. versionadded:: 1.5.0

    """
    try:
        plaintext_key = _do_aws_kms_decrypt(
            client_supplier=client_supplier,
            key_name=encrypted_data_key.key_provider.key_info.decode("utf-8"),
            encrypted_data_key=encrypted_data_key,
            encryption_context=decryption_materials.encryption_context,
            grant_tokens=grant_tokens,
        )
    except Exception:  # pylint: disable=broad-except
        # We intentionally WANT to catch all exceptions here
        _LOGGER.exception("Unable to decrypt encrypted data key from %s", encrypted_data_key.key_provider)
        return decryption_materials

    return decryption_materials.with_data_encryption_key(
        data_encryption_key=plaintext_key,
        keyring_trace=KeyringTrace(wrapping_key=encrypted_data_key.key_provider, flags=_DECRYPT_FLAGS),
    )
def _generate_data_key(
        encryption_materials,  # type: EncryptionMaterials
        key_provider,  # type: MasterKeyInfo
):
    # type: (...) -> EncryptionMaterials
    """Generates plaintext data key for the keyring.

    :param EncryptionMaterials encryption_materials: Encryption materials for the keyring to modify.
    :param MasterKeyInfo key_provider: Information about the key in the keyring.
    :rtype: EncryptionMaterials
    :returns: Encryption materials containing a data encryption key
    """
    # Check if encryption materials contain data encryption key
    if encryption_materials.data_encryption_key is not None:
        raise TypeError("Data encryption key already exists.")

    # Generate data key
    try:
        plaintext_data_key = os.urandom(
            encryption_materials.algorithm.kdf_input_len)
    except Exception:  # pylint: disable=broad-except
        error_message = "Unable to generate data encryption key."
        _LOGGER.exception(error_message)
        raise GenerateKeyError("Unable to generate data encryption key.")

    # Create a keyring trace
    keyring_trace = KeyringTrace(wrapping_key=key_provider,
                                 flags={KeyringTraceFlag.GENERATED_DATA_KEY})

    # plaintext_data_key to RawDataKey
    data_encryption_key = RawDataKey(key_provider=key_provider,
                                     data_key=plaintext_data_key)

    return encryption_materials.with_data_encryption_key(
        data_encryption_key=data_encryption_key, keyring_trace=keyring_trace)
def get_encryption_materials_with_encrypted_data_key():
    return EncryptionMaterials(
        algorithm=AlgorithmSuite.AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384,
        data_encryption_key=RawDataKey(
            key_provider=MasterKeyInfo(provider_id=_PROVIDER_ID,
                                       key_info=_EXISTING_KEY_ID),
            data_key=
            b'*!\xa1"^-(\xf3\x105\x05i@B\xc2\xa2\xb7\xdd\xd5\xd5\xa9\xddm\xfae\xa8\\$\xf9d\x1e(',
        ),
        encrypted_data_keys=[
            EncryptedDataKey(
                key_provider=MasterKeyInfo(provider_id=_PROVIDER_ID,
                                           key_info=_EXISTING_KEY_ID),
                encrypted_data_key=
                b"\xde^\x97\x7f\x84\xe9\x9e\x98\xd0\xe2\xf8\xd5\xcb\xe9\x7f.}\x87\x16,\x11n#\xc8p"
                b"\xdb\xbf\x94\x86*Q\x06\xd2\xf5\xdah\x08\xa4p\x81\xf7\xf4G\x07FzE\xde",
            )
        ],
        encryption_context=_ENCRYPTION_CONTEXT,
        signing_key=_SIGNING_KEY,
        keyring_trace=[
            KeyringTrace(
                wrapping_key=MasterKeyInfo(provider_id=_PROVIDER_ID,
                                           key_info=_EXISTING_KEY_ID),
                flags={
                    KeyringTraceFlag.GENERATED_DATA_KEY,
                    KeyringTraceFlag.ENCRYPTED_DATA_KEY
                },
            )
        ],
    )
def sample_encryption_materials():
    return [
        EncryptionMaterials(
            algorithm=Algorithm.AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384,
            encryption_context=_ENCRYPTION_CONTEXT,
            signing_key=_SIGNING_KEY,
        ),
        EncryptionMaterials(
            algorithm=Algorithm.AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384,
            data_encryption_key=RawDataKey(
                key_provider=MasterKeyInfo(provider_id=_PROVIDER_ID,
                                           key_info=_KEY_ID),
                data_key=
                b'*!\xa1"^-(\xf3\x105\x05i@B\xc2\xa2\xb7\xdd\xd5\xd5\xa9\xddm\xfae\xa8\\$\xf9d\x1e(',
            ),
            encryption_context=_ENCRYPTION_CONTEXT,
            signing_key=_SIGNING_KEY,
            keyring_trace=[
                KeyringTrace(
                    wrapping_key=MasterKeyInfo(provider_id=_PROVIDER_ID,
                                               key_info=_KEY_ID),
                    flags={KeyringTraceFlag.GENERATED_DATA_KEY},
                )
            ],
        ),
    ]
    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 encrypted_data_keys: List of encrypted data keys.
        :type: List[EncryptedDataKey]
        :returns: Decryption materials that MAY include a plaintext data key
        :rtype: DecryptionMaterials
        """
        new_materials = decryption_materials

        if new_materials.data_encryption_key is not None:
            return new_materials

        if self._private_wrapping_key is None:
            return new_materials

        # Decrypt data key
        for key in encrypted_data_keys:
            if key.key_provider != self._key_provider:
                continue

            # Wrapped EncryptedDataKey to deserialized EncryptedData
            encrypted_wrapped_key = deserialize_wrapped_key(
                wrapping_algorithm=self._wrapping_algorithm,
                wrapping_key_id=self.key_name,
                wrapped_encrypted_key=key)
            try:
                plaintext_data_key = self._private_wrapping_key.decrypt(
                    ciphertext=encrypted_wrapped_key.ciphertext,
                    padding=self._wrapping_algorithm.padding)
            except Exception:  # pylint: disable=broad-except
                error_message = "Raw RSA Keyring unable to decrypt data key"
                _LOGGER.exception(error_message)
                # The Raw RSA keyring MUST evaluate every encrypted data key
                # until it either succeeds or runs out of encrypted data keys.
                continue

            # Create a keyring trace
            keyring_trace = KeyringTrace(
                wrapping_key=self._key_provider,
                flags={KeyringTraceFlag.DECRYPTED_DATA_KEY})

            # Update decryption materials
            data_encryption_key = RawDataKey(key_provider=self._key_provider,
                                             data_key=plaintext_data_key)

            return new_materials.with_data_encryption_key(
                data_encryption_key=data_encryption_key,
                keyring_trace=keyring_trace)

        return new_materials
    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: Encryption materials containing data key and encrypted data key
        :rtype: EncryptionMaterials
        """
        new_materials = encryption_materials

        if new_materials.data_encryption_key is None:
            new_materials = _generate_data_key(
                encryption_materials=new_materials,
                key_provider=self._key_provider)

        if self._public_wrapping_key is None:
            # This should be impossible, but just in case, give a useful error message.
            raise EncryptKeyError(
                "Raw RSA keyring unable to encrypt data key: no public key available"
            )

        try:
            # Encrypt data key
            encrypted_wrapped_key = EncryptedData(
                iv=None,
                ciphertext=self._public_wrapping_key.encrypt(
                    plaintext=new_materials.data_encryption_key.data_key,
                    padding=self._wrapping_algorithm.padding,
                ),
                tag=None,
            )

            # EncryptedData to EncryptedDataKey
            encrypted_data_key = serialize_wrapped_key(
                key_provider=self._key_provider,
                wrapping_algorithm=self._wrapping_algorithm,
                wrapping_key_id=self.key_name,
                encrypted_wrapped_key=encrypted_wrapped_key,
            )
        except Exception:  # pylint: disable=broad-except
            error_message = "Raw RSA keyring unable to encrypt data key"
            _LOGGER.exception(error_message)
            raise EncryptKeyError(error_message)

        # Update Keyring Trace
        keyring_trace = KeyringTrace(
            wrapping_key=self._key_provider,
            flags={KeyringTraceFlag.ENCRYPTED_DATA_KEY})

        # Add encrypted data key to encryption_materials
        return new_materials.with_encrypted_data_key(
            encrypted_data_key=encrypted_data_key, keyring_trace=keyring_trace)
 def on_encrypt(self, encryption_materials):
     # type: (EncryptionMaterials) -> EncryptionMaterials
     if encryption_materials.data_encryption_key is None:
         key_provider = MasterKeyInfo(provider_id=_PROVIDER_ID,
                                      key_info=_KEY_ID)
         data_encryption_key = RawDataKey(
             key_provider=key_provider,
             data_key=os.urandom(
                 encryption_materials.algorithm.kdf_input_len))
         encryption_materials = encryption_materials.with_data_encryption_key(
             data_encryption_key=data_encryption_key,
             keyring_trace=KeyringTrace(
                 wrapping_key=key_provider,
                 flags={KeyringTraceFlag.GENERATED_DATA_KEY}),
         )
     return encryption_materials
    def on_encrypt(self, encryption_materials):
        # type: (EncryptionMaterials) -> EncryptionMaterials
        """Generate a data key if not present and encrypt it using any available wrapping key

        :param EncryptionMaterials encryption_materials: Encryption materials for the keyring to modify
        :returns: Encryption materials containing data key and encrypted data key
        :rtype: EncryptionMaterials
        """
        new_materials = encryption_materials

        if new_materials.data_encryption_key is None:
            # Get encryption materials with a new data key.
            new_materials = _generate_data_key(
                encryption_materials=new_materials,
                key_provider=self._key_provider)

        try:
            # Encrypt data key
            encrypted_wrapped_key = self._wrapping_key_structure.encrypt(
                plaintext_data_key=new_materials.data_encryption_key.data_key,
                encryption_context=new_materials.encryption_context,
            )

            # EncryptedData to EncryptedDataKey
            encrypted_data_key = serialize_wrapped_key(
                key_provider=self._key_provider,
                wrapping_algorithm=self._wrapping_algorithm,
                wrapping_key_id=self.key_name,
                encrypted_wrapped_key=encrypted_wrapped_key,
            )
        except Exception:  # pylint: disable=broad-except
            error_message = "Raw AES keyring unable to encrypt data key"
            _LOGGER.exception(error_message)
            raise EncryptKeyError(error_message)

        # Update Keyring Trace
        keyring_trace = KeyringTrace(
            wrapping_key=self._key_provider,
            flags={
                KeyringTraceFlag.ENCRYPTED_DATA_KEY,
                KeyringTraceFlag.SIGNED_ENCRYPTION_CONTEXT
            },
        )

        return new_materials.with_encrypted_data_key(
            encrypted_data_key=encrypted_data_key, keyring_trace=keyring_trace)
def get_decryption_materials_with_data_encryption_key():
    return DecryptionMaterials(
        algorithm=AlgorithmSuite.AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384,
        data_encryption_key=RawDataKey(
            key_provider=MasterKeyInfo(provider_id=_PROVIDER_ID,
                                       key_info=_EXISTING_KEY_ID),
            data_key=
            b'*!\xa1"^-(\xf3\x105\x05i@B\xc2\xa2\xb7\xdd\xd5\xd5\xa9\xddm\xfae\xa8\\$\xf9d\x1e(',
        ),
        encryption_context=_ENCRYPTION_CONTEXT,
        verification_key=b"ex_verification_key",
        keyring_trace=[
            KeyringTrace(
                wrapping_key=MasterKeyInfo(provider_id=_PROVIDER_ID,
                                           key_info=_EXISTING_KEY_ID),
                flags={KeyringTraceFlag.DECRYPTED_DATA_KEY},
            )
        ],
    )
Example #11
0
_ENCRYPTION_MATERIALS_WITHOUT_DATA_KEY = EncryptionMaterials(
    algorithm=ALGORITHM, encryption_context=_ENCRYPTION_CONTEXT)

_ENCRYPTION_MATERIALS_WITH_DATA_KEY = EncryptionMaterials(
    algorithm=ALGORITHM,
    data_encryption_key=RawDataKey(
        key_provider=MasterKeyInfo(provider_id=_PROVIDER_ID, key_info=_KEY_ID),
        data_key=
        b'*!\xa1"^-(\xf3\x105\x05i@B\xc2\xa2\xb7\xdd\xd5\xd5\xa9\xddm\xfae\xa8\\$\xf9d\x1e(',
    ),
    encryption_context=_ENCRYPTION_CONTEXT,
    keyring_trace=[
        KeyringTrace(
            wrapping_key=MasterKeyInfo(provider_id=_PROVIDER_ID,
                                       key_info=_KEY_ID),
            flags={KeyringTraceFlag.GENERATED_DATA_KEY},
        )
    ],
)

_MULTI_KEYRING_WITH_GENERATOR_AND_CHILDREN = MultiKeyring(
    generator=RawAESKeyring(
        key_namespace=_PROVIDER_ID,
        key_name=_KEY_ID,
        wrapping_key=_WRAPPING_KEY_AES,
    ),
    children=[
        RawRSAKeyring(
            key_namespace=_PROVIDER_ID,
            key_name=_KEY_ID,
    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 the keyring to modify
        :param List[EncryptedDataKey] encrypted_data_keys: List of encrypted data keys
        :returns: Decryption materials that MAY include a plaintext data key
        :rtype: DecryptionMaterials
        """
        new_materials = decryption_materials

        if new_materials.data_encryption_key is not None:
            return new_materials

        # Decrypt data key
        expected_key_info_len = len(
            self._key_info_prefix) + self._wrapping_algorithm.algorithm.iv_len
        for key in encrypted_data_keys:

            if (key.key_provider.provider_id != self._key_provider.provider_id
                    or len(key.key_provider.key_info) != expected_key_info_len
                    or not key.key_provider.key_info.startswith(
                        self._key_info_prefix)):
                continue

            # Wrapped EncryptedDataKey to deserialized EncryptedData
            encrypted_wrapped_key = deserialize_wrapped_key(
                wrapping_algorithm=self._wrapping_algorithm,
                wrapping_key_id=self.key_name,
                wrapped_encrypted_key=key)

            # EncryptedData to raw key string
            try:
                plaintext_data_key = self._wrapping_key_structure.decrypt(
                    encrypted_wrapped_data_key=encrypted_wrapped_key,
                    encryption_context=new_materials.encryption_context,
                )

            except Exception:  # pylint: disable=broad-except
                # We intentionally WANT to catch all exceptions here
                error_message = "Raw AES Keyring unable to decrypt data key"
                _LOGGER.exception(error_message)
                # The Raw AES keyring MUST evaluate every encrypted data key
                # until it either succeeds or runs out of encrypted data keys.
                continue

            # Create a keyring trace
            keyring_trace = KeyringTrace(
                wrapping_key=self._key_provider,
                flags={
                    KeyringTraceFlag.DECRYPTED_DATA_KEY,
                    KeyringTraceFlag.VERIFIED_ENCRYPTION_CONTEXT
                },
            )

            # Update decryption materials
            data_encryption_key = RawDataKey(key_provider=self._key_provider,
                                             data_key=plaintext_data_key)

            return new_materials.with_data_encryption_key(
                data_encryption_key=data_encryption_key,
                keyring_trace=keyring_trace)

        return new_materials