def test_decrypt_materials_uncommitting_alg_require_policy(
        patch_for_dcmm_decrypt):
    """Tests that a configuration which requires commitment does not allow decryption of un-committed messages."""
    mock_alg = MagicMock(__class__=Algorithm)
    mock_alg.is_committing.return_value = False
    mock_request = MagicMock(algorithm=mock_alg, encryption_context={})
    mock_request.commitment_policy = CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT

    cmm = DefaultCryptoMaterialsManager(master_key_provider=build_mkp())

    with pytest.raises(ActionNotAllowedError) as excinfo:
        cmm.decrypt_materials(request=mock_request)
    excinfo.match(
        "Configuration conflict. Cannot decrypt due to .* requiring only committed messages."
    )
Exemple #2
0
class ClassificationRequiringCryptoMaterialsManager(CryptoMaterialsManager):
    """Only allow requests when the encryption context contains a classification identifier."""

    def __init__(self, keyring):
        # type: (Keyring) -> None
        """Set up the inner cryptographic materials manager using the provided keyring.

        :param Keyring keyring: Keyring to use in the inner cryptographic materials manager
        """
        self._classification_field = "classification"
        self._classification_error = MissingClassificationError("Encryption context does not contain classification!")
        # Wrap the provided keyring in the default cryptographic materials manager (CMM).
        #
        # This is the same thing that the encrypt and decrypt APIs, as well as the caching CMM,
        # do if you provide a keyring instead of a CMM.
        self._cmm = DefaultCryptoMaterialsManager(keyring=keyring)

    def get_encryption_materials(self, request):
        # type: (EncryptionMaterialsRequest) -> EncryptionMaterials
        """Block any requests that do not contain a classification identifier in the encryption context."""
        if self._classification_field not in request.encryption_context:
            raise self._classification_error

        return self._cmm.get_encryption_materials(request)

    def decrypt_materials(self, request):
        # type: (DecryptionMaterialsRequest) -> DecryptionMaterials
        """Block any requests that do not contain a classification identifier in the encryption context."""
        if self._classification_field not in request.encryption_context:
            raise self._classification_error

        return self._cmm.decrypt_materials(request)
Exemple #3
0
class RequireApprovedAlgorithmSuitesCryptoMaterialsManager(
        CryptoMaterialsManager):
    """Only allow encryption requests for approved algorithm suites."""
    def __init__(self, keyring):
        # type: (Keyring) -> None
        """Set up the inner cryptographic materials manager using the provided keyring.

        :param Keyring keyring: Keyring to use in the inner cryptographic materials manager
        """
        self._allowed_algorithm_suites = {
            None,  # no algorithm suite in the request
            AlgorithmSuite.
            AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384,  # the default algorithm suite
            AlgorithmSuite.
            AES_256_GCM_IV12_TAG16_HKDF_SHA256,  # the recommended unsigned algorithm suite
        }
        # Wrap the provided keyring in the default cryptographic materials manager (CMM).
        #
        # This is the same thing that the encrypt and decrypt APIs, as well as the caching CMM,
        # do if you provide a keyring instead of a CMM.
        self._cmm = DefaultCryptoMaterialsManager(keyring=keyring)

    def get_encryption_materials(self, request):
        # type: (EncryptionMaterialsRequest) -> EncryptionMaterials
        """Block any requests that include an unapproved algorithm suite."""
        if request.algorithm not in self._allowed_algorithm_suites:
            raise UnapprovedAlgorithmSuite(
                "Unapproved algorithm suite requested!")

        return self._cmm.get_encryption_materials(request)

    def decrypt_materials(self, request):
        # type: (DecryptionMaterialsRequest) -> DecryptionMaterials
        """Be more permissive on decrypt and just pass through."""
        return self._cmm.decrypt_materials(request)
def test_decrypt_with_keyring_materials_do_not_match_request(
        kwargs, algorithm_suite):
    raw_aes256_keyring = ephemeral_raw_aes_keyring(
        WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING)

    encrypt_cmm = DefaultCryptoMaterialsManager(keyring=raw_aes256_keyring)
    decrypt_cmm = DefaultCryptoMaterialsManager(
        keyring=BrokenKeyring(inner_keyring=raw_aes256_keyring, **kwargs))

    encryption_materials_request = EncryptionMaterialsRequest(
        encryption_context={}, frame_length=1024, algorithm=algorithm_suite)
    encryption_materials = encrypt_cmm.get_encryption_materials(
        encryption_materials_request)

    decryption_materials_request = DecryptionMaterialsRequest(
        algorithm=encryption_materials.algorithm,
        encrypted_data_keys=encryption_materials.encrypted_data_keys,
        encryption_context=encryption_materials.encryption_context,
    )

    with pytest.raises(InvalidCryptographicMaterialsError) as excinfo:
        decrypt_cmm.decrypt_materials(decryption_materials_request)

    excinfo.match("Decryption materials do not match request!")
Exemple #5
0
class HalfSigningCryptoMaterialsManager(CryptoMaterialsManager):
    """
    Custom CMM that generates materials for an unsigned algorithm suite
    that includes the "aws-crypto-public-key" encryption context.

    THIS IS ONLY USED TO CREATE INVALID MESSAGES and should never be used in
    production! It is imitating what a malicious decryptor without encryption
    permissions might do, to attempt to forge an unsigned message from a decrypted
    signed message, and therefore this is an important case for ESDKs to reject.
    """

    wrapped_default_cmm = attr.ib(validator=attr.validators.instance_of(CryptoMaterialsManager))

    def __init__(self, master_key_provider):
        """
        Create a new CMM that wraps a new DefaultCryptoMaterialsManager
        based on the given master key provider.
        """
        self.wrapped_default_cmm = DefaultCryptoMaterialsManager(master_key_provider)

    def get_encryption_materials(self, request):
        """
        Generate half-signing materials by requesting signing materials
        from the wrapped default CMM, and then changing the algorithm suite
        and removing the signing key from teh result.
        """
        if request.algorithm == AlgorithmSuite.AES_256_GCM_HKDF_SHA512_COMMIT_KEY:
            signing_request = copy(request)
            signing_request.algorithm = AlgorithmSuite.AES_256_GCM_HKDF_SHA512_COMMIT_KEY_ECDSA_P384

            result = self.wrapped_default_cmm.get_encryption_materials(signing_request)
            result.algorithm = request.algorithm
            result.signing_key = None

            return result

        raise NotImplementedError(
            "The half-sign tampering method is only supported on the "
            "AES_256_GCM_HKDF_SHA512_COMMIT_KEY algorithm suite."
        )

    def decrypt_materials(self, request):
        """Thunks to the wrapped default CMM"""
        return self.wrapped_default_cmm.decrypt_materials(request)
def test_decrypt_materials_committing_alg(patch_for_dcmm_decrypt, policy):
    """Tests that all configurations of CommitmentPolicy are able to decrypt when the algorithm provides commitment."""
    mock_alg = MagicMock(__class__=Algorithm)
    mock_alg.is_committing.return_value = True
    mock_request = MagicMock(algorithm=mock_alg, encryption_context={})
    mock_request.commitment_policy = policy

    cmm = DefaultCryptoMaterialsManager(master_key_provider=build_mkp())

    test = cmm.decrypt_materials(request=mock_request)

    cmm.master_key_provider.decrypt_data_key_from_list.assert_called_once_with(
        encrypted_data_keys=mock_request.encrypted_data_keys,
        algorithm=mock_request.algorithm,
        encryption_context=mock_request.encryption_context,
    )
    cmm._load_verification_key_from_encryption_context.assert_called_once_with(
        algorithm=mock_request.algorithm,
        encryption_context=mock_request.encryption_context)
    assert test.data_key is cmm.master_key_provider.decrypt_data_key_from_list.return_value
    assert test.verification_key == patch_for_dcmm_decrypt