def test_wrapping_key_decrypt_symmetric(
    patch_default_backend,
    patch_serialization,
    patch_serialize_encryption_context,
    patch_derive_data_encryption_key,
    patch_decrypt,
):
    mock_wrapping_algorithm = MagicMock()
    test_wrapping_key = WrappingKey(
        wrapping_algorithm=mock_wrapping_algorithm,
        wrapping_key=sentinel.wrapping_key,
        wrapping_key_type=EncryptionKeyType.SYMMETRIC,
    )
    test = test_wrapping_key.decrypt(
        encrypted_wrapped_data_key=VALUES["ciphertext"],
        encryption_context=sentinel.encryption_context)
    patch_serialize_encryption_context.assert_called_once_with(
        encryption_context=sentinel.encryption_context)
    patch_decrypt.assert_called_once_with(
        algorithm=mock_wrapping_algorithm.algorithm,
        key=patch_derive_data_encryption_key.return_value,
        encrypted_data=VALUES["ciphertext"],
        associated_data=patch_serialize_encryption_context.return_value,
    )
    assert test is patch_decrypt.return_value
Esempio n. 2
0
def test_wrapping_key_decrypt_private(
        patch_default_backend,
        patch_serialization,
        patch_serialize_encryption_context,
        patch_decrypt
):
    private_key, _ = mock_wrapping_rsa_keys()
    patch_serialization.load_pem_private_key.return_value = private_key
    private_key.decrypt.return_value = sentinel.plaintext_data
    mock_wrapping_algorithm = MagicMock(encryption_type=EncryptionType.ASYMMETRIC)
    test_wrapping_key = WrappingKey(
        wrapping_algorithm=mock_wrapping_algorithm,
        wrapping_key=sentinel.wrapping_key,
        wrapping_key_type=EncryptionKeyType.PRIVATE
    )
    test = test_wrapping_key.decrypt(
        encrypted_wrapped_data_key=mock_encrypted_data(),
        encryption_context=sentinel.encryption_context
    )
    private_key.decrypt.assert_called_once_with(
        ciphertext=VALUES['ciphertext'],
        padding=mock_wrapping_algorithm.padding
    )
    assert not patch_serialize_encryption_context.called
    assert not patch_decrypt.called
    assert test is sentinel.plaintext_data
def test_wrapping_key_decrypt_public(patch_default_backend,
                                     patch_serialization,
                                     patch_serialize_encryption_context,
                                     patch_decrypt):
    private_key, _ = mock_wrapping_rsa_keys()
    patch_serialization.load_pem_private_key.return_value = private_key
    private_key.decrypt.return_value = sentinel.plaintext_data
    mock_wrapping_algorithm = MagicMock(
        encryption_type=EncryptionType.ASYMMETRIC)
    test_wrapping_key = WrappingKey(wrapping_algorithm=mock_wrapping_algorithm,
                                    wrapping_key=sentinel.wrapping_key,
                                    wrapping_key_type=EncryptionKeyType.PUBLIC)
    with pytest.raises(IncorrectMasterKeyError) as excinfo:
        test_wrapping_key.decrypt(
            encrypted_wrapped_data_key=mock_encrypted_data(),
            encryption_context=sentinel.encryption_context)

    excinfo.match(r'Public key cannot decrypt')
Esempio n. 4
0
class RawAESKeyring(Keyring):
    """Generate an instance of Raw AES Keyring which encrypts using AES-GCM algorithm using wrapping key provided as a
    byte array

    .. versionadded:: 1.5.0

    :param str key_namespace: String defining the keyring.
    :param bytes key_name: Key ID
    :param bytes wrapping_key: Encryption key with which to wrap plaintext data key.

    .. note::

        Only one wrapping key can be specified in a Raw AES Keyring
    """

    key_namespace = attr.ib(validator=instance_of(six.string_types))
    key_name = attr.ib(validator=instance_of(six.binary_type))
    _wrapping_key = attr.ib(repr=False, validator=instance_of(six.binary_type))

    def __attrs_post_init__(self):
        # type: () -> None
        """Prepares initial values not handled by attrs."""
        key_size_to_wrapping_algorithm = {
            wrapper.algorithm.kdf_input_len: wrapper
            for wrapper in (
                WrappingAlgorithm.AES_128_GCM_IV12_TAG16_NO_PADDING,
                WrappingAlgorithm.AES_192_GCM_IV12_TAG16_NO_PADDING,
                WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING,
            )
        }

        try:
            self._wrapping_algorithm = key_size_to_wrapping_algorithm[len(
                self._wrapping_key)]
        except KeyError:
            raise ValueError(
                "Invalid wrapping key length. Must be one of {} bytes.".format(
                    sorted(key_size_to_wrapping_algorithm.keys())))

        self._key_provider = MasterKeyInfo(provider_id=self.key_namespace,
                                           key_info=self.key_name)

        self._wrapping_key_structure = WrappingKey(
            wrapping_algorithm=self._wrapping_algorithm,
            wrapping_key=self._wrapping_key,
            wrapping_key_type=EncryptionKeyType.SYMMETRIC,
        )

        self._key_info_prefix = self._get_key_info_prefix(
            key_namespace=self.key_namespace,
            key_name=self.key_name,
            wrapping_key=self._wrapping_key_structure)

    @staticmethod
    def _get_key_info_prefix(key_namespace, key_name, wrapping_key):
        # type: (str, bytes, WrappingKey) -> six.binary_type
        """Helper function to get key info prefix

        :param str key_namespace: String defining the keyring.
        :param bytes key_name: Key ID
        :param WrappingKey wrapping_key: Encryption key with which to wrap plaintext data key.
        :return: Serialized key_info prefix
        :rtype: bytes
        """
        key_info_prefix = serialize_raw_master_key_prefix(
            RawMasterKey(provider_id=key_namespace,
                         key_id=key_name,
                         wrapping_key=wrapping_key))
        return key_info_prefix

    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 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