Exemple #1
0
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
Exemple #2
0
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)
Exemple #3
0
    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,
        )
Exemple #4
0
    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,
        )
Exemple #5
0
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)
Exemple #7
0
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)
Exemple #8
0
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)
Exemple #9
0
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]
Exemple #10
0
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()
Exemple #11
0
def _ddb_dict_ddb_transform_cycle(item):
    ddb_item = dict_to_ddb(item)
    cycled_item = ddb_to_dict(ddb_item)
    assert cycled_item == item
Exemple #12
0
def test_serializable_item(item):
    ddb_json = dict_to_ddb(item)
    end_result = ddb_to_dict(ddb_json)
    assert end_result == item