def client_cycle_single_item_check(materials_provider, initial_actions, initial_item, table_name, region_name=None): check_attribute_actions = initial_actions.copy() check_attribute_actions.set_index_keys(*list(TEST_KEY.keys())) item = initial_item.copy() item.update(TEST_KEY) ddb_item = dict_to_ddb(item) ddb_key = dict_to_ddb(TEST_KEY) kwargs = {} if region_name is not None: kwargs["region_name"] = region_name client = boto3.client("dynamodb", **kwargs) e_client = EncryptedClient(client=client, materials_provider=materials_provider, attribute_actions=initial_actions) _put_result = e_client.put_item(TableName=table_name, Item=ddb_item) # noqa encrypted_result = client.get_item(TableName=table_name, Key=ddb_key, ConsistentRead=True) check_encrypted_item(item, ddb_to_dict(encrypted_result["Item"]), check_attribute_actions) decrypted_result = e_client.get_item(TableName=table_name, Key=ddb_key, ConsistentRead=True) assert ddb_to_dict(decrypted_result["Item"]) == item e_client.delete_item(TableName=table_name, Key=ddb_key) del item del check_attribute_actions
def decrypt_python_item(item, crypto_config): # type: (dynamodb_types.ITEM, CryptoConfig) -> dynamodb_types.ITEM """Decrypt a dictionary for DynamoDB. >>> from dynamodb_encryption_sdk.encrypted.item import decrypt_python_item >>> encrypted_item = { ... 'some': Binary(b'ENCRYPTED_DATA'), ... 'more': Binary(b'ENCRYPTED_DATA') ... } >>> decrypted_item = decrypt_python_item( ... item=encrypted_item, ... crypto_config=my_crypto_config ... ) .. note:: This handles human-friendly dictionaries and is for use with the boto3 DynamoDB service or table resource. :param dict item: Encrypted and signed dictionary :param CryptoConfig crypto_config: Cryptographic configuration :returns: Plaintext dictionary :rtype: dict """ ddb_item = dict_to_ddb(item) decrypted_ddb_item = decrypt_dynamodb_item(ddb_item, crypto_config) return ddb_to_dict(decrypted_ddb_item)
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_transformable_item(item): ddb_json = dict_to_ddb(item) serialized = {} for key, value in ddb_json.items(): serialized[key] = serialize_attribute(value) deserialized = {} for key, value in serialized.items(): deserialized[key] = deserialize_attribute(value) end_result = ddb_to_dict(deserialized) assert end_result == item
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 decrypt_python_item( item, key_store: KeyStore, encryption_context: EncryptionContext, attribute_actions: AttributeActions, ): ddb_item = dict_to_ddb(item) decrypted_ddb_item = decrypt_dynamodb_item( item=ddb_item, key_store=key_store, encryption_context=encryption_context, attribute_actions=attribute_actions, ) return ddb_to_dict(decrypted_ddb_item)
def _client_setup(materials_provider, table_name, table_index, ciphertext_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 client = fake_client(table_name, ciphertext_item) table_info = TableInfo(name=table_name, primary_index=TableIndex( partition=table_index['partition'], sort=table_index.get('sort', None))) item_key = { table_info.primary_index.partition: ciphertext_item[table_info.primary_index.partition] } if table_info.primary_index.sort is not None: item_key[table_info.primary_index.sort] = ciphertext_item[ table_info.primary_index.sort] e_client = EncryptedClient(client=client, materials_provider=cmp, attribute_actions=attribute_actions, auto_refresh_table_indexes=False) e_client._table_info_cache._all_tables_info[table_name] = table_info return e_client, dict_to_ddb(item_key)
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 _generate(materials_provider, table_data, ciphertext_file, metastore_info): # pylint: disable=too-many-locals client = boto3.client("dynamodb", region_name="us-west-2") data_table_output = defaultdict(list) metastore_output = defaultdict(list) metatable = _create_meta_table(client, metastore_info) for table_name in table_data: table = None try: table_index = table_data[table_name]["index"] table_index_types = table_data[table_name]["index_types"] table_items = table_data[table_name]["items"] _create_data_table(client, table_name, table_index, table_index_types) table = boto3.resource("dynamodb", region_name="us-west-2").Table(table_name) table.wait_until_exists() cmp = materials_provider() table_info = TableInfo( name=table_name, primary_index=TableIndex(partition=table_index["partition"], sort=table_index.get("sort", None)), ) for plaintext_item in table_items: source_item = plaintext_item["item"] item_key = { table_info.primary_index.partition: source_item[table_info.primary_index.partition] } if table_info.primary_index.sort is not None: item_key[table_info.primary_index.sort] = source_item[ table_info.primary_index.sort] attribute_actions = plaintext_item["action"] e_table = EncryptedTable( table=table, materials_provider=cmp, table_info=table_info, attribute_actions=attribute_actions, auto_refresh_table_indexes=False, ) e_table.put_item(Item=ddb_to_dict(source_item)) retrieved_item = table.get_item(Key=ddb_to_dict(item_key)) parsed_item = dict_to_ddb(retrieved_item["Item"]) data_table_output[table_name].append(ddb_to_json(parsed_item)) finally: if table: table.delete() with open(ciphertext_file, "w", encoding="utf-8") as outfile: json.dump(data_table_output, outfile, indent=4) if metatable: # Assume exactly one entry in metastore table wrapping_key = dict_to_ddb(metatable.scan()["Items"][0]) metastore_output[metastore_info["table_name"]].append( ddb_to_json(wrapping_key)) metastore_ciphertext_file = _filename_from_uri( metastore_info["ciphertext"]) with open(metastore_ciphertext_file, "w", encoding="utf-8") as outfile: json.dump(metastore_output, outfile, indent=4) metatable.delete()
def _ddb_dict_ddb_transform_cycle(item): ddb_item = dict_to_ddb(item) cycled_item = ddb_to_dict(ddb_item) assert cycled_item == item
def test_serializable_item(item): ddb_json = dict_to_ddb(item) end_result = ddb_to_dict(ddb_json) assert end_result == item