From a56a19d17c22db302c53fae715d1b220a629beb3 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Wed, 25 Apr 2018 11:00:21 -0700 Subject: [PATCH] add wrapped and most recent provider examples and tests --- .../most_recent_provider_encrypted_table.py | 97 ++++++++++++++++++ examples/src/wrapped_rsa_encrypted_table.py | 99 +++++++++++++++++++ .../src/wrapped_symmetric_encrypted_table.py | 97 ++++++++++++++++++ ...most_recent_provider_encrypted_examples.py | 49 +++++++++ .../test/test_wrapped_encrypted_examples.py | 40 ++++++++ 5 files changed, 382 insertions(+) create mode 100644 examples/src/most_recent_provider_encrypted_table.py create mode 100644 examples/src/wrapped_rsa_encrypted_table.py create mode 100644 examples/src/wrapped_symmetric_encrypted_table.py create mode 100644 examples/test/test_most_recent_provider_encrypted_examples.py create mode 100644 examples/test/test_wrapped_encrypted_examples.py diff --git a/examples/src/most_recent_provider_encrypted_table.py b/examples/src/most_recent_provider_encrypted_table.py new file mode 100644 index 00000000..c75bb405 --- /dev/null +++ b/examples/src/most_recent_provider_encrypted_table.py @@ -0,0 +1,97 @@ +# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +"""Example showing use of MostRecentProvider backed by a MetaStore using an AWS KMS CMP, +with EncryptedTable. +""" +import boto3 +from boto3.dynamodb.types import Binary +from dynamodb_encryption_sdk.encrypted.table import EncryptedTable +from dynamodb_encryption_sdk.identifiers import CryptoAction +from dynamodb_encryption_sdk.material_providers.aws_kms import AwsKmsCryptographicMaterialsProvider +from dynamodb_encryption_sdk.material_providers.most_recent import MostRecentProvider +from dynamodb_encryption_sdk.material_providers.store.meta import MetaStore +from dynamodb_encryption_sdk.structures import AttributeActions + + +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) diff --git a/examples/src/wrapped_rsa_encrypted_table.py b/examples/src/wrapped_rsa_encrypted_table.py new file mode 100644 index 00000000..85816593 --- /dev/null +++ b/examples/src/wrapped_rsa_encrypted_table.py @@ -0,0 +1,99 @@ +# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +"""Example showing use of a RSA wrapped CMP with EncryptedTable.""" +import boto3 +from boto3.dynamodb.types import Binary +from dynamodb_encryption_sdk.delegated_keys.jce import JceNameLocalDelegatedKey +from dynamodb_encryption_sdk.encrypted.table import EncryptedTable +from dynamodb_encryption_sdk.identifiers import CryptoAction, EncryptionKeyType, KeyEncodingType +from dynamodb_encryption_sdk.material_providers.wrapped import WrappedCryptographicMaterialsProvider +from dynamodb_encryption_sdk.structures import AttributeActions + + +def encrypt_item(table_name, rsa_wrapping_private_key_bytes, rsa_signing_private_key_bytes): + """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 provided wrapping and signing keys. + # We show private keys used here, but public keys could be used as well, allowing + # only wrapping or signature verification. + wrapping_key = JceNameLocalDelegatedKey( + key=rsa_wrapping_private_key_bytes, + algorithm='RSA', + key_type=EncryptionKeyType.PRIVATE, + key_encoding=KeyEncodingType.DER + ) + signing_key = JceNameLocalDelegatedKey( + key=rsa_signing_private_key_bytes, + algorithm='SHA512withRSA', + key_type=EncryptionKeyType.PRIVATE, + key_encoding=KeyEncodingType.DER + ) + wrapped_cmp = WrappedCryptographicMaterialsProvider( + wrapping_key=wrapping_key, + unwrapping_key=wrapping_key, + signing_key=signing_key + ) + # 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=wrapped_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) diff --git a/examples/src/wrapped_symmetric_encrypted_table.py b/examples/src/wrapped_symmetric_encrypted_table.py new file mode 100644 index 00000000..6295a630 --- /dev/null +++ b/examples/src/wrapped_symmetric_encrypted_table.py @@ -0,0 +1,97 @@ +# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +"""Example showing use of a symmetric wrapped CMP with EncryptedTable.""" +import boto3 +from boto3.dynamodb.types import Binary +from dynamodb_encryption_sdk.delegated_keys.jce import JceNameLocalDelegatedKey +from dynamodb_encryption_sdk.encrypted.table import EncryptedTable +from dynamodb_encryption_sdk.identifiers import CryptoAction, EncryptionKeyType, KeyEncodingType +from dynamodb_encryption_sdk.material_providers.wrapped import WrappedCryptographicMaterialsProvider +from dynamodb_encryption_sdk.structures import AttributeActions + + +def encrypt_item(table_name, aes_wrapping_key_bytes, hmac_signing_key_bytes): + """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 provided wrapping and signing keys. + wrapping_key = JceNameLocalDelegatedKey( + key=aes_wrapping_key_bytes, + algorithm='AES', + key_type=EncryptionKeyType.SYMMETRIC, + key_encoding=KeyEncodingType.RAW + ) + signing_key = JceNameLocalDelegatedKey( + key=hmac_signing_key_bytes, + algorithm='HmacSHA512', + key_type=EncryptionKeyType.SYMMETRIC, + key_encoding=KeyEncodingType.RAW + ) + wrapped_cmp = WrappedCryptographicMaterialsProvider( + wrapping_key=wrapping_key, + unwrapping_key=wrapping_key, + signing_key=signing_key + ) + # 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=wrapped_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) diff --git a/examples/test/test_most_recent_provider_encrypted_examples.py b/examples/test/test_most_recent_provider_encrypted_examples.py new file mode 100644 index 00000000..5427f1ee --- /dev/null +++ b/examples/test/test_most_recent_provider_encrypted_examples.py @@ -0,0 +1,49 @@ +# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +"""Test most recent provider examples.""" +import os +import sys +sys.path.extend([ # noqa + os.sep.join([os.path.dirname(__file__), '..', '..', 'test', 'integration']), + os.sep.join([os.path.dirname(__file__), '..', 'src']) +]) +import uuid + +import boto3 +import pytest + +from dynamodb_encryption_sdk.material_providers.store.meta import MetaStore +import most_recent_provider_encrypted_table # noqa +from integration_test_utils import cmk_arn, ddb_table_name # noqa pylint: disable=unused-import + +pytestmark = [pytest.mark.examples] + + +def test_most_recent_encrypted_table(ddb_table_name, cmk_arn): + # define random new names for material and metastore table + meta_table_name = 'meta-table-{}'.format(uuid.uuid4()) + material_name = 'material-{}'.format(uuid.uuid4()) + + # create the metastore table + client = boto3.client('dynamodb') + MetaStore.create_table(client, meta_table_name, 10, 10) + waiter = client.get_waiter('table_exists') + waiter.wait(TableName=meta_table_name) + + # run the actual test + most_recent_provider_encrypted_table.encrypt_item(ddb_table_name, cmk_arn, meta_table_name, material_name) + + # clean up the meta store table + client.delete_table(TableName=meta_table_name) + waiter = client.get_waiter('table_not_exists') + waiter.wait(TableName=meta_table_name) diff --git a/examples/test/test_wrapped_encrypted_examples.py b/examples/test/test_wrapped_encrypted_examples.py new file mode 100644 index 00000000..9988e819 --- /dev/null +++ b/examples/test/test_wrapped_encrypted_examples.py @@ -0,0 +1,40 @@ +# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +"""Test ``wrapped_*_encrypted_*`` examples.""" +import os +import sys +sys.path.extend([ # noqa + os.sep.join([os.path.dirname(__file__), '..', '..', 'test', 'integration']), + os.sep.join([os.path.dirname(__file__), '..', 'src']) +]) + +import pytest + +from dynamodb_encryption_sdk.delegated_keys.jce import JceNameLocalDelegatedKey +import wrapped_rsa_encrypted_table # noqa +import wrapped_symmetric_encrypted_table # noqa +from integration_test_utils import ddb_table_name # noqa pylint: disable=unused-import + +pytestmark = [pytest.mark.examples] + + +def test_wrapped_rsa_encrypted_table(ddb_table_name): + wrapping_key_bytes = JceNameLocalDelegatedKey.generate('RSA', 4096).key + signing_key_bytes = JceNameLocalDelegatedKey.generate('SHA512withRSA', 4096).key + wrapped_rsa_encrypted_table.encrypt_item(ddb_table_name, wrapping_key_bytes, signing_key_bytes) + + +def test_wrapped_symmetric_encrypted_table(ddb_table_name): + wrapping_key_bytes = JceNameLocalDelegatedKey.generate('AES', 256).key + signing_key_bytes = JceNameLocalDelegatedKey.generate('HmacSHA512', 256).key + wrapped_symmetric_encrypted_table.encrypt_item(ddb_table_name, wrapping_key_bytes, signing_key_bytes)