def test_decrypt_python_item(): key_id = "key" key_store = create_in_memory_key_store() key_store.create_main_key(key_id) plaintext_item = { "example": "data", "some numbers": 99, } encrypted_attributes = set(plaintext_item.keys()) encrypted_item = encrypt_python_item( item=plaintext_item, key_id=key_id, key_store=key_store, encryption_context=EncryptionContext(), attribute_actions=AttributeActions(), ) decrypted_item = decrypt_python_item( item=encrypted_item, key_store=key_store, encryption_context=EncryptionContext(), attribute_actions=AttributeActions(), ) for name in encrypted_attributes: assert encrypted_item[name] != plaintext_item[name] assert decrypted_item[name] == plaintext_item[name]
def test_no_decryption_key_but_decryption_requested(actions, parametrized_item): encryption_key = JceNameLocalDelegatedKey.generate("AES", 256) signing_key = JceNameLocalDelegatedKey.generate("HmacSHA256", 256) encrypting_cmp = StaticCryptographicMaterialsProvider( encryption_materials=RawEncryptionMaterials( encryption_key=encryption_key, signing_key=signing_key)) decrypting_cmp = StaticCryptographicMaterialsProvider( decryption_materials=RawDecryptionMaterials( verification_key=signing_key)) encrypted_item = encrypt_python_item( parametrized_item, CryptoConfig(materials_provider=encrypting_cmp, encryption_context=EncryptionContext(), attribute_actions=actions), ) with pytest.raises(DecryptionError) as excinfo: decrypt_python_item( encrypted_item, CryptoConfig(materials_provider=decrypting_cmp, encryption_context=EncryptionContext(), attribute_actions=actions), ) excinfo.match( "Attribute actions ask for some attributes to be decrypted but no decryption key is available" )
def _item_check( materials_provider, table_name, table_index, ciphertext_item, plaintext_item, attribute_actions, prep ): prep() # Test scenario setup that needs to happen inside the test cmp = materials_provider() # Some of the materials providers need to be constructed inside the test encryption_context = EncryptionContext( table_name=table_name, partition_key_name=table_index['partition'], sort_key_name=table_index.get('sort', None), attributes=ciphertext_item ) crypto_config = CryptoConfig( materials_provider=cmp, encryption_context=encryption_context, attribute_actions=attribute_actions ) decrypted_item = decrypt_dynamodb_item(ciphertext_item.copy(), crypto_config) assert set(decrypted_item.keys()) == set(plaintext_item.keys()) for key in decrypted_item: if key == 'version': continue assert decrypted_item[key] == plaintext_item[key]
def _decrypt_dynamodb_response(self, response): ec_kwargs = self._table_info.encryption_context_values if self._table_info.primary_index is not None: ec_kwargs.update({ "partition_key_name": self._table_info.primary_index.partition, "sort_key_name": self._table_info.primary_index.sort, }) self._attribute_actions.set_index_keys( *self._table_info.protected_index_keys()) encryption_context = EncryptionContext(**ec_kwargs) def decrypt(items): for item in items: try: decrypted_item = decrypt_python_item( item=item, key_store=self._key_store, encryption_context=encryption_context, attribute_actions=self._attribute_actions, ) yield decrypted_item except Exception: # TODO pass response["Items"] = list(decrypt(response["Items"])) response["Count"] = len(response["Items"]) return response
def test_no_materials(method, message): empty_cmp = StaticCryptographicMaterialsProvider() with pytest.raises(AttributeError) as excinfo: getattr(empty_cmp, method)(EncryptionContext()) excinfo.match(message)
class CryptoItems: with open('aws') as v: aws_access_key_id = str(v.readline().strip()) aws_secret_access_key = str(v.readline().strip()) dbResource = boto3.resource('dynamodb', aws_access_key_id=aws_access_key_id, aws_secret_access_key=aws_secret_access_key, region_name='us-east-1').Table('Users') # crypto key and material provider aws_cmk_id = 'arn:aws:kms:us-east-1:910140038075:key/353f6f4c-0d0b-47b1-99fc-3aeec929b973' aws_kms_cmp = AwsKmsCryptographicMaterialsProvider(key_id=aws_cmk_id) # how the crypto is applied to attributes crypto_actions = AttributeActions( default_action=CryptoAction.DO_NOTHING, attribute_actions={'password': CryptoAction.ENCRYPT_AND_SIGN}) crypto_context = EncryptionContext(table_name='Users') custom_crypto_config = CryptoConfig(materials_provider=aws_kms_cmp, attribute_actions=crypto_actions, encryption_context=crypto_context) encrypted_resource = EncryptedTable(table=dbResource, materials_provider=aws_kms_cmp, attribute_actions=crypto_actions)
def _item_cycle_check(materials_provider, attribute_actions, item): crypto_config = CryptoConfig( materials_provider=materials_provider, encryption_context=EncryptionContext(), attribute_actions=attribute_actions, ) cycle_item_check(item, crypto_config)
def get_main_key(self, key_id: str) -> MainKey: index_key = {"key_id": key_id} encryption_context = EncryptionContext( table_name=self._table_name, partition_key_name="key_id", attributes=dict_to_ddb(index_key), ) crypto_config = CryptoConfig( materials_provider=self._materials_provider, encryption_context=encryption_context, attribute_actions=self._actions, ) encrypted_item = self._get_item(key_id=key_id) if encrypted_item["restricted"]: raise Exception("Access restricted.") decrypted_item = decrypt_python_item(encrypted_item, crypto_config) return MainKey( key_id=key_id, key_bytes=decrypted_item["key"].value, )
def create_main_key(self, key_id: str, key_length=256, key_bytes=None) -> MainKey: index_key = {"key_id": key_id} if key_bytes is None: key_bytes = self._key_bytes_generator(key_length) plaintext_item = { "restricted": False, "on_hold": False, "key": key_bytes, } plaintext_item.update(index_key) encryption_context = EncryptionContext( table_name=self._table_name, partition_key_name="key_id", attributes=dict_to_ddb(index_key), ) crypto_config = CryptoConfig( materials_provider=self._materials_provider, encryption_context=encryption_context, attribute_actions=self._actions, ) encrypted_item = encrypt_python_item(plaintext_item, crypto_config) self._put_item(key_id=key_id, encrypted_item=encrypted_item) return MainKey( key_id=key_id, key_bytes=key_bytes, )
def test_aws_kms_item_cycle(all_aws_kms_cmp_builders, parametrized_actions, parametrized_item): crypto_config = CryptoConfig( materials_provider=all_aws_kms_cmp_builders(), encryption_context=EncryptionContext(), attribute_actions=parametrized_actions, ) functional_test_utils.cycle_item_check(parametrized_item, crypto_config)
def test_no_materials(method, error_type, message): empty_cmp = WrappedCryptographicMaterialsProvider(signing_key=MagicMock( __class__=DelegatedKey)) with pytest.raises(error_type) as excinfo: getattr(empty_cmp, method)(EncryptionContext()) excinfo.match(message)
def test_aws_kms_item_cycle_hypothesis_veryslow(all_aws_kms_cmp_builders, hypothesis_actions, item): crypto_config = CryptoConfig( materials_provider=all_aws_kms_cmp_builders(), encryption_context=EncryptionContext(), attribute_actions=hypothesis_actions, ) functional_test_utils.cycle_item_check(item, crypto_config)
def test_decryption_materials(default_kms_cmp, patch_decrypt_initial_material): material_description = {"some": "data"} encryption_context = EncryptionContext(material_description=material_description) test = default_kms_cmp.decryption_materials(encryption_context) assert test.verification_key == _DELEGATED_KEYS["signing"] assert test.decryption_key == _DELEGATED_KEYS["encryption"] assert test.material_description == material_description
def test_generate_initial_material_fail(default_kms_cmp, patch_kms_client): default_kms_cmp._key_id = _KEY_ID patch_kms_client.return_value.generate_data_key.side_effect = botocore.exceptions.ClientError({}, "") with pytest.raises(WrappingError) as excinfo: default_kms_cmp._generate_initial_material(EncryptionContext()) excinfo.match("Failed to generate materials using AWS KMS")
def test_decrypt_initial_material(default_kms_cmp, patch_kms_client): default_kms_cmp._key_id = _KEY_ID test = default_kms_cmp._decrypt_initial_material( EncryptionContext(material_description=_DEFAULT_ADDITIONAL_MATERIAL_DESCRIPTION) ) assert test == _DERIVED_KEYS["initial_material"]
def test_with_item(wrapped_cmp): config = CryptoConfig(materials_provider=wrapped_cmp, encryption_context=EncryptionContext(attributes={}), attribute_actions=AttributeActions()) item = {'test': 'item', 'with': 'some data'} new_config = config.with_item(item) assert config.encryption_context.attributes == {} assert new_config.encryption_context.attributes == item
def test_decryption_materials(default_kms_cmp, patch_decrypt_initial_material): material_description = {'some': 'data'} encryption_context = EncryptionContext( material_description=material_description) test = default_kms_cmp.decryption_materials(encryption_context) assert test.verification_key == _DELEGATED_KEYS['signing'] assert test.decryption_key == _DELEGATED_KEYS['encryption'] assert test.material_description == material_description
def test_encryption_context_repr(operator): value = EncryptionContext( attributes={"unique_name": { "S": "unique_value" }}) test = operator(value) assert "unique_name" not in test assert "unique_value" not in test
def test_encryption_materials(default_kms_cmp, patch_generate_initial_material): material_description = {"some": "data"} encryption_context = EncryptionContext(material_description=material_description) test = default_kms_cmp.encryption_materials(encryption_context) expected_material_description = material_description.copy() expected_material_description.update(_DEFAULT_ADDITIONAL_MATERIAL_DESCRIPTION) assert test.signing_key == _DELEGATED_KEYS["signing"] assert test.encryption_key == _DELEGATED_KEYS["encryption"] assert test.material_description == expected_material_description
def test_decrypt_initial_material_fail(default_kms_cmp, patch_kms_client): default_kms_cmp._key_id = _KEY_ID patch_kms_client.return_value.decrypt.side_effect = botocore.exceptions.ClientError({}, "") with pytest.raises(UnwrappingError) as excinfo: default_kms_cmp._decrypt_initial_material( EncryptionContext(material_description=_DEFAULT_ADDITIONAL_MATERIAL_DESCRIPTION) ) excinfo.match("Failed to unwrap AWS KMS protected materials")
def test_with_item(wrapped_cmp): config = CryptoConfig( materials_provider=wrapped_cmp, encryption_context=EncryptionContext(attributes={}), attribute_actions=AttributeActions(), ) item = {"test": "item", "with": "some data"} new_config = config.with_item(item) assert config.encryption_context.attributes == {} assert new_config.encryption_context.attributes == item
def test_no_encryption_key_but_encryption_requested(actions, parametrized_item): signing_key = JceNameLocalDelegatedKey.generate("HmacSHA256", 256) cmp = StaticCryptographicMaterialsProvider(encryption_materials=RawEncryptionMaterials(signing_key=signing_key)) crypto_config = CryptoConfig( materials_provider=cmp, encryption_context=EncryptionContext(), attribute_actions=actions ) with pytest.raises(EncryptionError) as excinfo: encrypt_python_item(parametrized_item, crypto_config) excinfo.match("Attribute actions ask for some attributes to be encrypted but no encryption key is available")
def test_aws_kms_diverse_indexes(aws_kms_cmp, item): """Verify that AWS KMS cycle works for items with all possible combinations for primary index attribute types.""" crypto_config = CryptoConfig( materials_provider=aws_kms_cmp, encryption_context=EncryptionContext( partition_key_name='partition_key', sort_key_name='sort_key', attributes=dict_to_ddb(item)), attribute_actions=AttributeActions(attribute_actions={ key: CryptoAction.SIGN_ONLY for key in _primary_key_names })) functional_test_utils.cycle_item_check(item, crypto_config)
def test_valid_materials(mocker, method): mocker.patch.object(WrappedCryptographicMaterialsProvider, '_build_materials') cmp = WrappedCryptographicMaterialsProvider( signing_key=MagicMock(__class__=DelegatedKey), wrapping_key=MagicMock(__class__=DelegatedKey), unwrapping_key=MagicMock(__class__=DelegatedKey)) context = EncryptionContext() test = getattr(cmp, method)(context) WrappedCryptographicMaterialsProvider._build_materials.assert_called_once_with( context) assert test is WrappedCryptographicMaterialsProvider._build_materials.return_value
def test_only_sign_item(parametrized_item): signing_key = JceNameLocalDelegatedKey.generate("HmacSHA256", 256) cmp = StaticCryptographicMaterialsProvider( encryption_materials=RawEncryptionMaterials(signing_key=signing_key), decryption_materials=RawDecryptionMaterials(verification_key=signing_key), ) actions = AttributeActions(default_action=CryptoAction.SIGN_ONLY) crypto_config = CryptoConfig( materials_provider=cmp, encryption_context=EncryptionContext(), attribute_actions=actions ) signed_item = encrypt_python_item(parametrized_item, crypto_config) material_description = signed_item[ReservedAttributes.MATERIAL_DESCRIPTION.value].value assert MaterialDescriptionKeys.ATTRIBUTE_ENCRYPTION_MODE.value.encode("utf-8") not in material_description decrypt_python_item(signed_item, crypto_config)
def _item_check(materials_provider, table_name, table_index, ciphertext_item, plaintext_item, attribute_actions): cmp = materials_provider() # Some of the materials providers need to be constructed inside the test encryption_context = EncryptionContext( table_name=table_name, partition_key_name=table_index["partition"], sort_key_name=table_index.get("sort", None), attributes=ciphertext_item, ) crypto_config = CryptoConfig( materials_provider=cmp, encryption_context=encryption_context, attribute_actions=attribute_actions ) decrypted_item = decrypt_dynamodb_item(ciphertext_item.copy(), crypto_config) assert set(decrypted_item.keys()) == set(plaintext_item.keys()) for key in decrypted_item: if key == "version": continue assert decrypted_item[key] == plaintext_item[key]
def crypto_config_from_table_info(materials_provider, attribute_actions, table_info): """Build a crypto config from the provided values and table info. :returns: crypto config and updated kwargs :rtype: tuple(CryptoConfig, dict) """ ec_kwargs = table_info.encryption_context_values if table_info.primary_index is not None: ec_kwargs.update({ 'partition_key_name': table_info.primary_index.partition, 'sort_key_name': table_info.primary_index.sort }) return CryptoConfig(materials_provider=materials_provider, encryption_context=EncryptionContext(**ec_kwargs), attribute_actions=attribute_actions)
def test_build_materials(mocker): mocker.patch.object(dynamodb_encryption_sdk.material_providers.wrapped, 'WrappedCryptographicMaterials') cmp = WrappedCryptographicMaterialsProvider( signing_key=MagicMock(__class__=DelegatedKey), wrapping_key=MagicMock(__class__=DelegatedKey), unwrapping_key=MagicMock(__class__=DelegatedKey)) material_description = {'some': 'data'} context = EncryptionContext(material_description=material_description) test = cmp._build_materials(context) dynamodb_encryption_sdk.material_providers.wrapped.WrappedCryptographicMaterials.assert_called_once_with( wrapping_key=cmp._wrapping_key, unwrapping_key=cmp._unwrapping_key, signing_key=cmp._signing_key, material_description=material_description) assert test is dynamodb_encryption_sdk.material_providers.wrapped.WrappedCryptographicMaterials.return_value
def static_cmp_crypto_config(): return CryptoConfig( materials_provider=build_static_jce_cmp("AES", 256, "HmacSHA256", 256), encryption_context=EncryptionContext(), attribute_actions=AttributeActions(), )
def test_generate_initial_material(default_kms_cmp, patch_kms_client): default_kms_cmp._key_id = _KEY_ID test = default_kms_cmp._generate_initial_material(EncryptionContext()) assert test == (_DERIVED_KEYS["initial_material"], _DERIVED_KEYS["encrypted_initial_material"])