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_attribute_actions_index_override_fail(default): test = AttributeActions( default_action=default, attribute_actions={"indexed_attribute": CryptoAction.ENCRYPT_AND_SIGN}) with pytest.raises(InvalidArgumentError) as excinfo: test.set_index_keys("indexed_attribute") excinfo.match( r"Cannot overwrite a previously requested action on indexed attribute: *" )
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 test_query(table): key_id = "key" key_store = create_in_memory_key_store() key_store.create_main_key(key_id) index_key = {"id": "foo"} plaintext_item = { "example": "data", "some numbers": 99, "ignore": "no encryption", } encrypted_attributes = set(plaintext_item.keys()) encrypted_attributes.remove("ignore") unencrypted_attributes = set(index_key.keys()) unencrypted_attributes.add("ignore") plaintext_item.update(index_key) actions = AttributeActions( default_action=CryptoAction.ENCRYPT_AND_SIGN, attribute_actions={ "ignore": CryptoAction.DO_NOTHING, }, ) crypto_table = CryptoTable( table=table, key_store=key_store, attribute_actions=actions, ) crypto_table.put_item(CSEKeyId=key_id, Item=plaintext_item) encrypted_items = table.query( KeyConditionExpression=Key("id").eq("foo"))["Items"] decrypted_items = crypto_table.query( KeyConditionExpression=Key("id").eq("foo"))["Items"] assert len(encrypted_items) == 1 assert len(decrypted_items) == 1 for name in encrypted_attributes: assert encrypted_items[0][name] != plaintext_item[name] assert decrypted_items[0][name] == plaintext_item[name] for name in unencrypted_attributes: assert decrypted_items[0][name] == encrypted_items[0][ name] == plaintext_item[name] # shredding key_store.delete_main_key(key_id) encrypted = table.query(KeyConditionExpression=Key("id").eq("foo")) decrypted = crypto_table.query(KeyConditionExpression=Key("id").eq("foo")) assert encrypted["Count"] == 1 assert len(encrypted["Items"]) == 1 assert decrypted["Count"] == 0 assert len(decrypted["Items"]) == 0
def _build_plaintext_items(plaintext_file, version): # pylint: disable=too-many-locals with open(plaintext_file) as f: plaintext_data = json.load(f) actions = {} for name, description in plaintext_data["actions"].items(): default_action = _action(description["default"]) attribute_actions = { attribute_name: _action(attribute_action) for attribute_name, attribute_action in description.get( "override", {}).items() } actions[name.lower()] = AttributeActions( default_action=default_action, attribute_actions=attribute_actions) tables = defaultdict(list) for table_name, table_data in plaintext_data["items"].items(): table_items = [] for item in table_data["items"]: item_actions = actions[item["action"]].copy() item_actions.set_index_keys(*table_data["index"].values()) attributes = item["attributes"].copy() if not item.get("exact", False): for group in plaintext_data["versions"].get(table_name, {}).get( version, []): attributes.update(plaintext_data["attributes"][group]) _decode_item(attributes) table_items.append(dict(item=attributes, action=item_actions)) tables[table_name] = dict(index=table_data["index"], items=table_items) return tables
def encrypt_item(table_name, aws_cmk_id, meta_table_name, material_name): """Demonstrate use of EncryptedTable to transparently encrypt an item.""" index_key = {"partition_attribute": "is this", "sort_attribute": 55} plaintext_item = { "example": "data", "some numbers": 99, "and some binary": Binary(b"\x00\x01\x02"), "leave me": "alone", # We want to ignore this attribute } # Collect all of the attributes that will be encrypted (used later). encrypted_attributes = set(plaintext_item.keys()) encrypted_attributes.remove("leave me") # Collect all of the attributes that will not be encrypted (used later). unencrypted_attributes = set(index_key.keys()) unencrypted_attributes.add("leave me") # Add the index pairs to the item. plaintext_item.update(index_key) # Create a normal table resource for the meta store. meta_table = boto3.resource("dynamodb").Table(meta_table_name) # Create a crypto materials provider for the meta store using the specified AWS KMS key. aws_kms_cmp = AwsKmsCryptographicMaterialsProvider(key_id=aws_cmk_id) # Create a meta store using the AWS KMS crypto materials provider. meta_store = MetaStore(table=meta_table, materials_provider=aws_kms_cmp) # Create a most recent provider using the meta store. most_recent_cmp = MostRecentProvider( provider_store=meta_store, material_name=material_name, version_ttl=600.0, # Check for a new material version every five minutes. ) # Create a normal table resource. table = boto3.resource("dynamodb").Table(table_name) # Create attribute actions that tells the encrypted table to encrypt all attributes except one. actions = AttributeActions( default_action=CryptoAction.ENCRYPT_AND_SIGN, attribute_actions={"leave me": CryptoAction.DO_NOTHING} ) # Use these objects to create an encrypted table resource. encrypted_table = EncryptedTable(table=table, materials_provider=most_recent_cmp, attribute_actions=actions) # Put the item to the table, using the encrypted table resource to transparently encrypt it. encrypted_table.put_item(Item=plaintext_item) # Get the encrypted item using the standard table resource. encrypted_item = table.get_item(Key=index_key)["Item"] # Get the item using the encrypted table resource, transparently decyrpting it. decrypted_item = encrypted_table.get_item(Key=index_key)["Item"] # Verify that all of the attributes are different in the encrypted item for name in encrypted_attributes: assert encrypted_item[name] != plaintext_item[name] assert decrypted_item[name] == plaintext_item[name] # Verify that all of the attributes that should not be encrypted were not. for name in unencrypted_attributes: assert decrypted_item[name] == encrypted_item[name] == plaintext_item[name] # Clean up the item encrypted_table.delete_item(Key=index_key)
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 __init__( self, materials_provider, table_name: str = "key_store_table", key_bytes_generator=key_bytes_generator, ) -> None: self._materials_provider = materials_provider self._table_name = table_name self._key_bytes_generator = key_bytes_generator self._actions = AttributeActions( default_action=CryptoAction.ENCRYPT_AND_SIGN, attribute_actions={ "restricted": CryptoAction.DO_NOTHING, "on_hold": CryptoAction.DO_NOTHING, "ttl": CryptoAction.DO_NOTHING, }, ) self._actions.set_index_keys("key_id")
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 encrypt_item(table_name, aws_cmk_id): """Demonstrate use of EncryptedTable to transparently encrypt an item.""" index_key = {'partition_attribute': 'is this', 'sort_attribute': 55} plaintext_item = { 'example': 'data', 'some numbers': 99, 'and some binary': Binary(b'\x00\x01\x02'), 'leave me': 'alone' # We want to ignore this attribute } # Collect all of the attributes that will be encrypted (used later). encrypted_attributes = set(plaintext_item.keys()) encrypted_attributes.remove('leave me') # Collect all of the attributes that will not be encrypted (used later). unencrypted_attributes = set(index_key.keys()) unencrypted_attributes.add('leave me') # Add the index pairs to the item. plaintext_item.update(index_key) # Create a normal table resource. table = boto3.resource('dynamodb').Table(table_name) # Create a crypto materials provider using the specified AWS KMS key. aws_kms_cmp = AwsKmsCryptographicMaterialsProvider(key_id=aws_cmk_id) # Create attribute actions that tells the encrypted table to encrypt all attributes except one. actions = AttributeActions( default_action=CryptoAction.ENCRYPT_AND_SIGN, attribute_actions={'leave me': CryptoAction.DO_NOTHING}) # Use these objects to create an encrypted table resource. encrypted_table = EncryptedTable(table=table, materials_provider=aws_kms_cmp, attribute_actions=actions) # Put the item to the table, using the encrypted table resource to transparently encrypt it. encrypted_table.put_item(Item=plaintext_item) # Get the encrypted item using the standard table resource. encrypted_item = table.get_item(Key=index_key)['Item'] # Get the item using the encrypted table resource, transparently decyrpting it. decrypted_item = encrypted_table.get_item(Key=index_key)['Item'] # Verify that all of the attributes are different in the encrypted item for name in encrypted_attributes: assert encrypted_item[name] != plaintext_item[name] assert decrypted_item[name] == plaintext_item[name] # Verify that all of the attributes that should not be encrypted were not. for name in unencrypted_attributes: assert decrypted_item[name] == encrypted_item[name] == plaintext_item[ name] # Clean up the item encrypted_table.delete_item(Key=index_key)
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 encrypt_item(table_name, aws_cmk_id): """Demonstrate use of EncryptedClient to transparently encrypt an item.""" index_key = {"partition_attribute": {"S": "is this"}, "sort_attribute": {"N": "55"}} plaintext_item = { "example": {"S": "data"}, "some numbers": {"N": "99"}, "and some binary": {"B": b"\x00\x01\x02"}, "leave me": {"S": "alone"}, # We want to ignore this attribute } # Collect all of the attributes that will be encrypted (used later). encrypted_attributes = set(plaintext_item.keys()) encrypted_attributes.remove("leave me") # Collect all of the attributes that will not be encrypted (used later). unencrypted_attributes = set(index_key.keys()) unencrypted_attributes.add("leave me") # Add the index pairs to the item. plaintext_item.update(index_key) # Create a normal client. client = boto3.client("dynamodb") # Create a crypto materials provider using the specified AWS KMS key. aws_kms_cmp = AwsKmsCryptographicMaterialsProvider(key_id=aws_cmk_id) # Create attribute actions that tells the encrypted client to encrypt all attributes except one. actions = AttributeActions( default_action=CryptoAction.ENCRYPT_AND_SIGN, attribute_actions={"leave me": CryptoAction.DO_NOTHING} ) # Use these objects to create an encrypted client. encrypted_client = EncryptedClient(client=client, materials_provider=aws_kms_cmp, attribute_actions=actions) # Put the item to the table, using the encrypted client to transparently encrypt it. encrypted_client.put_item(TableName=table_name, Item=plaintext_item) # Get the encrypted item using the standard client. encrypted_item = client.get_item(TableName=table_name, Key=index_key)["Item"] # Get the item using the encrypted client, transparently decyrpting it. decrypted_item = encrypted_client.get_item(TableName=table_name, Key=index_key)["Item"] # Verify that all of the attributes are different in the encrypted item for name in encrypted_attributes: assert encrypted_item[name] != plaintext_item[name] assert decrypted_item[name] == plaintext_item[name] # Verify that all of the attributes that should not be encrypted were not. for name in unencrypted_attributes: assert decrypted_item[name] == encrypted_item[name] == plaintext_item[name] # Clean up the item encrypted_client.delete_item(TableName=table_name, Key=index_key)
def test_get_item(table): key_id = "key" key_store = create_in_memory_key_store() key_store.create_main_key(key_id) index_key = {"id": "foo"} plaintext_item = { "example": "data", "some numbers": 99, "ignore": "no encryption", } encrypted_attributes = set(plaintext_item.keys()) encrypted_attributes.remove("ignore") unencrypted_attributes = set(index_key.keys()) unencrypted_attributes.add("ignore") plaintext_item.update(index_key) actions = AttributeActions( default_action=CryptoAction.ENCRYPT_AND_SIGN, attribute_actions={ "ignore": CryptoAction.DO_NOTHING, }, ) crypto_table = CryptoTable( table=table, key_store=key_store, attribute_actions=actions, ) crypto_table.put_item(CSEKeyId=key_id, Item=plaintext_item) encrypted_item = table.get_item(Key=index_key)["Item"] decrypted_item = crypto_table.get_item(Key=index_key)["Item"] for name in encrypted_attributes: assert encrypted_item[name] != plaintext_item[name] assert decrypted_item[name] == plaintext_item[name] for name in unencrypted_attributes: assert decrypted_item[name] == encrypted_item[name] == plaintext_item[ name] key_store.delete_main_key(key_id) with pytest.raises(Exception): decrypted_item = crypto_table.get_item(Key=index_key)["Item"]
def fetch_user_info(username, data, awsCmkId, table): kmsCmp = AwsKmsCryptographicMaterialsProvider(key_id=awsCmkId) actions = AttributeActions( default_action=CryptoAction.ENCRYPT_AND_SIGN ) encrypted_table = EncryptedTable(table=table, materials_provider=kmsCmp, attribute_actions=actions) resp = encrypted_table.get_item( Key = {'USERNAME': username} ) user = resp['Item'] info = user[data] return info
def __init__( self, client: BaseClient, key_store: KeyStore, attribute_actions: Optional[AttributeActions] = None, auto_refresh_table_indexes: bool = True, expect_standard_dictionaries: bool = False, ) -> None: if attribute_actions is None: attribute_actions = AttributeActions() self._client = client self._key_store = key_store self._attribute_actions = attribute_actions self._auto_refresh_table_indexes = auto_refresh_table_indexes self._expect_standard_dictionaries = expect_standard_dictionaries
def get_decrypted_item(index_key, table_name): table = boto3.resource('dynamodb').Table(table_name) aws_kms_cmp = AwsKmsCryptographicMaterialsProvider(key_id=aws_cmk_id) actions = AttributeActions( default_action=CryptoAction.ENCRYPT_AND_SIGN, attribute_actions={'Account_Id': CryptoAction.DO_NOTHING}) encrypted_table = EncryptedTable(table=table, materials_provider=aws_kms_cmp, attribute_actions=actions) response = encrypted_table.get_item(Key=index_key) return response
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 encrypt_item(USERNAME, PASSWORD, FIRST_NAME, MIDDLE_INITIAL, LAST_NAME, DOB, SEX, HEIGHT, WEIGHT, ALLERGIES, TOKEN, awsCmkId, table): kmsCmp = AwsKmsCryptographicMaterialsProvider(key_id=awsCmkId) actions = AttributeActions(default_action=CryptoAction.ENCRYPT_AND_SIGN) index_key = {} plaintext_item = { 'USERNAME': USERNAME, 'PASSWORD': PASSWORD, 'FIRST_NAME': FIRST_NAME, 'MIDDLE_INITIAL': MIDDLE_INITIAL, 'LAST_NAME': LAST_NAME, 'DOB': DOB, 'SEX': SEX, 'HEIGHT': HEIGHT, 'WEIGHT': WEIGHT, 'ALLERGIES': ALLERGIES, 'AUTH_TOKEN': TOKEN } encrypted_attributes = set(plaintext_item.keys()) unencrypted_attributes = set(index_key.keys()) encrypted_table = EncryptedTable(table=table, materials_provider=kmsCmp, attribute_actions=actions) encrypted_table.put_item(Item=plaintext_item) encrypted_item = table.get_item(Key={'USERNAME': USERNAME})["Item"] decrypted_item = encrypted_table.get_item( Key={'USERNAME': USERNAME})["Item"] # Verify that all of the attributes are different in the encrypted item for name in encrypted_attributes: if name != 'USERNAME': assert encrypted_item[name] != plaintext_item[name] assert decrypted_item[name] == plaintext_item[name] # Verify that all of the attributes that should not be encrypted were not. for name in unencrypted_attributes: assert decrypted_item[name] == encrypted_item[name] == plaintext_item[ name]
def string_to_sign_test_vectors(): with open(_STRING_TO_SIGN_TEST_VECTORS_FILE) as f: vectors = json.load(f) for vector in vectors: item = { key: decode_value(value['value']) for key, value in vector['item'].items() } bare_actions = { key: ACTION_MAP[value['action']] for key, value in vector['item'].items() } attribute_actions = AttributeActions( default_action=CryptoAction.DO_NOTHING, attribute_actions=bare_actions) yield (item, vector['table'], attribute_actions, base64.b64decode( codecs.encode(vector['string_to_sign'], 'utf-8')))
def encrypt_item(plaintext_item, table_name): table = boto3.resource('dynamodb').Table(table_name) aws_kms_cmp = AwsKmsCryptographicMaterialsProvider(key_id=aws_cmk_id) actions = AttributeActions( default_action=CryptoAction.ENCRYPT_AND_SIGN, attribute_actions={'Account_Id': CryptoAction.DO_NOTHING}) encrypted_table = EncryptedTable(table=table, materials_provider=aws_kms_cmp, attribute_actions=actions) print(plaintext_item) response = encrypted_table.put_item(Item=plaintext_item) print(response) return response
def get_decrypted_item(index_key, table_name): #table_name='CreditCardTokenizerTable' table = boto3.resource('dynamodb').Table(table_name) aws_cmk_id = 'arn:aws:kms:us-west-2:176385768664:key/bd3a8796-1638-42f3-b318-ac357427f326' aws_kms_cmp = AwsKmsCryptographicMaterialsProvider(key_id=aws_cmk_id) actions = AttributeActions( default_action=CryptoAction.ENCRYPT_AND_SIGN, attribute_actions={'Account_Id': CryptoAction.DO_NOTHING}) encrypted_table = EncryptedTable(table=table, materials_provider=aws_kms_cmp, attribute_actions=actions) response = encrypted_table.get_item(Key=index_key) return response
def __init__( self, resource, # type: ServiceResource materials_provider, # type: CryptographicMaterialsProvider attribute_actions=None, # type: Optional[AttributeActions] auto_refresh_table_indexes=True, # type: Optional[bool] ): # noqa=D107 # type: (...) -> None # Workaround pending resolution of attrs/mypy interaction. # https://github.com/python/mypy/issues/2088 # https://github.com/python-attrs/attrs/issues/215 if attribute_actions is None: attribute_actions = AttributeActions() self._resource = resource self._materials_provider = materials_provider self._attribute_actions = attribute_actions self._auto_refresh_table_indexes = auto_refresh_table_indexes attr.validate(self) self.__attrs_post_init__()
def __init__( self, table: ServiceResource, key_store: KeyStore, table_info: Optional[TableInfo] = None, attribute_actions: Optional[AttributeActions] = None, auto_refresh_table_indexes: Optional[bool] = True, ) -> None: if attribute_actions is None: attribute_actions = AttributeActions() if table_info is None: table_info = TableInfo(name=table.name) if auto_refresh_table_indexes: table_info.refresh_indexed_attributes(table.meta.client) self._table = table self._key_store = key_store self._table_info = table_info self._attribute_actions = attribute_actions
def fetch_user(username, awsCmkId, table): try: kmsCmp = AwsKmsCryptographicMaterialsProvider(key_id=awsCmkId) actions = AttributeActions( default_action=CryptoAction.ENCRYPT_AND_SIGN ) encrypted_table = EncryptedTable(table=table, materials_provider=kmsCmp, attribute_actions=actions) #gets decrypted info from encrypted table response = encrypted_table.get_item( Key = {'USERNAME': username} ) item = response['Item'] return item except Exception as e: print("in get_user.py: Exception. Could not get user from database. " + str(e)) return -1
def __init__( self, client, # type: botocore.client.BaseClient materials_provider, # type: CryptographicMaterialsProvider attribute_actions=None, # type: Optional[AttributeActions] auto_refresh_table_indexes=True, # type: Optional[bool] expect_standard_dictionaries=False # type: Optional[bool] ): # noqa=D107 # type: (...) -> None # Workaround pending resolution of attrs/mypy interaction. # https://github.com/python/mypy/issues/2088 # https://github.com/python-attrs/attrs/issues/215 if attribute_actions is None: attribute_actions = AttributeActions() self._client = client self._materials_provider = materials_provider self._attribute_actions = attribute_actions self._auto_refresh_table_indexes = auto_refresh_table_indexes self._expect_standard_dictionaries = expect_standard_dictionaries attr.validate(self) self.__attrs_post_init__()
def static_cmp_crypto_config(): return CryptoConfig( materials_provider=build_static_jce_cmp("AES", 256, "HmacSHA256", 256), encryption_context=EncryptionContext(), attribute_actions=AttributeActions(), )
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) @pytest.mark.parametrize( "actions", ( AttributeActions(default_action=CryptoAction.ENCRYPT_AND_SIGN), AttributeActions( default_action=CryptoAction.SIGN_ONLY, attribute_actions={"test": CryptoAction.ENCRYPT_AND_SIGN}), ), ) 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:
id=id_string, ) def set_parametrized_cmp(metafunc): """Set paramatrized values for cryptographic materials providers.""" for name, algorithm_generator in (("all_the_cmps", _all_algorithm_pairs), ("some_cmps", _some_algorithm_pairs)): if name in metafunc.fixturenames: metafunc.parametrize(name, _all_possible_cmps(algorithm_generator)) _ACTIONS = { "hypothesis_actions": ( pytest.param( AttributeActions(default_action=CryptoAction.ENCRYPT_AND_SIGN), id="encrypt all"), pytest.param(AttributeActions(default_action=CryptoAction.SIGN_ONLY), id="sign only all"), pytest.param(AttributeActions(default_action=CryptoAction.DO_NOTHING), id="do nothing"), ) } _ACTIONS["parametrized_actions"] = _ACTIONS["hypothesis_actions"] + ( pytest.param( AttributeActions( default_action=CryptoAction.ENCRYPT_AND_SIGN, attribute_actions={ "number_set": CryptoAction.SIGN_ONLY, "string_set": CryptoAction.SIGN_ONLY, "binary_set": CryptoAction.SIGN_ONLY,
def encrypt_item(table_name, aws_cmk_id): """Demonstrate use of EncryptedTable to transparently encrypt an item.""" index_key = { 'partition_attribute': 'is this', 'sort_attribute': 55 } plaintext_item = { 'example': 'data', 'some numbers': 99, 'and some binary': Binary(b'\x00\x01\x02'), 'leave me': 'alone' # We want to ignore this attribute } # Collect all of the attributes that will be encrypted (used later). encrypted_attributes = set(plaintext_item.keys()) encrypted_attributes.remove('leave me') # Collect all of the attributes that will not be encrypted (used later). unencrypted_attributes = set(index_key.keys()) unencrypted_attributes.add('leave me') # Add the index pairs to the item. plaintext_item.update(index_key) # Create a normal table resource. table = boto3.resource('dynamodb').Table(table_name) # Use the TableInfo helper to collect information about the indexes. table_info = TableInfo(name=table_name) table_info.refresh_indexed_attributes(table.meta.client) # Create a crypto materials provider using the specified AWS KMS key. aws_kms_cmp = AwsKmsCryptographicMaterialsProvider(key_id=aws_cmk_id) encryption_context = EncryptionContext( table_name=table_name, partition_key_name=table_info.primary_index.partition, sort_key_name=table_info.primary_index.sort, # The only attributes that are used by the AWS KMS cryptographic materials providers # are the primary index attributes. # These attributes need to be in the form of a DynamoDB JSON structure, so first # convert the standard dictionary. attributes=dict_to_ddb(index_key) ) # Create attribute actions that tells the encrypted table to encrypt all attributes, # only sign the primary index attributes, and ignore the one identified attribute to # ignore. actions = AttributeActions( default_action=CryptoAction.ENCRYPT_AND_SIGN, attribute_actions={'leave me': CryptoAction.DO_NOTHING} ) actions.set_index_keys(*table_info.protected_index_keys()) # Build the crypto config to use for this item. # When using the higher-level helpers, this is handled for you. crypto_config = CryptoConfig( materials_provider=aws_kms_cmp, encryption_context=encryption_context, attribute_actions=actions ) # Encrypt the plaintext item directly encrypted_item = encrypt_python_item(plaintext_item, crypto_config) # You could now put the encrypted item to DynamoDB just as you would any other item. # table.put_item(Item=encrypted_item) # We will skip this for the purposes of this example. # Decrypt the encrypted item directly decrypted_item = decrypt_python_item(encrypted_item, crypto_config) # Verify that all of the attributes are different in the encrypted item for name in encrypted_attributes: assert encrypted_item[name] != plaintext_item[name] assert decrypted_item[name] == plaintext_item[name] # Verify that all of the attributes that should not be encrypted were not. for name in unencrypted_attributes: assert decrypted_item[name] == encrypted_item[name] == plaintext_item[name]
def test_attribute_actions_index_override(default, overrides, expected_result): test = AttributeActions(default_action=default, attribute_actions=overrides) test.set_index_keys("indexed_attribute") assert test.action("indexed_attribute") is expected_result