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." )
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)
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!")
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