예제 #1
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)
예제 #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)
예제 #3
0
    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 test_get_encryption_materials_uncommitting_algorithm_policy_forbid(
        patch_for_dcmm_encrypt):
    """Tests that a Default Crypto Materials Manager request with policy FORBID_ENCRYPT_ALLOW_DECRYPT can
    successfully encrypt using an algorithm that does not provide commitment."""
    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.FORBID_ENCRYPT_ALLOW_DECRYPT

    cmm = DefaultCryptoMaterialsManager(master_key_provider=build_mkp())

    test = cmm.get_encryption_materials(request=mock_request)
    assert test.algorithm is mock_request.algorithm
def test_get_encryption_materials_committing_algorithm_require_encrypt_require_decrypt(
        patch_for_dcmm_encrypt):
    """Tests that a Default Crypto Materials Manager request with policy REQUIRE_ENCRYPT_REQUIRE_DECRYPT can
    successfully encrypt using an algorithm that 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 = CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT

    cmm = DefaultCryptoMaterialsManager(master_key_provider=build_mkp())

    test = cmm.get_encryption_materials(request=mock_request)
    assert test.algorithm is mock_request.algorithm
    def __attrs_post_init__(self):
        """Normalize inputs to crypto material manager."""
        options_provided = [option is not None for option in (self.materials_manager, self.keyring, self.key_provider)]
        provided_count = len([is_set for is_set in options_provided if is_set])

        if provided_count != 1:
            raise TypeError("Exactly one of 'materials_manager', 'keyring', or 'key_provider' must be provided")

        if self.materials_manager is None:
            if self.key_provider is not None:
                self.materials_manager = DefaultCryptoMaterialsManager(master_key_provider=self.key_provider)
            else:
                self.materials_manager = DefaultCryptoMaterialsManager(keyring=self.keyring)
def test_encrypt_with_keyring_materials_incomplete(algorithm_suite):
    raw_aes256_keyring = ephemeral_raw_aes_keyring(
        WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING)

    encrypt_cmm = DefaultCryptoMaterialsManager(
        keyring=NoEncryptedDataKeysKeyring(inner_keyring=raw_aes256_keyring))

    encryption_materials_request = EncryptionMaterialsRequest(
        encryption_context={}, frame_length=1024, algorithm=algorithm_suite)

    with pytest.raises(InvalidCryptographicMaterialsError) as excinfo:
        encrypt_cmm.get_encryption_materials(encryption_materials_request)

    excinfo.match("Encryption materials are incomplete!")
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."
    )
def test_encrypt_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=BrokenKeyring(inner_keyring=raw_aes256_keyring, **kwargs))

    encryption_materials_request = EncryptionMaterialsRequest(
        encryption_context={}, frame_length=1024, algorithm=algorithm_suite)

    with pytest.raises(InvalidCryptographicMaterialsError) as excinfo:
        encrypt_cmm.get_encryption_materials(encryption_materials_request)

    excinfo.match("Encryption materials do not match request!")
def test_get_encryption_materials_uncommitting_algorithm_require_encrypt_allow_decrypt(
        patch_for_dcmm_encrypt):
    """Tests that a Default Crypto Materials Manager request with policy REQUIRE_ENCRYPT_ALLOW_DECRYPT cannot
    encrypt using an algorithm that does not provide commitment."""
    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_ALLOW_DECRYPT

    cmm = DefaultCryptoMaterialsManager(master_key_provider=build_mkp())

    with pytest.raises(ActionNotAllowedError) as excinfo:
        cmm.get_encryption_materials(request=mock_request)
    excinfo.match(
        "Configuration conflict. Cannot encrypt due to .* requiring only committed messages"
    )
    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 build_cmm():
    mock_mkp = MagicMock(__class__=MasterKeyProvider)
    mock_mkp.decrypt_data_key_from_list.return_value = _DATA_KEY
    mock_mkp.master_keys_for_encryption.return_value = (
        sentinel.primary_mk,
        {sentinel.primary_mk, sentinel.mk_a, sentinel.mk_b},
    )
    return DefaultCryptoMaterialsManager(master_key_provider=mock_mkp)
예제 #13
0
    def __attrs_post_init__(self):
        """Normalize inputs to crypto material manager."""
        both_cmm_and_mkp_defined = self.materials_manager is not None and self.key_provider is not None
        neither_cmm_nor_mkp_defined = self.materials_manager is None and self.key_provider is None

        if both_cmm_and_mkp_defined or neither_cmm_nor_mkp_defined:
            raise TypeError("Exactly one of materials_manager or key_provider must be provided")
        if self.materials_manager is None:
            self.materials_manager = DefaultCryptoMaterialsManager(master_key_provider=self.key_provider)
예제 #14
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
예제 #16
0
    def run_scenario_with_tampering(self, ciphertext_writer, generation_scenario, plaintext_uri):
        """
        Run a given scenario, tampering with the input or the result.

        return: a list of (ciphertext, result) pairs
        """
        materials_manager = DefaultCryptoMaterialsManager(
            generation_scenario.encryption_scenario.master_key_provider_fn()
        )
        ciphertext_to_decrypt = generation_scenario.encryption_scenario.run(materials_manager)
        if generation_scenario.result:
            expected_result = generation_scenario.result
        else:
            expected_result = MessageDecryptionTestResult.expect_output(
                plaintext_uri=plaintext_uri, plaintext=generation_scenario.encryption_scenario.plaintext
            )
        return [
            generation_scenario.decryption_test_scenario_pair(ciphertext_writer, ciphertext_to_decrypt, expected_result)
        ]
def test_decrypt_with_keyring_materials_incomplete(algorithm_suite):
    raw_aes256_keyring = ephemeral_raw_aes_keyring(
        WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING)
    raw_aes128_keyring = ephemeral_raw_aes_keyring(
        WrappingAlgorithm.AES_128_GCM_IV12_TAG16_NO_PADDING)

    encrypt_cmm = DefaultCryptoMaterialsManager(keyring=raw_aes256_keyring)
    decrypt_cmm = DefaultCryptoMaterialsManager(keyring=raw_aes128_keyring)

    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 are incomplete!")
def test_attributes_default():
    cmm = DefaultCryptoMaterialsManager(master_key_provider=MagicMock(
        __class__=MasterKeyProvider))
    assert cmm.algorithm is ALGORITHM
def test_attributes_fail(kwargs):
    with pytest.raises(TypeError):
        DefaultCryptoMaterialsManager(**kwargs)
def build_cmm():
    mock_mkp = build_mkp()
    return DefaultCryptoMaterialsManager(master_key_provider=mock_mkp)
def test_attributes_fail():
    with pytest.raises(TypeError):
        DefaultCryptoMaterialsManager(master_key_provider=None)
예제 #22
0
 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)