Esempio n. 1
0
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: *"
    )
Esempio n. 2
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]
Esempio n. 3
0
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
Esempio n. 4
0
class KeyStore(ABC):
    @abstractmethod
    def _get_item(self, key_id: str):
        pass

    @abstractmethod
    def _put_item(self, key_id: str, encrypted_item):
        pass

    @abstractmethod
    def _delete_item(self, key_id: str) -> None:
        pass

    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 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 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 delete_main_key(self, key_id: str, allow_recovering=False) -> None:
        encrypted_item = self._get_item(key_id=key_id)

        if encrypted_item["on_hold"]:
            raise Exception("Deletion currently not possible.")

        self._delete_item(key_id=key_id)