From 0938325c6440b1ac0393f5a2cc2c1fc09e6dc8c7 Mon Sep 17 00:00:00 2001 From: John Walker Date: Mon, 15 Jul 2019 11:08:04 -0700 Subject: [PATCH 01/54] Update PR template --- .github/PULL_REQUEST_TEMPLATE.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index ab40d21d7..176df025b 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -4,3 +4,7 @@ By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. + +# Check any applicable: +- [ ] Were any files moved? Moving files changes their URL, which breaks all hyperlinks to the files. + From ee1b4cc11ca182211dcd932569b7ab64879f8245 Mon Sep 17 00:00:00 2001 From: Caitlin Tibbetts Date: Mon, 22 Jul 2019 14:51:25 -0700 Subject: [PATCH 02/54] Added a check for max_age being greater than 0 (#172) * Added a check for max_age being greater than 0 * Fixed flake8 by adding missing pydocstyle dependency * Added the dependency to decrypt_oracle as well * Added test for max_age<=0 ValueError * Updated test for max_age<=0.0 ValueError * Added negative test case --- decrypt_oracle/tox.ini | 1 + src/aws_encryption_sdk/materials_managers/caching.py | 3 +++ test/unit/test_material_managers_caching.py | 2 ++ tox.ini | 1 + 4 files changed, 7 insertions(+) diff --git a/decrypt_oracle/tox.ini b/decrypt_oracle/tox.ini index 60aea91c7..f0a7804e5 100644 --- a/decrypt_oracle/tox.ini +++ b/decrypt_oracle/tox.ini @@ -156,6 +156,7 @@ basepython = python3 deps = flake8 flake8-docstrings + pydocstyle<4.0.0 # https://github.com/JBKahn/flake8-print/pull/30 flake8-print>=3.1.0 commands = diff --git a/src/aws_encryption_sdk/materials_managers/caching.py b/src/aws_encryption_sdk/materials_managers/caching.py index b2bdcba9f..992a39a7a 100644 --- a/src/aws_encryption_sdk/materials_managers/caching.py +++ b/src/aws_encryption_sdk/materials_managers/caching.py @@ -108,6 +108,9 @@ def __attrs_post_init__(self): if self.max_bytes_encrypted > MAX_BYTES_PER_KEY: raise ValueError("max_bytes_encrypted cannot exceed {}".format(MAX_BYTES_PER_KEY)) + if self.max_age <= 0.0: + raise ValueError("max_age cannot be less than or equal to 0") + if self.backing_materials_manager is None: if self.master_key_provider is None: raise TypeError("Either backing_materials_manager or master_key_provider must be defined") diff --git a/test/unit/test_material_managers_caching.py b/test/unit/test_material_managers_caching.py index 426fe3348..833d6aa53 100644 --- a/test/unit/test_material_managers_caching.py +++ b/test/unit/test_material_managers_caching.py @@ -117,6 +117,8 @@ def test_mkp_to_default_cmm(mocker): dict(max_bytes_encrypted=MAX_BYTES_PER_KEY + 1), r"max_bytes_encrypted cannot exceed {}".format(MAX_BYTES_PER_KEY), ), + (dict(max_age=0.0), r"max_age cannot be less than or equal to 0"), + (dict(max_age=-1.0), r"max_age cannot be less than or equal to 0"), ), ) def test_invalid_values(invalid_kwargs, error_message): diff --git a/tox.ini b/tox.ini index e13ea2cb8..06564ef6a 100644 --- a/tox.ini +++ b/tox.ini @@ -117,6 +117,7 @@ basepython = python3 deps = flake8 flake8-docstrings + pydocstyle<4.0.0 # https://github.com/JBKahn/flake8-print/pull/30 flake8-print>=3.1.0 flake8-bugbear From 3b62bc3628d3e39c6704f49be95bb89ce7921395 Mon Sep 17 00:00:00 2001 From: Caitlin Tibbetts Date: Tue, 23 Jul 2019 09:48:43 -0700 Subject: [PATCH 03/54] Testing something, want AppVeyor to run --- tox.ini | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 06564ef6a..77b46ca70 100644 --- a/tox.ini +++ b/tox.ini @@ -56,6 +56,10 @@ commands = all: {[testenv:base-command]commands} test/ examples/test/ manual: {[testenv:base-command]commands} +[testenv:py34] +basepython = python34 +deps = c:\python35\Lib\runpy.py + # Verify that local tests work without environment variables present [testenv:nocmk] basepython = python3 @@ -252,7 +256,7 @@ commands = python setup.py check -r -s [testenv:bandit] basepython = python3 -deps = +deps = bandit>=1.5.1 commands = bandit -r src/aws_encryption_sdk/ From 626d5ba224c0cbd3f973f4d6bf4f01e3a543d6d9 Mon Sep 17 00:00:00 2001 From: Caitlin Tibbetts Date: Tue, 23 Jul 2019 09:52:40 -0700 Subject: [PATCH 04/54] Quick change --- tox.ini | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 77b46ca70..cf91274ff 100644 --- a/tox.ini +++ b/tox.ini @@ -57,8 +57,9 @@ commands = manual: {[testenv:base-command]commands} [testenv:py34] -basepython = python34 -deps = c:\python35\Lib\runpy.py +deps = + -rtest/requirements.txt + c:\python35\Lib\runpy.py # Verify that local tests work without environment variables present [testenv:nocmk] From 83f4ff8f9f211bbfd086fea5adf2fa68957d1332 Mon Sep 17 00:00:00 2001 From: Caitlin Tibbetts Date: Tue, 23 Jul 2019 10:39:04 -0700 Subject: [PATCH 05/54] Running AppVeyor --- setup.py | 3 +++ tox.ini | 7 +------ 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/setup.py b/setup.py index 1afa9d869..8bfa1613e 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,7 @@ """AWS Encryption SDK for Python.""" import os import re +import ast from setuptools import find_packages, setup @@ -57,3 +58,5 @@ def get_requirements(): "Topic :: Security :: Cryptography", ], ) +if not hasattr(ast, "MatMult"): + print("HERE") diff --git a/tox.ini b/tox.ini index cf91274ff..389aff177 100644 --- a/tox.ini +++ b/tox.ini @@ -55,12 +55,7 @@ commands = examples: {[testenv:base-command]commands} examples/test/ -m examples all: {[testenv:base-command]commands} test/ examples/test/ manual: {[testenv:base-command]commands} - -[testenv:py34] -deps = - -rtest/requirements.txt - c:\python35\Lib\runpy.py - + # Verify that local tests work without environment variables present [testenv:nocmk] basepython = python3 From 534e2251b5f983c73b31edc3bb42e85666f2c652 Mon Sep 17 00:00:00 2001 From: Caitlin Tibbetts Date: Wed, 24 Jul 2019 10:14:09 -0700 Subject: [PATCH 06/54] Added example for using multiple keyrings in multiple regions --- examples/src/multiple_kms_cmk_regions.py | 48 +++++++++++++++++++ .../test/test_i_multiple_kms_cmk_regions.py | 30 ++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 examples/src/multiple_kms_cmk_regions.py create mode 100644 examples/test/test_i_multiple_kms_cmk_regions.py diff --git a/examples/src/multiple_kms_cmk_regions.py b/examples/src/multiple_kms_cmk_regions.py new file mode 100644 index 000000000..ca8f2a2ca --- /dev/null +++ b/examples/src/multiple_kms_cmk_regions.py @@ -0,0 +1,48 @@ +# Copyright 2019 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 basic encryption and decryption of a value already in memory using multiple KMS CMKs in multiple regions.""" +import aws_encryption_sdk + + +def encrypt_decrypt(key_arn1, key_arn2, region_name1, region_name2, source_plaintext, botocore_session=None): + """Encrypts and then decrypts a string under one KMS customer master key (CMK). + + :param str key_arn: Amazon Resource Name (ARN) of the KMS CMK + :param bytes source_plaintext: Data to encrypt + :param botocore_session: existing botocore session instance + :type botocore_session: botocore.session.Session + """ + kwargs = dict(key_ids=[key_arn1, key_arn2], region_names=[region_name1, region_name2]) + + if botocore_session is not None: + kwargs["botocore_session"] = botocore_session + + # Create master key provider using the ARN of the key and the session (botocore_session) + kms_key_provider = aws_encryption_sdk.KMSMasterKeyProvider(**kwargs) + + # Encrypt the plaintext using the AWS Encryption SDK. It returns the encrypted message and the header + ciphertext, encrypted_message_header = aws_encryption_sdk.encrypt( + source=source_plaintext, key_provider=kms_key_provider + ) + + # Decrypt the encrypted message using the AWS Encryption SDK. It returns the decrypted message and the header + plaintext, decrypted_message_header = aws_encryption_sdk.decrypt(source=ciphertext, key_provider=kms_key_provider) + + # Check if the original message and the decrypted message are the same + assert source_plaintext == plaintext + + # Check if the headers of the encrypted message and decrypted message match + assert all( + pair in encrypted_message_header.encryption_context.items() + for pair in decrypted_message_header.encryption_context.items() + ) diff --git a/examples/test/test_i_multiple_kms_cmk_regions.py b/examples/test/test_i_multiple_kms_cmk_regions.py new file mode 100644 index 000000000..ffa82749c --- /dev/null +++ b/examples/test/test_i_multiple_kms_cmk_regions.py @@ -0,0 +1,30 @@ +# Copyright 2019 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. +"""Unit test suite for the encryption and decryption using multiple KMS CMKs in multiple regions example.""" + +import botocore.session +import pytest + +from ..src.one_kms_cmk import multiple_kms_cmk_regions +from .examples_test_utils import get_cmk_arn +from .examples_test_utils import static_plaintext + + +pytestmark = [pytest.mark.examples] + + +def test_one_kms_cmk(): + plaintext = static_plaintext + cmk_arn1 = get_cmk_arn() + cmk_arn2 = get_cmk_arn() + encrypt_decrypt(key_arn1=cmk_arn1, key_arn2=cmk_arn2, region_name1="us-west-1", region_name2="us-east-1", source_plaintext=plaintext, botocore_session=botocore.session.Session()) From 42e86ab600a1c790f559885a31ee1e9dfe3898a4 Mon Sep 17 00:00:00 2001 From: Caitlin Tibbetts Date: Wed, 24 Jul 2019 10:18:53 -0700 Subject: [PATCH 07/54] Undid something quickly --- setup.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/setup.py b/setup.py index 8bfa1613e..1afa9d869 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,6 @@ """AWS Encryption SDK for Python.""" import os import re -import ast from setuptools import find_packages, setup @@ -58,5 +57,3 @@ def get_requirements(): "Topic :: Security :: Cryptography", ], ) -if not hasattr(ast, "MatMult"): - print("HERE") From fabc5e3d4525e4deb089fdeeca203aabfa3bbac7 Mon Sep 17 00:00:00 2001 From: Caitlin Tibbetts Date: Wed, 24 Jul 2019 10:58:37 -0700 Subject: [PATCH 08/54] Fixed importerror --- examples/test/test_i_multiple_kms_cmk_regions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/test/test_i_multiple_kms_cmk_regions.py b/examples/test/test_i_multiple_kms_cmk_regions.py index ffa82749c..1f57dcae7 100644 --- a/examples/test/test_i_multiple_kms_cmk_regions.py +++ b/examples/test/test_i_multiple_kms_cmk_regions.py @@ -15,7 +15,7 @@ import botocore.session import pytest -from ..src.one_kms_cmk import multiple_kms_cmk_regions +from ..src.multiple_kms_cmk_regions import encrypt_decrypt from .examples_test_utils import get_cmk_arn from .examples_test_utils import static_plaintext From 30eab330b7b96751c9887262b2f85f88b8271fd9 Mon Sep 17 00:00:00 2001 From: Caitlin Tibbetts Date: Wed, 24 Jul 2019 11:33:12 -0700 Subject: [PATCH 09/54] Formatting fix --- examples/test/test_i_multiple_kms_cmk_regions.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/test/test_i_multiple_kms_cmk_regions.py b/examples/test/test_i_multiple_kms_cmk_regions.py index 1f57dcae7..c23beb444 100644 --- a/examples/test/test_i_multiple_kms_cmk_regions.py +++ b/examples/test/test_i_multiple_kms_cmk_regions.py @@ -27,4 +27,5 @@ def test_one_kms_cmk(): plaintext = static_plaintext cmk_arn1 = get_cmk_arn() cmk_arn2 = get_cmk_arn() - encrypt_decrypt(key_arn1=cmk_arn1, key_arn2=cmk_arn2, region_name1="us-west-1", region_name2="us-east-1", source_plaintext=plaintext, botocore_session=botocore.session.Session()) + encrypt_decrypt(key_arn1=cmk_arn1, key_arn2=cmk_arn2, region_name1="us-west-1", + region_name2="us-east-1", source_plaintext=plaintext, botocore_session=botocore.session.Session()) From 453b82da0ad2e2aa5a535101d1c72d9182e8b8b0 Mon Sep 17 00:00:00 2001 From: Caitlin Tibbetts Date: Thu, 25 Jul 2019 09:53:12 -0700 Subject: [PATCH 10/54] Update tox.ini --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 389aff177..47bf379d5 100644 --- a/tox.ini +++ b/tox.ini @@ -55,7 +55,7 @@ commands = examples: {[testenv:base-command]commands} examples/test/ -m examples all: {[testenv:base-command]commands} test/ examples/test/ manual: {[testenv:base-command]commands} - + # Verify that local tests work without environment variables present [testenv:nocmk] basepython = python3 From 22088903515894f2de1d4d0e3dd7077002b306b0 Mon Sep 17 00:00:00 2001 From: Caitlin Tibbetts Date: Thu, 25 Jul 2019 09:53:55 -0700 Subject: [PATCH 11/54] Update tox.ini --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 47bf379d5..06564ef6a 100644 --- a/tox.ini +++ b/tox.ini @@ -252,7 +252,7 @@ commands = python setup.py check -r -s [testenv:bandit] basepython = python3 -deps = +deps = bandit>=1.5.1 commands = bandit -r src/aws_encryption_sdk/ From d7243352d24dc14a37837f9bf3d8ef108184d116 Mon Sep 17 00:00:00 2001 From: Caitlin Tibbetts Date: Thu, 25 Jul 2019 11:51:51 -0700 Subject: [PATCH 12/54] Made some changes to the multiple_kms_cmk_regions example/test --- examples/src/multiple_kms_cmk_regions.py | 34 ++++++++++++++----- .../test/test_i_multiple_kms_cmk_regions.py | 11 +++--- 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/examples/src/multiple_kms_cmk_regions.py b/examples/src/multiple_kms_cmk_regions.py index ca8f2a2ca..84ec797fd 100644 --- a/examples/src/multiple_kms_cmk_regions.py +++ b/examples/src/multiple_kms_cmk_regions.py @@ -14,15 +14,27 @@ import aws_encryption_sdk -def encrypt_decrypt(key_arn1, key_arn2, region_name1, region_name2, source_plaintext, botocore_session=None): - """Encrypts and then decrypts a string under one KMS customer master key (CMK). +def encrypt(kms_key_provider, source_plaintext): + return aws_encryption_sdk.encrypt(source=source_plaintext, key_provider=kms_key_provider) - :param str key_arn: Amazon Resource Name (ARN) of the KMS CMK + +def decrypt(kms_key_provider, ciphertext): + return aws_encryption_sdk.decrypt(source=ciphertext, key_provider=kms_key_provider) + + +def multiple_kms_cmk_regions(key_arn1, key_arn2, source_plaintext, botocore_session=None): + """Encrypts and then decrypts a string under multiple KMS customer master keys (CMKs) in multiple regions. + + :param str key_arn1: Amazon Resource Name (ARN) of the KMS CMK + :param str key_arn2: Amazon Resource Name (ARN) of another KMS CMK :param bytes source_plaintext: Data to encrypt :param botocore_session: existing botocore session instance :type botocore_session: botocore.session.Session """ - kwargs = dict(key_ids=[key_arn1, key_arn2], region_names=[region_name1, region_name2]) + # Check that these keys are in different regions + assert not key_arn1[12:21] == key_arn2[12:21] + + kwargs = dict(key_ids=[key_arn1, key_arn2]) if botocore_session is not None: kwargs["botocore_session"] = botocore_session @@ -31,15 +43,19 @@ def encrypt_decrypt(key_arn1, key_arn2, region_name1, region_name2, source_plain kms_key_provider = aws_encryption_sdk.KMSMasterKeyProvider(**kwargs) # Encrypt the plaintext using the AWS Encryption SDK. It returns the encrypted message and the header - ciphertext, encrypted_message_header = aws_encryption_sdk.encrypt( - source=source_plaintext, key_provider=kms_key_provider - ) + ciphertext, encrypted_message_header = encrypt(kms_key_provider, source_plaintext) + + # Check if both key ARNs are in the message headers + assert len(encrypted_message_header.encryption_context.items()) == 2 # Decrypt the encrypted message using the AWS Encryption SDK. It returns the decrypted message and the header - plaintext, decrypted_message_header = aws_encryption_sdk.decrypt(source=ciphertext, key_provider=kms_key_provider) + # Either of our keys can be used to decrypt the message + plaintext1, decrypted_message_header1 = decrypt(aws_encryption_sdk.KMSMasterKeyProvider(key_arn1), ciphertext) + plaintext2, decrypted_message_header2 = decrypt(aws_encryption_sdk.KMSMasterKeyProvider(key_arn2), ciphertext) # Check if the original message and the decrypted message are the same - assert source_plaintext == plaintext + assert source_plaintext == plaintext1 + assert source_plaintext == plaintext2 # Check if the headers of the encrypted message and decrypted message match assert all( diff --git a/examples/test/test_i_multiple_kms_cmk_regions.py b/examples/test/test_i_multiple_kms_cmk_regions.py index c23beb444..93710f283 100644 --- a/examples/test/test_i_multiple_kms_cmk_regions.py +++ b/examples/test/test_i_multiple_kms_cmk_regions.py @@ -15,7 +15,7 @@ import botocore.session import pytest -from ..src.multiple_kms_cmk_regions import encrypt_decrypt +from ..src.multiple_kms_cmk_regions import multiple_kms_cmk_regions from .examples_test_utils import get_cmk_arn from .examples_test_utils import static_plaintext @@ -23,9 +23,8 @@ pytestmark = [pytest.mark.examples] -def test_one_kms_cmk(): +def test_multiple_kms_cmk_regions(): plaintext = static_plaintext - cmk_arn1 = get_cmk_arn() - cmk_arn2 = get_cmk_arn() - encrypt_decrypt(key_arn1=cmk_arn1, key_arn2=cmk_arn2, region_name1="us-west-1", - region_name2="us-east-1", source_plaintext=plaintext, botocore_session=botocore.session.Session()) + cmk_arn1 = "arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab" + cmk_arn2 = "arn:aws:kms:eu-central-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab" + multiple_kms_cmk_regions(cmk_arn1, cmk_arn2, source_plaintext=plaintext, botocore_session=botocore.session.Session()) From 306d1a9717f740116374ce75231bcd1f8073697e Mon Sep 17 00:00:00 2001 From: Caitlin Tibbetts Date: Thu, 25 Jul 2019 13:45:02 -0700 Subject: [PATCH 13/54] This is my next interation of the code for the example; however, I am still working on populating the tests correctly, so the CI will fail, but I tested the code with my own KMS CMK ARNs, so I know it will work once the tests are populated (working with Tejeswini on this) --- examples/src/multiple_kms_cmk_regions.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/examples/src/multiple_kms_cmk_regions.py b/examples/src/multiple_kms_cmk_regions.py index 84ec797fd..6a725379f 100644 --- a/examples/src/multiple_kms_cmk_regions.py +++ b/examples/src/multiple_kms_cmk_regions.py @@ -46,12 +46,12 @@ def multiple_kms_cmk_regions(key_arn1, key_arn2, source_plaintext, botocore_sess ciphertext, encrypted_message_header = encrypt(kms_key_provider, source_plaintext) # Check if both key ARNs are in the message headers - assert len(encrypted_message_header.encryption_context.items()) == 2 + assert len(encrypted_message_header.encrypted_data_keys) == 2 # Decrypt the encrypted message using the AWS Encryption SDK. It returns the decrypted message and the header # Either of our keys can be used to decrypt the message - plaintext1, decrypted_message_header1 = decrypt(aws_encryption_sdk.KMSMasterKeyProvider(key_arn1), ciphertext) - plaintext2, decrypted_message_header2 = decrypt(aws_encryption_sdk.KMSMasterKeyProvider(key_arn2), ciphertext) + plaintext1, decrypted_message_header1 = decrypt(aws_encryption_sdk.KMSMasterKeyProvider(**dict(key_ids=[key_arn1])), ciphertext) + plaintext2, decrypted_message_header2 = decrypt(aws_encryption_sdk.KMSMasterKeyProvider(**dict(key_ids=[key_arn2])), ciphertext) # Check if the original message and the decrypted message are the same assert source_plaintext == plaintext1 @@ -60,5 +60,9 @@ def multiple_kms_cmk_regions(key_arn1, key_arn2, source_plaintext, botocore_sess # Check if the headers of the encrypted message and decrypted message match assert all( pair in encrypted_message_header.encryption_context.items() - for pair in decrypted_message_header.encryption_context.items() + for pair in decrypted_message_header1.encryption_context.items() + ) + assert all( + pair in encrypted_message_header.encryption_context.items() + for pair in decrypted_message_header2.encryption_context.items() ) From bde7a56bb614b7ee76f436baf4ae93d8150d14b0 Mon Sep 17 00:00:00 2001 From: Caitlin Tibbetts Date: Fri, 26 Jul 2019 09:57:54 -0700 Subject: [PATCH 14/54] Changed the example to test two CMKs in the same region until Issue #178 is cleared up --- examples/src/multiple_kms_cmk_regions.py | 68 ------------------- .../test/test_i_multiple_kms_cmk_regions.py | 30 -------- 2 files changed, 98 deletions(-) delete mode 100644 examples/src/multiple_kms_cmk_regions.py delete mode 100644 examples/test/test_i_multiple_kms_cmk_regions.py diff --git a/examples/src/multiple_kms_cmk_regions.py b/examples/src/multiple_kms_cmk_regions.py deleted file mode 100644 index 6a725379f..000000000 --- a/examples/src/multiple_kms_cmk_regions.py +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright 2019 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 basic encryption and decryption of a value already in memory using multiple KMS CMKs in multiple regions.""" -import aws_encryption_sdk - - -def encrypt(kms_key_provider, source_plaintext): - return aws_encryption_sdk.encrypt(source=source_plaintext, key_provider=kms_key_provider) - - -def decrypt(kms_key_provider, ciphertext): - return aws_encryption_sdk.decrypt(source=ciphertext, key_provider=kms_key_provider) - - -def multiple_kms_cmk_regions(key_arn1, key_arn2, source_plaintext, botocore_session=None): - """Encrypts and then decrypts a string under multiple KMS customer master keys (CMKs) in multiple regions. - - :param str key_arn1: Amazon Resource Name (ARN) of the KMS CMK - :param str key_arn2: Amazon Resource Name (ARN) of another KMS CMK - :param bytes source_plaintext: Data to encrypt - :param botocore_session: existing botocore session instance - :type botocore_session: botocore.session.Session - """ - # Check that these keys are in different regions - assert not key_arn1[12:21] == key_arn2[12:21] - - kwargs = dict(key_ids=[key_arn1, key_arn2]) - - if botocore_session is not None: - kwargs["botocore_session"] = botocore_session - - # Create master key provider using the ARN of the key and the session (botocore_session) - kms_key_provider = aws_encryption_sdk.KMSMasterKeyProvider(**kwargs) - - # Encrypt the plaintext using the AWS Encryption SDK. It returns the encrypted message and the header - ciphertext, encrypted_message_header = encrypt(kms_key_provider, source_plaintext) - - # Check if both key ARNs are in the message headers - assert len(encrypted_message_header.encrypted_data_keys) == 2 - - # Decrypt the encrypted message using the AWS Encryption SDK. It returns the decrypted message and the header - # Either of our keys can be used to decrypt the message - plaintext1, decrypted_message_header1 = decrypt(aws_encryption_sdk.KMSMasterKeyProvider(**dict(key_ids=[key_arn1])), ciphertext) - plaintext2, decrypted_message_header2 = decrypt(aws_encryption_sdk.KMSMasterKeyProvider(**dict(key_ids=[key_arn2])), ciphertext) - - # Check if the original message and the decrypted message are the same - assert source_plaintext == plaintext1 - assert source_plaintext == plaintext2 - - # Check if the headers of the encrypted message and decrypted message match - assert all( - pair in encrypted_message_header.encryption_context.items() - for pair in decrypted_message_header1.encryption_context.items() - ) - assert all( - pair in encrypted_message_header.encryption_context.items() - for pair in decrypted_message_header2.encryption_context.items() - ) diff --git a/examples/test/test_i_multiple_kms_cmk_regions.py b/examples/test/test_i_multiple_kms_cmk_regions.py deleted file mode 100644 index 93710f283..000000000 --- a/examples/test/test_i_multiple_kms_cmk_regions.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2019 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. -"""Unit test suite for the encryption and decryption using multiple KMS CMKs in multiple regions example.""" - -import botocore.session -import pytest - -from ..src.multiple_kms_cmk_regions import multiple_kms_cmk_regions -from .examples_test_utils import get_cmk_arn -from .examples_test_utils import static_plaintext - - -pytestmark = [pytest.mark.examples] - - -def test_multiple_kms_cmk_regions(): - plaintext = static_plaintext - cmk_arn1 = "arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab" - cmk_arn2 = "arn:aws:kms:eu-central-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab" - multiple_kms_cmk_regions(cmk_arn1, cmk_arn2, source_plaintext=plaintext, botocore_session=botocore.session.Session()) From b7e9dd1f663a462d2c07f738acf2478e559bd049 Mon Sep 17 00:00:00 2001 From: Caitlin Tibbetts Date: Fri, 26 Jul 2019 10:59:46 -0700 Subject: [PATCH 15/54] Found out how to make a new valid test key, so now there are two valid test keys in different regions for this example --- examples/src/multiple_kms_cmk_regions.py | 67 +++++++++++++++++++ .../test/test_i_multiple_kms_cmk_regions.py | 29 ++++++++ 2 files changed, 96 insertions(+) create mode 100644 examples/src/multiple_kms_cmk_regions.py create mode 100644 examples/test/test_i_multiple_kms_cmk_regions.py diff --git a/examples/src/multiple_kms_cmk_regions.py b/examples/src/multiple_kms_cmk_regions.py new file mode 100644 index 000000000..618d43631 --- /dev/null +++ b/examples/src/multiple_kms_cmk_regions.py @@ -0,0 +1,67 @@ +# Copyright 2019 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 basic encryption and decryption of a value already in memory using multiple KMS CMKs in multiple regions.""" +import aws_encryption_sdk + + +def encrypt(kms_key_provider, source_plaintext): + return aws_encryption_sdk.encrypt(source=source_plaintext, key_provider=kms_key_provider) + + +def decrypt(kms_key_provider, ciphertext): + return aws_encryption_sdk.decrypt(source=ciphertext, key_provider=kms_key_provider) + +def multiple_kms_cmk_regions(key_arn1, key_arn2, source_plaintext, botocore_session=None): + """Encrypts and then decrypts a string under multiple KMS customer master keys (CMKs) in multiple regions. + + :param str key_arn1: Amazon Resource Name (ARN) of the KMS CMK + :param str key_arn2: Amazon Resource Name (ARN) of another KMS CMK + :param bytes source_plaintext: Data to encrypt + :param botocore_session: existing botocore session instance + :type botocore_session: botocore.session.Session + """ + # Check that these keys are in different regions + assert not key_arn1[12:21] == key_arn2[12:21] + + kwargs = dict(key_ids=[key_arn1, key_arn2]) + + if botocore_session is not None: + kwargs["botocore_session"] = botocore_session + + # Create master key provider using the ARN of the key and the session (botocore_session) + kms_key_provider = aws_encryption_sdk.KMSMasterKeyProvider(**kwargs) + + # Encrypt the plaintext using the AWS Encryption SDK. It returns the encrypted message and the header + ciphertext, encrypted_message_header = encrypt(kms_key_provider, source_plaintext) + + # Check if both key ARNs are in the message headers + assert len(encrypted_message_header.encrypted_data_keys) == 2 + + # Decrypt the encrypted message using the AWS Encryption SDK. It returns the decrypted message and the header + # Either of our keys can be used to decrypt the message + plaintext1, decrypted_message_header1 = decrypt(aws_encryption_sdk.KMSMasterKeyProvider(**dict(key_ids=[key_arn1])), ciphertext) + plaintext2, decrypted_message_header2 = decrypt(aws_encryption_sdk.KMSMasterKeyProvider(**dict(key_ids=[key_arn2])), ciphertext) + + # Check if the original message and the decrypted message are the same + assert source_plaintext == plaintext1 + assert source_plaintext == plaintext2 + + # Check if the headers of the encrypted message and decrypted message match + assert all( + pair in encrypted_message_header.encryption_context.items() + for pair in decrypted_message_header1.encryption_context.items() + ) + assert all( + pair in encrypted_message_header.encryption_context.items() + for pair in decrypted_message_header2.encryption_context.items() + ) diff --git a/examples/test/test_i_multiple_kms_cmk_regions.py b/examples/test/test_i_multiple_kms_cmk_regions.py new file mode 100644 index 000000000..48869c8e5 --- /dev/null +++ b/examples/test/test_i_multiple_kms_cmk_regions.py @@ -0,0 +1,29 @@ +# Copyright 2019 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. +"""Unit test suite for the encryption and decryption using multiple KMS CMKs in multiple regions example.""" + +import botocore.session +import pytest + +from ..src.multiple_kms_cmk_regions import multiple_kms_cmk_regions +#from .examples_test_utils import get_cmk_arn +from .examples_test_utils import static_plaintext + + +pytestmark = [pytest.mark.examples] + +def test_multiple_kms_cmk_regions(): + plaintext = static_plaintext + cmk_arn1 = "arn:aws:kms:us-west-1:658956600833:alias/EncryptDecrypt" + cmk_arn2 = "arn:aws:kms:us-west-2:658956600833:alias/EncryptDecrypt" + multiple_kms_cmk_regions(cmk_arn1, cmk_arn2, source_plaintext=plaintext, botocore_session=botocore.session.Session()) From 4d8c7a0502273cabea0197c8d4ffa359bb0af98b Mon Sep 17 00:00:00 2001 From: Tibbetts Date: Fri, 26 Jul 2019 11:29:00 -0700 Subject: [PATCH 16/54] Ran autoformat --- examples/src/multiple_kms_cmk_regions.py | 9 +++++++-- examples/test/test_i_multiple_kms_cmk_regions.py | 8 ++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/examples/src/multiple_kms_cmk_regions.py b/examples/src/multiple_kms_cmk_regions.py index 618d43631..e0dae1976 100644 --- a/examples/src/multiple_kms_cmk_regions.py +++ b/examples/src/multiple_kms_cmk_regions.py @@ -21,6 +21,7 @@ def encrypt(kms_key_provider, source_plaintext): def decrypt(kms_key_provider, ciphertext): return aws_encryption_sdk.decrypt(source=ciphertext, key_provider=kms_key_provider) + def multiple_kms_cmk_regions(key_arn1, key_arn2, source_plaintext, botocore_session=None): """Encrypts and then decrypts a string under multiple KMS customer master keys (CMKs) in multiple regions. @@ -49,8 +50,12 @@ def multiple_kms_cmk_regions(key_arn1, key_arn2, source_plaintext, botocore_sess # Decrypt the encrypted message using the AWS Encryption SDK. It returns the decrypted message and the header # Either of our keys can be used to decrypt the message - plaintext1, decrypted_message_header1 = decrypt(aws_encryption_sdk.KMSMasterKeyProvider(**dict(key_ids=[key_arn1])), ciphertext) - plaintext2, decrypted_message_header2 = decrypt(aws_encryption_sdk.KMSMasterKeyProvider(**dict(key_ids=[key_arn2])), ciphertext) + plaintext1, decrypted_message_header1 = decrypt( + aws_encryption_sdk.KMSMasterKeyProvider(**dict(key_ids=[key_arn1])), ciphertext + ) + plaintext2, decrypted_message_header2 = decrypt( + aws_encryption_sdk.KMSMasterKeyProvider(**dict(key_ids=[key_arn2])), ciphertext + ) # Check if the original message and the decrypted message are the same assert source_plaintext == plaintext1 diff --git a/examples/test/test_i_multiple_kms_cmk_regions.py b/examples/test/test_i_multiple_kms_cmk_regions.py index 48869c8e5..9ecda6244 100644 --- a/examples/test/test_i_multiple_kms_cmk_regions.py +++ b/examples/test/test_i_multiple_kms_cmk_regions.py @@ -16,14 +16,18 @@ import pytest from ..src.multiple_kms_cmk_regions import multiple_kms_cmk_regions -#from .examples_test_utils import get_cmk_arn + +# from .examples_test_utils import get_cmk_arn from .examples_test_utils import static_plaintext pytestmark = [pytest.mark.examples] + def test_multiple_kms_cmk_regions(): plaintext = static_plaintext cmk_arn1 = "arn:aws:kms:us-west-1:658956600833:alias/EncryptDecrypt" cmk_arn2 = "arn:aws:kms:us-west-2:658956600833:alias/EncryptDecrypt" - multiple_kms_cmk_regions(cmk_arn1, cmk_arn2, source_plaintext=plaintext, botocore_session=botocore.session.Session()) + multiple_kms_cmk_regions( + cmk_arn1, cmk_arn2, source_plaintext=plaintext, botocore_session=botocore.session.Session() + ) From 1fdbb32b5fa9c783ffdedadeac4e4d9ad6433d69 Mon Sep 17 00:00:00 2001 From: Caitlin Tibbetts Date: Fri, 26 Jul 2019 13:05:31 -0700 Subject: [PATCH 17/54] Added some docstrings --- examples/src/multiple_kms_cmk_regions.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/src/multiple_kms_cmk_regions.py b/examples/src/multiple_kms_cmk_regions.py index e0dae1976..31b25ed45 100644 --- a/examples/src/multiple_kms_cmk_regions.py +++ b/examples/src/multiple_kms_cmk_regions.py @@ -15,10 +15,12 @@ def encrypt(kms_key_provider, source_plaintext): + """Encrypts source_plaintext with the key(s) in kms_key_provider""" return aws_encryption_sdk.encrypt(source=source_plaintext, key_provider=kms_key_provider) def decrypt(kms_key_provider, ciphertext): + """Decrypts ciphertext with the key(s) in kms_key_provider""" return aws_encryption_sdk.decrypt(source=ciphertext, key_provider=kms_key_provider) From d3240ebcd383f99092573c4093d6c5c2bbc4be7b Mon Sep 17 00:00:00 2001 From: Caitlin Tibbetts Date: Fri, 26 Jul 2019 13:22:41 -0700 Subject: [PATCH 18/54] Formatting will be the death of me --- examples/src/multiple_kms_cmk_regions.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/src/multiple_kms_cmk_regions.py b/examples/src/multiple_kms_cmk_regions.py index 31b25ed45..047a16521 100644 --- a/examples/src/multiple_kms_cmk_regions.py +++ b/examples/src/multiple_kms_cmk_regions.py @@ -10,7 +10,10 @@ # 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 basic encryption and decryption of a value already in memory using multiple KMS CMKs in multiple regions.""" +""" +Example showing basic encryption and decryption of a value already in memory +using multiple KMS CMKs in multiple regions. +""" import aws_encryption_sdk From 4eb5fdee31244d25091b04e5bece3e1849f26a0a Mon Sep 17 00:00:00 2001 From: Caitlin Tibbetts Date: Fri, 26 Jul 2019 14:17:29 -0700 Subject: [PATCH 19/54] Used correct keys in test --- examples/test/test_i_multiple_kms_cmk_regions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/test/test_i_multiple_kms_cmk_regions.py b/examples/test/test_i_multiple_kms_cmk_regions.py index 9ecda6244..c2722d9b1 100644 --- a/examples/test/test_i_multiple_kms_cmk_regions.py +++ b/examples/test/test_i_multiple_kms_cmk_regions.py @@ -26,8 +26,8 @@ def test_multiple_kms_cmk_regions(): plaintext = static_plaintext - cmk_arn1 = "arn:aws:kms:us-west-1:658956600833:alias/EncryptDecrypt" - cmk_arn2 = "arn:aws:kms:us-west-2:658956600833:alias/EncryptDecrypt" + cmk_arn1 = "arn:aws:kms:us-west-2:658956600833:alias/EncryptDecrypt" + cmk_arn2 = "arn:aws:kms:eu-central-1:658956600833:alias/EncryptDecrypt" multiple_kms_cmk_regions( cmk_arn1, cmk_arn2, source_plaintext=plaintext, botocore_session=botocore.session.Session() ) From bb6c650e08f850e5b965b18f21495da9674f3f53 Mon Sep 17 00:00:00 2001 From: Caitlin Tibbetts Date: Mon, 29 Jul 2019 11:30:52 -0700 Subject: [PATCH 20/54] Updated some comments --- examples/src/multiple_kms_cmk_regions.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/src/multiple_kms_cmk_regions.py b/examples/src/multiple_kms_cmk_regions.py index 047a16521..7a7a9ba9a 100644 --- a/examples/src/multiple_kms_cmk_regions.py +++ b/examples/src/multiple_kms_cmk_regions.py @@ -44,13 +44,13 @@ def multiple_kms_cmk_regions(key_arn1, key_arn2, source_plaintext, botocore_sess if botocore_session is not None: kwargs["botocore_session"] = botocore_session - # Create master key provider using the ARN of the key and the session (botocore_session) + # Create master key provider using the ARNs of the keys and the session (botocore_session) kms_key_provider = aws_encryption_sdk.KMSMasterKeyProvider(**kwargs) # Encrypt the plaintext using the AWS Encryption SDK. It returns the encrypted message and the header ciphertext, encrypted_message_header = encrypt(kms_key_provider, source_plaintext) - # Check if both key ARNs are in the message headers + # Check that both key ARNs are in the message headers assert len(encrypted_message_header.encrypted_data_keys) == 2 # Decrypt the encrypted message using the AWS Encryption SDK. It returns the decrypted message and the header @@ -62,11 +62,11 @@ def multiple_kms_cmk_regions(key_arn1, key_arn2, source_plaintext, botocore_sess aws_encryption_sdk.KMSMasterKeyProvider(**dict(key_ids=[key_arn2])), ciphertext ) - # Check if the original message and the decrypted message are the same + # Check that the original message and the decrypted message are the same assert source_plaintext == plaintext1 assert source_plaintext == plaintext2 - # Check if the headers of the encrypted message and decrypted message match + # Check that the headers of the encrypted message and decrypted message match assert all( pair in encrypted_message_header.encryption_context.items() for pair in decrypted_message_header1.encryption_context.items() From 1de8d5cf6aaabb7ea4d5de20e5defaafdc61b614 Mon Sep 17 00:00:00 2001 From: Ryan Ragona Date: Fri, 2 Aug 2019 07:33:25 -0800 Subject: [PATCH 21/54] Fixed KMS master key provider tests when default AWS region is configured (#179) * Fixed KMS master key provider tests for users who have their default AWS region configured * created fixture for botocore session with no region set * add auto-used fixture in KMS master key provider unit tests to test against both with and without default region --- .../test_providers_kms_master_key_provider.py | 33 ++++++++++++++----- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/test/unit/test_providers_kms_master_key_provider.py b/test/unit/test_providers_kms_master_key_provider.py index 48802a36f..9a122e622 100644 --- a/test/unit/test_providers_kms_master_key_provider.py +++ b/test/unit/test_providers_kms_master_key_provider.py @@ -12,6 +12,7 @@ # language governing permissions and limitations under the License. """Unit test suite from aws_encryption_sdk.key_providers.kms.KMSMasterKeyProvider""" import botocore.client +import botocore.session import pytest from mock import ANY, MagicMock, call, patch, sentinel @@ -22,6 +23,19 @@ pytestmark = [pytest.mark.unit, pytest.mark.local] +@pytest.fixture(autouse=True, params=[True, False], ids=["default region", "no default region"]) +def patch_default_region(request, monkeypatch): + """Run all tests in this module both with a default region set and no default region set. + + This ensures that we do not regress on default region handling. + https://github.com/aws/aws-encryption-sdk-python/issues/31 + """ + if request.param: + monkeypatch.setenv("AWS_DEFAULT_REGION", "us-west-2") + else: + monkeypatch.delenv("AWS_DEFAULT_REGION", raising=False) + + def test_init_with_regionless_key_ids_and_region_names(): key_ids = ("alias/key_1",) region_names = ("test-region-1",) @@ -32,6 +46,7 @@ def test_init_with_regionless_key_ids_and_region_names(): class TestKMSMasterKeyProvider(object): @pytest.fixture(autouse=True) def apply_fixtures(self): + self.botocore_no_region_session = botocore.session.Session(session_vars={"region": (None, None, None, None)}) self.mock_botocore_session_patcher = patch("aws_encryption_sdk.key_providers.kms.botocore.session.Session") self.mock_botocore_session = self.mock_botocore_session_patcher.start() self.mock_boto3_session_patcher = patch("aws_encryption_sdk.key_providers.kms.boto3.session.Session") @@ -69,7 +84,7 @@ def test_init_with_region_names(self, mock_add_clients): @patch("aws_encryption_sdk.key_providers.kms.KMSMasterKeyProvider.add_regional_client") def test_init_with_default_region_found(self, mock_add_regional_client): - test = KMSMasterKeyProvider() + test = KMSMasterKeyProvider(botocore_session=self.botocore_no_region_session) assert test.default_region is None with patch.object( test.config.botocore_session, "get_config_variable", return_value=sentinel.default_region @@ -77,11 +92,11 @@ def test_init_with_default_region_found(self, mock_add_regional_client): test._process_config() mock_get_config.assert_called_once_with("region") assert test.default_region is sentinel.default_region - mock_add_regional_client.assert_called_once_with(sentinel.default_region) + mock_add_regional_client.assert_called_with(sentinel.default_region) @patch("aws_encryption_sdk.key_providers.kms.KMSMasterKeyProvider.add_regional_client") def test_init_with_default_region_not_found(self, mock_add_regional_client): - test = KMSMasterKeyProvider() + test = KMSMasterKeyProvider(botocore_session=self.botocore_no_region_session) assert test.default_region is None with patch.object(test.config.botocore_session, "get_config_variable", return_value=None) as mock_get_config: test._process_config() @@ -93,12 +108,12 @@ def test_add_regional_client_new(self): test = KMSMasterKeyProvider() test._regional_clients = {} test.add_regional_client("ex_region_name") - self.mock_boto3_session.assert_called_once_with(region_name="ex_region_name", botocore_session=ANY) - self.mock_boto3_session_instance.client.assert_called_once_with("kms", config=test._user_agent_adding_config) + self.mock_boto3_session.assert_called_with(region_name="ex_region_name", botocore_session=ANY) + self.mock_boto3_session_instance.client.assert_called_with("kms", config=test._user_agent_adding_config) assert test._regional_clients["ex_region_name"] is self.mock_boto3_client_instance def test_add_regional_client_exists(self): - test = KMSMasterKeyProvider() + test = KMSMasterKeyProvider(botocore_session=self.botocore_no_region_session) test._regional_clients["ex_region_name"] = sentinel.existing_client test.add_regional_client("ex_region_name") assert not self.mock_boto3_session.called @@ -114,7 +129,7 @@ def test_client_valid_region_name(self, mock_add_client): test = KMSMasterKeyProvider() test._regional_clients["us-east-1"] = self.mock_boto3_client_instance client = test._client("arn:aws:kms:us-east-1:222222222222:key/aaaaaaaa-1111-2222-3333-bbbbbbbbbbbb") - mock_add_client.assert_called_once_with("us-east-1") + mock_add_client.assert_called_with("us-east-1") assert client is self.mock_boto3_client_instance @patch("aws_encryption_sdk.key_providers.kms.KMSMasterKeyProvider.add_regional_client") @@ -124,10 +139,10 @@ def test_client_no_region_name_with_default(self, mock_add_client): test._regional_clients[sentinel.default_region] = sentinel.default_client client = test._client("") assert client is sentinel.default_client - mock_add_client.assert_called_once_with(sentinel.default_region) + mock_add_client.assert_called_with(sentinel.default_region) def test_client_no_region_name_without_default(self): - test = KMSMasterKeyProvider() + test = KMSMasterKeyProvider(botocore_session=self.botocore_no_region_session) with pytest.raises(UnknownRegionError) as excinfo: test._client("") excinfo.match("No default region found and no region determinable from key id: *") From baf1164c13f5231f3532cc2b6e2b59f163357cca Mon Sep 17 00:00:00 2001 From: Caitlin Tibbetts Date: Fri, 2 Aug 2019 11:49:05 -0700 Subject: [PATCH 22/54] Wrote example and test for using one kms cmk with an unsigned algorithm --- examples/src/one_kms_cmk_unsigned.py | 51 ++++++++++++++++++++ examples/test/test_i_one_kms_cmk_unsigned.py | 29 +++++++++++ 2 files changed, 80 insertions(+) create mode 100644 examples/src/one_kms_cmk_unsigned.py create mode 100644 examples/test/test_i_one_kms_cmk_unsigned.py diff --git a/examples/src/one_kms_cmk_unsigned.py b/examples/src/one_kms_cmk_unsigned.py new file mode 100644 index 000000000..235a8ac06 --- /dev/null +++ b/examples/src/one_kms_cmk_unsigned.py @@ -0,0 +1,51 @@ +# Copyright 2019 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 basic encryption and decryption of a value already in memory +using one KMS CMK with an unsigned algorithm. +""" +import aws_encryption_sdk +from aws_encryption_sdk.identifiers import Algorithm + + +def encrypt_decrypt(key_arn, source_plaintext, botocore_session=None): + """Encrypts and then decrypts a string under one KMS customer master key (CMK) with an unsigned algorithm. + + :param str key_arn: Amazon Resource Name (ARN) of the KMS CMK + :param bytes source_plaintext: Data to encrypt + :param botocore_session: existing botocore session instance + :type botocore_session: botocore.session.Session + """ + kwargs = dict(key_ids=[key_arn]) + + if botocore_session is not None: + kwargs["botocore_session"] = botocore_session + + # Create master key provider using the ARN of the key and the session (botocore_session) + kms_key_provider = aws_encryption_sdk.KMSMasterKeyProvider(**kwargs) + + # Encrypt the plaintext using the AWS Encryption SDK. It returns the encrypted message and the header + ciphertext, encrypted_message_header = aws_encryption_sdk.encrypt( + algorithm=Algorithm.AES_256_GCM_IV12_TAG16, source=source_plaintext, key_provider=kms_key_provider + ) + + # Decrypt the encrypted message using the AWS Encryption SDK. It returns the decrypted message and the header + plaintext, decrypted_message_header = aws_encryption_sdk.decrypt(source=ciphertext, key_provider=kms_key_provider) + + # Check if the original message and the decrypted message are the same + assert source_plaintext == plaintext + + # Check if the headers of the encrypted message and decrypted message match + assert all( + pair in encrypted_message_header.encryption_context.items() + for pair in decrypted_message_header.encryption_context.items() + ) diff --git a/examples/test/test_i_one_kms_cmk_unsigned.py b/examples/test/test_i_one_kms_cmk_unsigned.py new file mode 100644 index 000000000..8a2758c96 --- /dev/null +++ b/examples/test/test_i_one_kms_cmk_unsigned.py @@ -0,0 +1,29 @@ +# Copyright 2019 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. +"""Unit test suite for the encryption and decryption using one KMS CMK with an unsigned algorithm example.""" + +import botocore.session +import pytest + +from ..src.one_kms_cmk_unsigned import encrypt_decrypt +from .examples_test_utils import get_cmk_arn +from .examples_test_utils import static_plaintext + + +pytestmark = [pytest.mark.examples] + + +def test_one_kms_cmk_unsigned(): + plaintext = static_plaintext + cmk_arn = get_cmk_arn() + encrypt_decrypt(key_arn=cmk_arn, source_plaintext=plaintext, botocore_session=botocore.session.Session()) From 9e5fcd44f2b360ef70cbf2c4cc71fbf2642fb336 Mon Sep 17 00:00:00 2001 From: Caitlin Tibbetts Date: Mon, 5 Aug 2019 14:55:02 -0700 Subject: [PATCH 23/54] Update the integration tests --- examples/src/multiple_kms_cmk_regions.py | 43 ++++++++----------- .../test/test_i_multiple_kms_cmk_regions.py | 8 ++-- test/integration/README.rst | 3 +- test/integration/integration_test_utils.py | 21 +++++++++ 4 files changed, 46 insertions(+), 29 deletions(-) diff --git a/examples/src/multiple_kms_cmk_regions.py b/examples/src/multiple_kms_cmk_regions.py index 7a7a9ba9a..f036e9121 100644 --- a/examples/src/multiple_kms_cmk_regions.py +++ b/examples/src/multiple_kms_cmk_regions.py @@ -15,37 +15,29 @@ using multiple KMS CMKs in multiple regions. """ import aws_encryption_sdk +from aws_encryption_sdk.key_providers.kms import KMSMasterKey, KMSMasterKeyProvider +from aws_encryption_sdk.internal.crypto.encryption import encrypt, decrypt -def encrypt(kms_key_provider, source_plaintext): - """Encrypts source_plaintext with the key(s) in kms_key_provider""" - return aws_encryption_sdk.encrypt(source=source_plaintext, key_provider=kms_key_provider) - - -def decrypt(kms_key_provider, ciphertext): - """Decrypts ciphertext with the key(s) in kms_key_provider""" - return aws_encryption_sdk.decrypt(source=ciphertext, key_provider=kms_key_provider) - - -def multiple_kms_cmk_regions(key_arn1, key_arn2, source_plaintext, botocore_session=None): +def multiple_kms_cmk_regions(key_arn_1, key_arn_2, source_plaintext, botocore_session=None): """Encrypts and then decrypts a string under multiple KMS customer master keys (CMKs) in multiple regions. - :param str key_arn1: Amazon Resource Name (ARN) of the KMS CMK - :param str key_arn2: Amazon Resource Name (ARN) of another KMS CMK + :param str key_arn_1: Amazon Resource Name (ARN) of the KMS CMK + :param str key_arn_2: Amazon Resource Name (ARN) of another KMS CMK :param bytes source_plaintext: Data to encrypt :param botocore_session: existing botocore session instance :type botocore_session: botocore.session.Session """ # Check that these keys are in different regions - assert not key_arn1[12:21] == key_arn2[12:21] + assert not key_arn_1.split(":")[3] == key_arn_2.split(":")[3] - kwargs = dict(key_ids=[key_arn1, key_arn2]) + kwargs = dict(key_ids=[key_arn_1, key_arn_2]) if botocore_session is not None: kwargs["botocore_session"] = botocore_session # Create master key provider using the ARNs of the keys and the session (botocore_session) - kms_key_provider = aws_encryption_sdk.KMSMasterKeyProvider(**kwargs) + kms_key_provider = KMSMasterKeyProvider(**kwargs) # Encrypt the plaintext using the AWS Encryption SDK. It returns the encrypted message and the header ciphertext, encrypted_message_header = encrypt(kms_key_provider, source_plaintext) @@ -55,23 +47,26 @@ def multiple_kms_cmk_regions(key_arn1, key_arn2, source_plaintext, botocore_sess # Decrypt the encrypted message using the AWS Encryption SDK. It returns the decrypted message and the header # Either of our keys can be used to decrypt the message - plaintext1, decrypted_message_header1 = decrypt( - aws_encryption_sdk.KMSMasterKeyProvider(**dict(key_ids=[key_arn1])), ciphertext + plaintext_1, decrypted_message_header_1 = decrypt( + KMSMasterKey(key_id=key_arn_1), ciphertext ) - plaintext2, decrypted_message_header2 = decrypt( - aws_encryption_sdk.KMSMasterKeyProvider(**dict(key_ids=[key_arn2])), ciphertext + plaintext_2, decrypted_message_header_2 = decrypt( + KMSMasterKey(key_id=key_arn_2), ciphertext ) # Check that the original message and the decrypted message are the same - assert source_plaintext == plaintext1 - assert source_plaintext == plaintext2 + if not isinstance(source_plaintext, bytes): + plaintext1 = plaintext_1.decode("utf-8") + plaintext2 = plaintext_2.decode("utf-8") + assert source_plaintext == plaintext_1 + assert source_plaintext == plaintext_2 # Check that the headers of the encrypted message and decrypted message match assert all( pair in encrypted_message_header.encryption_context.items() - for pair in decrypted_message_header1.encryption_context.items() + for pair in decrypted_message_header_1.encryption_context.items() ) assert all( pair in encrypted_message_header.encryption_context.items() - for pair in decrypted_message_header2.encryption_context.items() + for pair in decrypted_message_header_2.encryption_context.items() ) diff --git a/examples/test/test_i_multiple_kms_cmk_regions.py b/examples/test/test_i_multiple_kms_cmk_regions.py index c2722d9b1..15a604f69 100644 --- a/examples/test/test_i_multiple_kms_cmk_regions.py +++ b/examples/test/test_i_multiple_kms_cmk_regions.py @@ -17,7 +17,7 @@ from ..src.multiple_kms_cmk_regions import multiple_kms_cmk_regions -# from .examples_test_utils import get_cmk_arn +from .examples_test_utils import get_cmk_arn from .examples_test_utils import static_plaintext @@ -26,8 +26,8 @@ def test_multiple_kms_cmk_regions(): plaintext = static_plaintext - cmk_arn1 = "arn:aws:kms:us-west-2:658956600833:alias/EncryptDecrypt" - cmk_arn2 = "arn:aws:kms:eu-central-1:658956600833:alias/EncryptDecrypt" + cmk_arn_1 = get_cmk_arn("us-west-2") + cmk_arn_2 = get_cmk_arn("eu-central-1") multiple_kms_cmk_regions( - cmk_arn1, cmk_arn2, source_plaintext=plaintext, botocore_session=botocore.session.Session() + cmk_arn_1, cmk_arn_2, source_plaintext=plaintext, botocore_session=botocore.session.Session() ) diff --git a/test/integration/README.rst b/test/integration/README.rst index 33ecbbedd..eb3453e25 100644 --- a/test/integration/README.rst +++ b/test/integration/README.rst @@ -5,7 +5,8 @@ aws-encryption-sdk Integration Tests In order to run these integration tests successfully, these things must be configured. #. Ensure that AWS credentials are available in one of the `automatically discoverable credential locations`_. -#. Set environment variable ``AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID`` to valid +#. Set environment variable ``AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID`` + and ``AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2`` to valid `AWS KMS key id`_ to use for integration tests. .. _automatically discoverable credential locations: http://boto3.readthedocs.io/en/latest/guide/configuration.html diff --git a/test/integration/integration_test_utils.py b/test/integration/integration_test_utils.py index a5b4d6001..4169131b2 100644 --- a/test/integration/integration_test_utils.py +++ b/test/integration/integration_test_utils.py @@ -16,6 +16,7 @@ from aws_encryption_sdk.key_providers.kms import KMSMasterKeyProvider AWS_KMS_KEY_ID = "AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID" +AWS_KMS_KEY_ID_2 = "AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2" _KMS_MKP = None @@ -32,6 +33,26 @@ def get_cmk_arn(): return arn raise ValueError("KMS CMK ARN provided for integration tests much be a key not an alias") +def get_cmk_arn(region_name='us-west-2'): + """Retrieves a CMK ARN based on the requested region_name""" + if AWS_KMS_KEY_ID in os.environ and AWS_KMS_KEY_ID_2 in os.environ: + raise ValueError( + 'Environment variable "{}" or "{}" must be set to a valid KMS CMK ARN for integration tests to run'.format( + AWS_KMS_KEY_ID, AWS_KMS_KEY_ID_2 + ) + ) + arn_1 = os.environ.get(AWS_KMS_KEY_ID, None) + arn_2 = os.environ.get(AWS_KMS_KEY_ID_2, None) + if arn_1.split(':')[3] == region_name: + return os.environ.get(AWS_KMS_KEY_ID, None) + elif arn_2.split(':')[3] == region_name: + return os.environ.get(AWS_KMS_KEY_ID_2, None) + else: + raise ValueError( + 'No CMK in the region {} exist in either of your environment variables "{}" or "{}"'.format( + region_name, AWS_KMS_KEY_ID, AWS_KMS_KEY_ID_2 + ) + ) def setup_kms_master_key_provider(cache=True): """Reads the test_values config file and builds the requested KMS Master Key Provider.""" From 38e27574c3ee8538116b1dbf5fc45edf78335889 Mon Sep 17 00:00:00 2001 From: Caitlin Tibbetts Date: Mon, 5 Aug 2019 15:25:37 -0700 Subject: [PATCH 24/54] Small changes --- test/integration/integration_test_utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/integration/integration_test_utils.py b/test/integration/integration_test_utils.py index 4169131b2..2072cc0ff 100644 --- a/test/integration/integration_test_utils.py +++ b/test/integration/integration_test_utils.py @@ -33,7 +33,7 @@ def get_cmk_arn(): return arn raise ValueError("KMS CMK ARN provided for integration tests much be a key not an alias") -def get_cmk_arn(region_name='us-west-2'): +def get_cmk_arn(region_name): """Retrieves a CMK ARN based on the requested region_name""" if AWS_KMS_KEY_ID in os.environ and AWS_KMS_KEY_ID_2 in os.environ: raise ValueError( @@ -44,9 +44,9 @@ def get_cmk_arn(region_name='us-west-2'): arn_1 = os.environ.get(AWS_KMS_KEY_ID, None) arn_2 = os.environ.get(AWS_KMS_KEY_ID_2, None) if arn_1.split(':')[3] == region_name: - return os.environ.get(AWS_KMS_KEY_ID, None) + return arn_1 elif arn_2.split(':')[3] == region_name: - return os.environ.get(AWS_KMS_KEY_ID_2, None) + return arn_2 else: raise ValueError( 'No CMK in the region {} exist in either of your environment variables "{}" or "{}"'.format( From a7fcb4a01b6c14628eabcd1c18b9c4112c556b48 Mon Sep 17 00:00:00 2001 From: Caitlin Tibbetts Date: Tue, 6 Aug 2019 14:45:05 -0700 Subject: [PATCH 25/54] Update one_kms_cmk_unsigned.py --- examples/src/one_kms_cmk_unsigned.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/src/one_kms_cmk_unsigned.py b/examples/src/one_kms_cmk_unsigned.py index 235a8ac06..4c4e737bd 100644 --- a/examples/src/one_kms_cmk_unsigned.py +++ b/examples/src/one_kms_cmk_unsigned.py @@ -14,6 +14,7 @@ using one KMS CMK with an unsigned algorithm. """ import aws_encryption_sdk +from aws_encryption_sdk import encrypt, decrypt from aws_encryption_sdk.identifiers import Algorithm @@ -34,12 +35,12 @@ def encrypt_decrypt(key_arn, source_plaintext, botocore_session=None): kms_key_provider = aws_encryption_sdk.KMSMasterKeyProvider(**kwargs) # Encrypt the plaintext using the AWS Encryption SDK. It returns the encrypted message and the header - ciphertext, encrypted_message_header = aws_encryption_sdk.encrypt( + ciphertext, encrypted_message_header = encrypt( algorithm=Algorithm.AES_256_GCM_IV12_TAG16, source=source_plaintext, key_provider=kms_key_provider ) # Decrypt the encrypted message using the AWS Encryption SDK. It returns the decrypted message and the header - plaintext, decrypted_message_header = aws_encryption_sdk.decrypt(source=ciphertext, key_provider=kms_key_provider) + plaintext, decrypted_message_header = decrypt(source=ciphertext, key_provider=kms_key_provider) # Check if the original message and the decrypted message are the same assert source_plaintext == plaintext From 862734a027059dd34d16e89d76e8b85b66aa0653 Mon Sep 17 00:00:00 2001 From: Caitlin Tibbetts Date: Tue, 6 Aug 2019 14:45:27 -0700 Subject: [PATCH 26/54] Update examples/src/one_kms_cmk_unsigned.py Co-Authored-By: Matt Bullock --- examples/src/one_kms_cmk_unsigned.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/src/one_kms_cmk_unsigned.py b/examples/src/one_kms_cmk_unsigned.py index 4c4e737bd..1b7a24533 100644 --- a/examples/src/one_kms_cmk_unsigned.py +++ b/examples/src/one_kms_cmk_unsigned.py @@ -36,7 +36,7 @@ def encrypt_decrypt(key_arn, source_plaintext, botocore_session=None): # Encrypt the plaintext using the AWS Encryption SDK. It returns the encrypted message and the header ciphertext, encrypted_message_header = encrypt( - algorithm=Algorithm.AES_256_GCM_IV12_TAG16, source=source_plaintext, key_provider=kms_key_provider + algorithm=Algorithm.AES_256_GCM_IV12_TAG16_HKDF_SHA256, source=source_plaintext, key_provider=kms_key_provider ) # Decrypt the encrypted message using the AWS Encryption SDK. It returns the decrypted message and the header From 4def8ba64a4ac9ce692dab4a30b7a6aca1ab6b30 Mon Sep 17 00:00:00 2001 From: Caitlin Tibbetts Date: Wed, 7 Aug 2019 09:40:14 -0700 Subject: [PATCH 27/54] isort-check now succeeds --- examples/src/one_kms_cmk_unsigned.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/src/one_kms_cmk_unsigned.py b/examples/src/one_kms_cmk_unsigned.py index 1b7a24533..df2f4373d 100644 --- a/examples/src/one_kms_cmk_unsigned.py +++ b/examples/src/one_kms_cmk_unsigned.py @@ -13,8 +13,7 @@ """Example showing basic encryption and decryption of a value already in memory using one KMS CMK with an unsigned algorithm. """ -import aws_encryption_sdk -from aws_encryption_sdk import encrypt, decrypt +from aws_encryption_sdk import KMSMasterKeyProvider, decrypt, encrypt from aws_encryption_sdk.identifiers import Algorithm @@ -32,7 +31,7 @@ def encrypt_decrypt(key_arn, source_plaintext, botocore_session=None): kwargs["botocore_session"] = botocore_session # Create master key provider using the ARN of the key and the session (botocore_session) - kms_key_provider = aws_encryption_sdk.KMSMasterKeyProvider(**kwargs) + kms_key_provider = KMSMasterKeyProvider(**kwargs) # Encrypt the plaintext using the AWS Encryption SDK. It returns the encrypted message and the header ciphertext, encrypted_message_header = encrypt( From 81d79d3eaed2b1cc5c8bb366f9ee97e3bd6036da Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Sat, 7 Mar 2020 11:20:55 -0800 Subject: [PATCH 28/54] chore: move existing examples into "legacy" directory --- examples/src/{ => legacy}/basic_encryption.py | 14 ++------------ ...asic_file_encryption_with_multiple_providers.py | 14 ++------------ .../basic_file_encryption_with_raw_key_provider.py | 14 ++------------ .../src/{ => legacy}/data_key_caching_basic.py | 14 ++------------ examples/src/{ => legacy}/one_kms_cmk.py | 14 ++------------ .../src/{ => legacy}/one_kms_cmk_streaming_data.py | 14 ++------------ examples/src/{ => legacy}/one_kms_cmk_unsigned.py | 14 ++------------ 7 files changed, 14 insertions(+), 84 deletions(-) rename examples/src/{ => legacy}/basic_encryption.py (75%) rename examples/src/{ => legacy}/basic_file_encryption_with_multiple_providers.py (91%) rename examples/src/{ => legacy}/basic_file_encryption_with_raw_key_provider.py (85%) rename examples/src/{ => legacy}/data_key_caching_basic.py (74%) rename examples/src/{ => legacy}/one_kms_cmk.py (74%) rename examples/src/{ => legacy}/one_kms_cmk_streaming_data.py (79%) rename examples/src/{ => legacy}/one_kms_cmk_unsigned.py (76%) diff --git a/examples/src/basic_encryption.py b/examples/src/legacy/basic_encryption.py similarity index 75% rename from examples/src/basic_encryption.py rename to examples/src/legacy/basic_encryption.py index 6c194e45d..121baa250 100644 --- a/examples/src/basic_encryption.py +++ b/examples/src/legacy/basic_encryption.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Example showing basic encryption and decryption of a value already in memory.""" import aws_encryption_sdk diff --git a/examples/src/basic_file_encryption_with_multiple_providers.py b/examples/src/legacy/basic_file_encryption_with_multiple_providers.py similarity index 91% rename from examples/src/basic_file_encryption_with_multiple_providers.py rename to examples/src/legacy/basic_file_encryption_with_multiple_providers.py index 9edceef9e..fc11a3995 100644 --- a/examples/src/basic_file_encryption_with_multiple_providers.py +++ b/examples/src/legacy/basic_file_encryption_with_multiple_providers.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Example showing creation of a RawMasterKeyProvider, how to use multiple master key providers to encrypt, and demonstrating that each master key provider can then be used independently to decrypt the same encrypted message. diff --git a/examples/src/basic_file_encryption_with_raw_key_provider.py b/examples/src/legacy/basic_file_encryption_with_raw_key_provider.py similarity index 85% rename from examples/src/basic_file_encryption_with_raw_key_provider.py rename to examples/src/legacy/basic_file_encryption_with_raw_key_provider.py index 91e2a7e9a..909be54bf 100644 --- a/examples/src/basic_file_encryption_with_raw_key_provider.py +++ b/examples/src/legacy/basic_file_encryption_with_raw_key_provider.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Example showing creation and use of a RawMasterKeyProvider.""" import filecmp import os diff --git a/examples/src/data_key_caching_basic.py b/examples/src/legacy/data_key_caching_basic.py similarity index 74% rename from examples/src/data_key_caching_basic.py rename to examples/src/legacy/data_key_caching_basic.py index 1d5445615..f6e3e4732 100644 --- a/examples/src/data_key_caching_basic.py +++ b/examples/src/legacy/data_key_caching_basic.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Example of encryption with data key caching.""" import aws_encryption_sdk diff --git a/examples/src/one_kms_cmk.py b/examples/src/legacy/one_kms_cmk.py similarity index 74% rename from examples/src/one_kms_cmk.py rename to examples/src/legacy/one_kms_cmk.py index 1ba1d869f..a76985b0a 100644 --- a/examples/src/one_kms_cmk.py +++ b/examples/src/legacy/one_kms_cmk.py @@ -1,15 +1,5 @@ -# Copyright 2019 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Example showing basic encryption and decryption of a value already in memory using one KMS CMK.""" import aws_encryption_sdk diff --git a/examples/src/one_kms_cmk_streaming_data.py b/examples/src/legacy/one_kms_cmk_streaming_data.py similarity index 79% rename from examples/src/one_kms_cmk_streaming_data.py rename to examples/src/legacy/one_kms_cmk_streaming_data.py index 3edfa82ab..61f4443bc 100644 --- a/examples/src/one_kms_cmk_streaming_data.py +++ b/examples/src/legacy/one_kms_cmk_streaming_data.py @@ -1,15 +1,5 @@ -# Copyright 2019 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Example showing basic encryption and decryption of streaming data in memory using one KMS CMK.""" import filecmp diff --git a/examples/src/one_kms_cmk_unsigned.py b/examples/src/legacy/one_kms_cmk_unsigned.py similarity index 76% rename from examples/src/one_kms_cmk_unsigned.py rename to examples/src/legacy/one_kms_cmk_unsigned.py index df2f4373d..f7c603ce8 100644 --- a/examples/src/one_kms_cmk_unsigned.py +++ b/examples/src/legacy/one_kms_cmk_unsigned.py @@ -1,15 +1,5 @@ -# Copyright 2019 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Example showing basic encryption and decryption of a value already in memory using one KMS CMK with an unsigned algorithm. """ From 8026e3173c92ecb33a1514b7dd29e714ba81df5c Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Sat, 7 Mar 2020 12:46:36 -0800 Subject: [PATCH 29/54] chore: add automatic test runner for examples --- examples/test/examples_test_utils.py | 63 ++++++++++++++++++++++------ examples/test/test_run_examples.py | 40 ++++++++++++++++++ 2 files changed, 91 insertions(+), 12 deletions(-) create mode 100644 examples/test/test_run_examples.py diff --git a/examples/test/examples_test_utils.py b/examples/test/examples_test_utils.py index 0984ee684..965599c3d 100644 --- a/examples/test/examples_test_utils.py +++ b/examples/test/examples_test_utils.py @@ -1,18 +1,21 @@ -# Copyright 2019 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Helper utilities for use while testing examples.""" import os import sys +import inspect + +import pytest +import six + +try: # Python 3.5.0 and 3.5.1 have incompatible typing modules + from typing import Callable, List, Iterable # noqa pylint: disable=unused-import +except ImportError: # pragma: no cover + # We only actually need these imports when running the mypy checks + pass + +HERE = os.path.abspath(os.path.dirname(__file__)) +EXAMPLES_SOURCE = os.path.join(HERE, "..", "src") os.environ["AWS_ENCRYPTION_SDK_EXAMPLES_TESTING"] = "yes" sys.path.extend([os.sep.join([os.path.dirname(__file__), "..", "..", "test", "integration"])]) @@ -48,3 +51,39 @@ from integration_test_utils import get_cmk_arn # noqa pylint: disable=unused-import,import-error + + +@pytest.fixture +def aws_kms_cmk_arns(): + # type: () -> List[str] + return [get_cmk_arn()] + + +def all_examples(): + # type: () -> Iterable[pytest.param] + for (dirpath, dirnames, filenames) in os.walk(EXAMPLES_SOURCE): + for testfile in filenames: + split_path = testfile.rsplit(".", 1) + if len(split_path) != 2: + continue + stem, suffix = split_path + if suffix == "py" and stem != "__init__": + module_parent = dirpath[len(EXAMPLES_SOURCE) + 1 :].replace("/", ".") + module_name = stem + if module_parent: + import_path = "..src.{base}.{name}".format(base=module_parent, name=module_name) + else: + import_path = "..src.{name}".format(name=module_name) + + yield pytest.param(import_path, id="{base}.{name}".format(base=module_parent, name=module_name)) + + +def get_arg_names(function): + # type: (Callable) -> List[str] + if six.PY2: + # getargspec was deprecated in CPython 3.0 but 2.7 does not have either of the new options + spec = inspect.getargspec(function) + return spec.args + + spec = inspect.getfullargspec(function) + return spec.args diff --git a/examples/test/test_run_examples.py b/examples/test/test_run_examples.py new file mode 100644 index 000000000..88cf4f4ca --- /dev/null +++ b/examples/test/test_run_examples.py @@ -0,0 +1,40 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Test all examples.""" +from importlib import import_module + +import pytest + +from .examples_test_utils import all_examples, aws_kms_cmk_arns, get_arg_names, static_plaintext + +pytestmark = [pytest.mark.examples] +SINGLE_CMK_ARG = "aws_kms_cmk_arn" +GENERATOR_CMK_ARG = "aws_kms_generator_cmk" +CHILD_CMK_ARG = "aws_kms_child_cmks" +PLAINTEXT_ARG = "plaintext" + + +@pytest.mark.parametrize("import_path", all_examples()) +def test_examples(import_path, aws_kms_cmk_arns): + module = import_module(name=import_path, package=__package__) + try: + run_function = getattr(module, "run") + except AttributeError: + pytest.skip("Module lacks 'run' function.") + return + + args = get_arg_names(run_function) + possible_kwargs = { + SINGLE_CMK_ARG: aws_kms_cmk_arns[0], + GENERATOR_CMK_ARG: aws_kms_cmk_arns[0], + CHILD_CMK_ARG: aws_kms_cmk_arns[1:], + PLAINTEXT_ARG: static_plaintext, + } + kwargs = {} + for name in args: + try: + kwargs[name] = possible_kwargs[name] + except KeyError: + pass + + run_function(**kwargs) From 9d07fde86cefe0ac4c3e14b35270b3e1ae5e8bb8 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Sat, 7 Mar 2020 12:58:01 -0800 Subject: [PATCH 30/54] chore: convert existing examples to work with automatic test runner --- examples/src/legacy/__init__.py | 2 + examples/src/legacy/basic_encryption.py | 6 +-- ...file_encryption_with_multiple_providers.py | 6 +-- ...c_file_encryption_with_raw_key_provider.py | 2 +- examples/src/legacy/data_key_caching_basic.py | 6 +-- examples/src/legacy/one_kms_cmk.py | 6 +-- .../src/legacy/one_kms_cmk_streaming_data.py | 6 +-- examples/src/legacy/one_kms_cmk_unsigned.py | 6 +-- examples/test/test_i_basic_encryption.py | 27 ------------ ...file_encryption_with_multiple_providers.py | 41 ------------------- ...c_file_encryption_with_raw_key_provider.py | 36 ---------------- .../test/test_i_data_key_caching_basic.py | 25 ----------- examples/test/test_i_one_kms_cmk.py | 29 ------------- .../test/test_i_one_kms_cmk_streaming_data.py | 40 ------------------ examples/test/test_i_one_kms_cmk_unsigned.py | 29 ------------- examples/test/test_run_examples.py | 9 +++- 16 files changed, 28 insertions(+), 248 deletions(-) create mode 100644 examples/src/legacy/__init__.py delete mode 100644 examples/test/test_i_basic_encryption.py delete mode 100644 examples/test/test_i_basic_file_encryption_with_multiple_providers.py delete mode 100644 examples/test/test_i_basic_file_encryption_with_raw_key_provider.py delete mode 100644 examples/test/test_i_data_key_caching_basic.py delete mode 100644 examples/test/test_i_one_kms_cmk.py delete mode 100644 examples/test/test_i_one_kms_cmk_streaming_data.py delete mode 100644 examples/test/test_i_one_kms_cmk_unsigned.py diff --git a/examples/src/legacy/__init__.py b/examples/src/legacy/__init__.py new file mode 100644 index 000000000..f94fd12a2 --- /dev/null +++ b/examples/src/legacy/__init__.py @@ -0,0 +1,2 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 diff --git a/examples/src/legacy/basic_encryption.py b/examples/src/legacy/basic_encryption.py index 121baa250..bed46bd9d 100644 --- a/examples/src/legacy/basic_encryption.py +++ b/examples/src/legacy/basic_encryption.py @@ -4,16 +4,16 @@ import aws_encryption_sdk -def cycle_string(key_arn, source_plaintext, botocore_session=None): +def run(aws_kms_cmk_arn, source_plaintext, botocore_session=None): """Encrypts and then decrypts a string under a KMS customer master key (CMK). - :param str key_arn: Amazon Resource Name (ARN) of the KMS CMK + :param str aws_kms_cmk_arn: Amazon Resource Name (ARN) of the KMS CMK :param bytes source_plaintext: Data to encrypt :param botocore_session: existing botocore session instance :type botocore_session: botocore.session.Session """ # Create a KMS master key provider - kms_kwargs = dict(key_ids=[key_arn]) + kms_kwargs = dict(key_ids=[aws_kms_cmk_arn]) if botocore_session is not None: kms_kwargs["botocore_session"] = botocore_session master_key_provider = aws_encryption_sdk.KMSMasterKeyProvider(**kms_kwargs) diff --git a/examples/src/legacy/basic_file_encryption_with_multiple_providers.py b/examples/src/legacy/basic_file_encryption_with_multiple_providers.py index fc11a3995..aee55c90c 100644 --- a/examples/src/legacy/basic_file_encryption_with_multiple_providers.py +++ b/examples/src/legacy/basic_file_encryption_with_multiple_providers.py @@ -50,12 +50,12 @@ def _get_raw_key(self, key_id): ) -def cycle_file(key_arn, source_plaintext_filename, botocore_session=None): +def run(aws_kms_cmk_arn, source_plaintext_filename, botocore_session=None): """Encrypts and then decrypts a file using a KMS master key provider and a custom static master key provider. Both master key providers are used to encrypt the plaintext file, so either one alone can decrypt it. - :param str key_arn: Amazon Resource Name (ARN) of the KMS Customer Master Key (CMK) + :param str aws_kms_cmk_arn: Amazon Resource Name (ARN) of the KMS Customer Master Key (CMK) (http://docs.aws.amazon.com/kms/latest/developerguide/viewing-keys.html) :param str source_plaintext_filename: Filename of file to encrypt :param botocore_session: existing botocore session instance @@ -67,7 +67,7 @@ def cycle_file(key_arn, source_plaintext_filename, botocore_session=None): cycled_static_plaintext_filename = source_plaintext_filename + ".static.decrypted" # Create a KMS master key provider - kms_kwargs = dict(key_ids=[key_arn]) + kms_kwargs = dict(key_ids=[aws_kms_cmk_arn]) if botocore_session is not None: kms_kwargs["botocore_session"] = botocore_session kms_master_key_provider = aws_encryption_sdk.KMSMasterKeyProvider(**kms_kwargs) diff --git a/examples/src/legacy/basic_file_encryption_with_raw_key_provider.py b/examples/src/legacy/basic_file_encryption_with_raw_key_provider.py index 909be54bf..1e3eff8e0 100644 --- a/examples/src/legacy/basic_file_encryption_with_raw_key_provider.py +++ b/examples/src/legacy/basic_file_encryption_with_raw_key_provider.py @@ -38,7 +38,7 @@ def _get_raw_key(self, key_id): ) -def cycle_file(source_plaintext_filename): +def run(source_plaintext_filename): """Encrypts and then decrypts a file under a custom static master key provider. :param str source_plaintext_filename: Filename of file to encrypt diff --git a/examples/src/legacy/data_key_caching_basic.py b/examples/src/legacy/data_key_caching_basic.py index f6e3e4732..49f64adf8 100644 --- a/examples/src/legacy/data_key_caching_basic.py +++ b/examples/src/legacy/data_key_caching_basic.py @@ -4,10 +4,10 @@ import aws_encryption_sdk -def encrypt_with_caching(kms_cmk_arn, max_age_in_cache, cache_capacity): +def run(aws_kms_cmk_arn, max_age_in_cache=10.0, cache_capacity=10): """Encrypts a string using an AWS KMS customer master key (CMK) and data key caching. - :param str kms_cmk_arn: Amazon Resource Name (ARN) of the KMS customer master key + :param str aws_kms_cmk_arn: Amazon Resource Name (ARN) of the KMS customer master key :param float max_age_in_cache: Maximum time in seconds that a cached entry can be used :param int cache_capacity: Maximum number of entries to retain in cache at once """ @@ -22,7 +22,7 @@ def encrypt_with_caching(kms_cmk_arn, max_age_in_cache, cache_capacity): encryption_context = {"purpose": "test"} # Create a master key provider for the KMS customer master key (CMK) - key_provider = aws_encryption_sdk.KMSMasterKeyProvider(key_ids=[kms_cmk_arn]) + key_provider = aws_encryption_sdk.KMSMasterKeyProvider(key_ids=[aws_kms_cmk_arn]) # Create a local cache cache = aws_encryption_sdk.LocalCryptoMaterialsCache(cache_capacity) diff --git a/examples/src/legacy/one_kms_cmk.py b/examples/src/legacy/one_kms_cmk.py index a76985b0a..86611b2e0 100644 --- a/examples/src/legacy/one_kms_cmk.py +++ b/examples/src/legacy/one_kms_cmk.py @@ -4,15 +4,15 @@ import aws_encryption_sdk -def encrypt_decrypt(key_arn, source_plaintext, botocore_session=None): +def run(aws_kms_cmk_arn, source_plaintext, botocore_session=None): """Encrypts and then decrypts a string under one KMS customer master key (CMK). - :param str key_arn: Amazon Resource Name (ARN) of the KMS CMK + :param str aws_kms_cmk_arn: Amazon Resource Name (ARN) of the KMS CMK :param bytes source_plaintext: Data to encrypt :param botocore_session: existing botocore session instance :type botocore_session: botocore.session.Session """ - kwargs = dict(key_ids=[key_arn]) + kwargs = dict(key_ids=[aws_kms_cmk_arn]) if botocore_session is not None: kwargs["botocore_session"] = botocore_session diff --git a/examples/src/legacy/one_kms_cmk_streaming_data.py b/examples/src/legacy/one_kms_cmk_streaming_data.py index 61f4443bc..74a433391 100644 --- a/examples/src/legacy/one_kms_cmk_streaming_data.py +++ b/examples/src/legacy/one_kms_cmk_streaming_data.py @@ -6,17 +6,17 @@ import aws_encryption_sdk -def encrypt_decrypt_stream(key_arn, source_plaintext_filename, botocore_session=None): +def run(aws_kms_cmk_arn, source_plaintext_filename, botocore_session=None): """Encrypts and then decrypts streaming data under one KMS customer master key (CMK). - :param str key_arn: Amazon Resource Name (ARN) of the KMS CMK + :param str aws_kms_cmk_arn: Amazon Resource Name (ARN) of the KMS CMK :param str source_plaintext_filename: Filename of file to encrypt :param botocore_session: existing botocore session instance :type botocore_session: botocore.session.Session """ kwargs = dict() - kwargs["key_ids"] = [key_arn] + kwargs["key_ids"] = [aws_kms_cmk_arn] if botocore_session is not None: kwargs["botocore_session"] = botocore_session diff --git a/examples/src/legacy/one_kms_cmk_unsigned.py b/examples/src/legacy/one_kms_cmk_unsigned.py index f7c603ce8..d8a98a3e2 100644 --- a/examples/src/legacy/one_kms_cmk_unsigned.py +++ b/examples/src/legacy/one_kms_cmk_unsigned.py @@ -7,15 +7,15 @@ from aws_encryption_sdk.identifiers import Algorithm -def encrypt_decrypt(key_arn, source_plaintext, botocore_session=None): +def run(aws_kms_cmk_arn, source_plaintext, botocore_session=None): """Encrypts and then decrypts a string under one KMS customer master key (CMK) with an unsigned algorithm. - :param str key_arn: Amazon Resource Name (ARN) of the KMS CMK + :param str aws_kms_cmk_arn: Amazon Resource Name (ARN) of the KMS CMK :param bytes source_plaintext: Data to encrypt :param botocore_session: existing botocore session instance :type botocore_session: botocore.session.Session """ - kwargs = dict(key_ids=[key_arn]) + kwargs = dict(key_ids=[aws_kms_cmk_arn]) if botocore_session is not None: kwargs["botocore_session"] = botocore_session diff --git a/examples/test/test_i_basic_encryption.py b/examples/test/test_i_basic_encryption.py deleted file mode 100644 index f2a4fab51..000000000 --- a/examples/test/test_i_basic_encryption.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright 2017-2019 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. -"""Unit test suite for the Strings examples in the AWS-hosted documentation.""" -import botocore.session -import pytest - -from ..src.basic_encryption import cycle_string -from .examples_test_utils import get_cmk_arn, static_plaintext - - -pytestmark = [pytest.mark.examples] - - -def test_cycle_string(): - plaintext = static_plaintext - cmk_arn = get_cmk_arn() - cycle_string(key_arn=cmk_arn, source_plaintext=plaintext, botocore_session=botocore.session.Session()) diff --git a/examples/test/test_i_basic_file_encryption_with_multiple_providers.py b/examples/test/test_i_basic_file_encryption_with_multiple_providers.py deleted file mode 100644 index 282a272ab..000000000 --- a/examples/test/test_i_basic_file_encryption_with_multiple_providers.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright 2017-2019 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. -"""Unit test suite for the Bytes Streams Multiple Providers examples in the AWS-hosted documentation.""" -import os -import tempfile - -import botocore.session -import pytest - -from ..src.basic_file_encryption_with_multiple_providers import cycle_file -from .examples_test_utils import get_cmk_arn -from .examples_test_utils import static_plaintext - - -pytestmark = [pytest.mark.examples] - - -def test_cycle_file(): - cmk_arn = get_cmk_arn() - handle, filename = tempfile.mkstemp() - with open(filename, "wb") as f: - f.write(static_plaintext) - try: - new_files = cycle_file( - key_arn=cmk_arn, source_plaintext_filename=filename, botocore_session=botocore.session.Session() - ) - for f in new_files: - os.remove(f) - finally: - os.close(handle) - os.remove(filename) diff --git a/examples/test/test_i_basic_file_encryption_with_raw_key_provider.py b/examples/test/test_i_basic_file_encryption_with_raw_key_provider.py deleted file mode 100644 index 710c0ccac..000000000 --- a/examples/test/test_i_basic_file_encryption_with_raw_key_provider.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright 2017-2019 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. -"""Unit test suite for the Bytes Streams examples in the AWS-hosted documentation.""" -import os -import tempfile - -import pytest - -from ..src.basic_file_encryption_with_raw_key_provider import cycle_file -from .examples_test_utils import static_plaintext - - -pytestmark = [pytest.mark.examples] - - -def test_cycle_file(): - handle, filename = tempfile.mkstemp() - with open(filename, "wb") as f: - f.write(static_plaintext) - try: - new_files = cycle_file(source_plaintext_filename=filename) - for f in new_files: - os.remove(f) - finally: - os.close(handle) - os.remove(filename) diff --git a/examples/test/test_i_data_key_caching_basic.py b/examples/test/test_i_data_key_caching_basic.py deleted file mode 100644 index 734c35692..000000000 --- a/examples/test/test_i_data_key_caching_basic.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright 2017-2019 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. -"""Unit test suite for the basic data key caching example in the AWS-hosted documentation.""" -import pytest - -from ..src.data_key_caching_basic import encrypt_with_caching -from .examples_test_utils import get_cmk_arn - - -pytestmark = [pytest.mark.examples] - - -def test_encrypt_with_caching(): - cmk_arn = get_cmk_arn() - encrypt_with_caching(kms_cmk_arn=cmk_arn, max_age_in_cache=10.0, cache_capacity=10) diff --git a/examples/test/test_i_one_kms_cmk.py b/examples/test/test_i_one_kms_cmk.py deleted file mode 100644 index 71ce74d3d..000000000 --- a/examples/test/test_i_one_kms_cmk.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright 2019 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. -"""Unit test suite for the encryption and decryption using one KMS CMK example.""" - -import botocore.session -import pytest - -from ..src.one_kms_cmk import encrypt_decrypt -from .examples_test_utils import get_cmk_arn -from .examples_test_utils import static_plaintext - - -pytestmark = [pytest.mark.examples] - - -def test_one_kms_cmk(): - plaintext = static_plaintext - cmk_arn = get_cmk_arn() - encrypt_decrypt(key_arn=cmk_arn, source_plaintext=plaintext, botocore_session=botocore.session.Session()) diff --git a/examples/test/test_i_one_kms_cmk_streaming_data.py b/examples/test/test_i_one_kms_cmk_streaming_data.py deleted file mode 100644 index b22fa4232..000000000 --- a/examples/test/test_i_one_kms_cmk_streaming_data.py +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright 2017-2019 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. -"""Unit test suite for the encryption and decryption of streaming data using one KMS CMK example.""" -import os -import tempfile - -import botocore.session -import pytest - -from ..src.one_kms_cmk_streaming_data import encrypt_decrypt_stream -from .examples_test_utils import get_cmk_arn, static_plaintext - - -pytestmark = [pytest.mark.examples] - - -def test_one_kms_cmk_streaming_data(): - cmk_arn = get_cmk_arn() - handle, filename = tempfile.mkstemp() - with open(filename, "wb") as f: - f.write(static_plaintext) - try: - new_files = encrypt_decrypt_stream( - key_arn=cmk_arn, source_plaintext_filename=filename, botocore_session=botocore.session.Session() - ) - for f in new_files: - os.remove(f) - finally: - os.close(handle) - os.remove(filename) diff --git a/examples/test/test_i_one_kms_cmk_unsigned.py b/examples/test/test_i_one_kms_cmk_unsigned.py deleted file mode 100644 index 8a2758c96..000000000 --- a/examples/test/test_i_one_kms_cmk_unsigned.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright 2019 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. -"""Unit test suite for the encryption and decryption using one KMS CMK with an unsigned algorithm example.""" - -import botocore.session -import pytest - -from ..src.one_kms_cmk_unsigned import encrypt_decrypt -from .examples_test_utils import get_cmk_arn -from .examples_test_utils import static_plaintext - - -pytestmark = [pytest.mark.examples] - - -def test_one_kms_cmk_unsigned(): - plaintext = static_plaintext - cmk_arn = get_cmk_arn() - encrypt_decrypt(key_arn=cmk_arn, source_plaintext=plaintext, botocore_session=botocore.session.Session()) diff --git a/examples/test/test_run_examples.py b/examples/test/test_run_examples.py index 88cf4f4ca..1f0aba150 100644 --- a/examples/test/test_run_examples.py +++ b/examples/test/test_run_examples.py @@ -11,11 +11,12 @@ SINGLE_CMK_ARG = "aws_kms_cmk_arn" GENERATOR_CMK_ARG = "aws_kms_generator_cmk" CHILD_CMK_ARG = "aws_kms_child_cmks" -PLAINTEXT_ARG = "plaintext" +PLAINTEXT_ARG = "source_plaintext" +PLAINTEXT_FILE_ARG = "source_plaintext_filename" @pytest.mark.parametrize("import_path", all_examples()) -def test_examples(import_path, aws_kms_cmk_arns): +def test_examples(import_path, aws_kms_cmk_arns, tmp_path): module = import_module(name=import_path, package=__package__) try: run_function = getattr(module, "run") @@ -23,12 +24,16 @@ def test_examples(import_path, aws_kms_cmk_arns): pytest.skip("Module lacks 'run' function.") return + plaintext_file = tmp_path / "plaintext" + plaintext_file.write_bytes(static_plaintext) + args = get_arg_names(run_function) possible_kwargs = { SINGLE_CMK_ARG: aws_kms_cmk_arns[0], GENERATOR_CMK_ARG: aws_kms_cmk_arns[0], CHILD_CMK_ARG: aws_kms_cmk_arns[1:], PLAINTEXT_ARG: static_plaintext, + PLAINTEXT_FILE_ARG: str(plaintext_file.absolute()), } kwargs = {} for name in args: From 421e12183acacdeed879f6651affbf2a85ac825b Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Sat, 7 Mar 2020 13:08:18 -0800 Subject: [PATCH 31/54] chore: move examples kwarg building into utils module --- examples/test/examples_test_utils.py | 40 +++++++++++++++++++++++----- examples/test/test_run_examples.py | 27 +++---------------- 2 files changed, 36 insertions(+), 31 deletions(-) diff --git a/examples/test/examples_test_utils.py b/examples/test/examples_test_utils.py index 965599c3d..290381a87 100644 --- a/examples/test/examples_test_utils.py +++ b/examples/test/examples_test_utils.py @@ -9,13 +9,20 @@ import six try: # Python 3.5.0 and 3.5.1 have incompatible typing modules - from typing import Callable, List, Iterable # noqa pylint: disable=unused-import + from typing import Callable, Dict, Iterable, List # noqa pylint: disable=unused-import + # we only need pathlib here for typehints + from pathlib import Path except ImportError: # pragma: no cover # We only actually need these imports when running the mypy checks pass HERE = os.path.abspath(os.path.dirname(__file__)) EXAMPLES_SOURCE = os.path.join(HERE, "..", "src") +SINGLE_CMK_ARG = "aws_kms_cmk_arn" +GENERATOR_CMK_ARG = "aws_kms_generator_cmk" +CHILD_CMK_ARG = "aws_kms_child_cmks" +PLAINTEXT_ARG = "source_plaintext" +PLAINTEXT_FILE_ARG = "source_plaintext_filename" os.environ["AWS_ENCRYPTION_SDK_EXAMPLES_TESTING"] = "yes" sys.path.extend([os.sep.join([os.path.dirname(__file__), "..", "..", "test", "integration"])]) @@ -53,12 +60,6 @@ from integration_test_utils import get_cmk_arn # noqa pylint: disable=unused-import,import-error -@pytest.fixture -def aws_kms_cmk_arns(): - # type: () -> List[str] - return [get_cmk_arn()] - - def all_examples(): # type: () -> Iterable[pytest.param] for (dirpath, dirnames, filenames) in os.walk(EXAMPLES_SOURCE): @@ -87,3 +88,28 @@ def get_arg_names(function): spec = inspect.getfullargspec(function) return spec.args + + +def build_kwargs(function, temp_dir): + # type: (Callable, Path) -> Dict[str, str] + + plaintext_file = temp_dir / "plaintext" + plaintext_file.write_bytes(static_plaintext) + + cmk_arns = [get_cmk_arn()] + + args = get_arg_names(function) + possible_kwargs = { + SINGLE_CMK_ARG: cmk_arns[0], + GENERATOR_CMK_ARG: cmk_arns[0], + CHILD_CMK_ARG: cmk_arns[1:], + PLAINTEXT_ARG: static_plaintext, + PLAINTEXT_FILE_ARG: str(plaintext_file.absolute()), + } + kwargs = {} + for name in args: + try: + kwargs[name] = possible_kwargs[name] + except KeyError: + pass + return kwargs diff --git a/examples/test/test_run_examples.py b/examples/test/test_run_examples.py index 1f0aba150..2edc68626 100644 --- a/examples/test/test_run_examples.py +++ b/examples/test/test_run_examples.py @@ -5,18 +5,13 @@ import pytest -from .examples_test_utils import all_examples, aws_kms_cmk_arns, get_arg_names, static_plaintext +from .examples_test_utils import all_examples, build_kwargs pytestmark = [pytest.mark.examples] -SINGLE_CMK_ARG = "aws_kms_cmk_arn" -GENERATOR_CMK_ARG = "aws_kms_generator_cmk" -CHILD_CMK_ARG = "aws_kms_child_cmks" -PLAINTEXT_ARG = "source_plaintext" -PLAINTEXT_FILE_ARG = "source_plaintext_filename" @pytest.mark.parametrize("import_path", all_examples()) -def test_examples(import_path, aws_kms_cmk_arns, tmp_path): +def test_examples(import_path, tmp_path): module = import_module(name=import_path, package=__package__) try: run_function = getattr(module, "run") @@ -24,22 +19,6 @@ def test_examples(import_path, aws_kms_cmk_arns, tmp_path): pytest.skip("Module lacks 'run' function.") return - plaintext_file = tmp_path / "plaintext" - plaintext_file.write_bytes(static_plaintext) - - args = get_arg_names(run_function) - possible_kwargs = { - SINGLE_CMK_ARG: aws_kms_cmk_arns[0], - GENERATOR_CMK_ARG: aws_kms_cmk_arns[0], - CHILD_CMK_ARG: aws_kms_cmk_arns[1:], - PLAINTEXT_ARG: static_plaintext, - PLAINTEXT_FILE_ARG: str(plaintext_file.absolute()), - } - kwargs = {} - for name in args: - try: - kwargs[name] = possible_kwargs[name] - except KeyError: - pass + kwargs = build_kwargs(function=run_function, temp_dir=tmp_path) run_function(**kwargs) From ef475d76c478051e3f1a8f3d2ea89b8870c58f53 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Sun, 8 Mar 2020 16:14:04 -0700 Subject: [PATCH 32/54] chore: convert multi-CMK test runners to new configuration format --- examples/test/examples_test_utils.py | 5 ++- .../test/test_i_multiple_kms_cmk_regions.py | 5 +-- test/integration/integration_test_utils.py | 45 +++++++------------ tox.ini | 3 +- 4 files changed, 24 insertions(+), 34 deletions(-) diff --git a/examples/test/examples_test_utils.py b/examples/test/examples_test_utils.py index 290381a87..736e9145d 100644 --- a/examples/test/examples_test_utils.py +++ b/examples/test/examples_test_utils.py @@ -10,6 +10,7 @@ try: # Python 3.5.0 and 3.5.1 have incompatible typing modules from typing import Callable, Dict, Iterable, List # noqa pylint: disable=unused-import + # we only need pathlib here for typehints from pathlib import Path except ImportError: # pragma: no cover @@ -57,7 +58,7 @@ ) -from integration_test_utils import get_cmk_arn # noqa pylint: disable=unused-import,import-error +from integration_test_utils import get_all_cmk_arns # noqa pylint: disable=unused-import,import-error def all_examples(): @@ -96,7 +97,7 @@ def build_kwargs(function, temp_dir): plaintext_file = temp_dir / "plaintext" plaintext_file.write_bytes(static_plaintext) - cmk_arns = [get_cmk_arn()] + cmk_arns = get_all_cmk_arns() args = get_arg_names(function) possible_kwargs = { diff --git a/examples/test/test_i_multiple_kms_cmk_regions.py b/examples/test/test_i_multiple_kms_cmk_regions.py index 15a604f69..cdbc52ee7 100644 --- a/examples/test/test_i_multiple_kms_cmk_regions.py +++ b/examples/test/test_i_multiple_kms_cmk_regions.py @@ -17,7 +17,7 @@ from ..src.multiple_kms_cmk_regions import multiple_kms_cmk_regions -from .examples_test_utils import get_cmk_arn +from .examples_test_utils import get_all_cmk_arns from .examples_test_utils import static_plaintext @@ -26,8 +26,7 @@ def test_multiple_kms_cmk_regions(): plaintext = static_plaintext - cmk_arn_1 = get_cmk_arn("us-west-2") - cmk_arn_2 = get_cmk_arn("eu-central-1") + cmk_arn_1, cmk_arn_2 = get_all_cmk_arns()[:2] multiple_kms_cmk_regions( cmk_arn_1, cmk_arn_2, source_plaintext=plaintext, botocore_session=botocore.session.Session() ) diff --git a/test/integration/integration_test_utils.py b/test/integration/integration_test_utils.py index fc31db16b..4928f3637 100644 --- a/test/integration/integration_test_utils.py +++ b/test/integration/integration_test_utils.py @@ -26,39 +26,28 @@ _KMS_KEYRING = None -def get_cmk_arn(): - """Retrieves the target CMK ARN from environment variable.""" - arn = os.environ.get(AWS_KMS_KEY_ID, None) +def _get_single_cmk_arn(name): + # type: (str) -> str + """Retrieve a single target AWS KMS CMK ARN from the specified environment variable name.""" + arn = os.environ.get(name, None) if arn is None: raise ValueError( - 'Environment variable "{}" must be set to a valid KMS CMK ARN for integration tests to run'.format( - AWS_KMS_KEY_ID - ) + 'Environment variable "{}" must be set to a valid KMS CMK ARN for integration tests to run'.format(name) ) if arn.startswith("arn:") and ":alias/" not in arn: return arn raise ValueError("KMS CMK ARN provided for integration tests much be a key not an alias") -def get_cmk_arn(region_name): - """Retrieves a CMK ARN based on the requested region_name""" - if AWS_KMS_KEY_ID in os.environ and AWS_KMS_KEY_ID_2 in os.environ: - raise ValueError( - 'Environment variable "{}" or "{}" must be set to a valid KMS CMK ARN for integration tests to run'.format( - AWS_KMS_KEY_ID, AWS_KMS_KEY_ID_2 - ) - ) - arn_1 = os.environ.get(AWS_KMS_KEY_ID, None) - arn_2 = os.environ.get(AWS_KMS_KEY_ID_2, None) - if arn_1.split(':')[3] == region_name: - return arn_1 - elif arn_2.split(':')[3] == region_name: - return arn_2 - else: - raise ValueError( - 'No CMK in the region {} exist in either of your environment variables "{}" or "{}"'.format( - region_name, AWS_KMS_KEY_ID, AWS_KMS_KEY_ID_2 - ) - ) + +def get_cmk_arn(): + """Retrieves the target AWS KMS CMK ARN from environment variable.""" + return _get_single_cmk_arn(AWS_KMS_KEY_ID) + + +def get_all_cmk_arns(): + """Retrieve all known target AWS KMS CMK ARNs from environment variables.""" + return [_get_single_cmk_arn(AWS_KMS_KEY_ID), _get_single_cmk_arn(AWS_KMS_KEY_ID_2)] + def setup_kms_master_key_provider(cache=True): """Build an AWS KMS Master Key Provider.""" @@ -68,7 +57,7 @@ def setup_kms_master_key_provider(cache=True): cmk_arn = get_cmk_arn() kms_master_key_provider = KMSMasterKeyProvider() - kms_master_key_provider.add_master_key(cmk_arn) + kms_master_key_provider.add_master_key(cmk_arn.encode("utf-8")) if cache: _KMS_MKP = kms_master_key_provider @@ -84,7 +73,7 @@ def setup_kms_master_key_provider_with_botocore_session(cache=True): cmk_arn = get_cmk_arn() kms_master_key_provider = KMSMasterKeyProvider(botocore_session=botocore.session.Session()) - kms_master_key_provider.add_master_key(cmk_arn) + kms_master_key_provider.add_master_key(cmk_arn.encode("utf-8")) if cache: _KMS_MKP_BOTO = kms_master_key_provider diff --git a/tox.ini b/tox.ini index d9d60d756..0104196f0 100644 --- a/tox.ini +++ b/tox.ini @@ -40,8 +40,9 @@ commands = pytest --basetemp={envtmpdir} -l --cov aws_encryption_sdk {posargs} [testenv] passenv = - # Identifies AWS KMS key id to use in integration tests + # Identifies AWS KMS key ids to use in integration tests AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID \ + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2 \ # Pass through AWS credentials AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN \ # Pass through AWS profile name (useful for local testing) From 371daafd253477cd8b859c94d95bea0e5d68ffe0 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Sun, 8 Mar 2020 16:18:15 -0700 Subject: [PATCH 33/54] fix: fix multi-CMK example logic --- examples/src/multiple_kms_cmk_regions.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/examples/src/multiple_kms_cmk_regions.py b/examples/src/multiple_kms_cmk_regions.py index f036e9121..40031ae39 100644 --- a/examples/src/multiple_kms_cmk_regions.py +++ b/examples/src/multiple_kms_cmk_regions.py @@ -16,7 +16,6 @@ """ import aws_encryption_sdk from aws_encryption_sdk.key_providers.kms import KMSMasterKey, KMSMasterKeyProvider -from aws_encryption_sdk.internal.crypto.encryption import encrypt, decrypt def multiple_kms_cmk_regions(key_arn_1, key_arn_2, source_plaintext, botocore_session=None): @@ -40,24 +39,26 @@ def multiple_kms_cmk_regions(key_arn_1, key_arn_2, source_plaintext, botocore_se kms_key_provider = KMSMasterKeyProvider(**kwargs) # Encrypt the plaintext using the AWS Encryption SDK. It returns the encrypted message and the header - ciphertext, encrypted_message_header = encrypt(kms_key_provider, source_plaintext) + ciphertext, encrypted_message_header = aws_encryption_sdk.encrypt( + key_provider=kms_key_provider, source=source_plaintext + ) # Check that both key ARNs are in the message headers assert len(encrypted_message_header.encrypted_data_keys) == 2 # Decrypt the encrypted message using the AWS Encryption SDK. It returns the decrypted message and the header # Either of our keys can be used to decrypt the message - plaintext_1, decrypted_message_header_1 = decrypt( - KMSMasterKey(key_id=key_arn_1), ciphertext + plaintext_1, decrypted_message_header_1 = aws_encryption_sdk.decrypt( + key_provider=KMSMasterKey(key_id=key_arn_1), source=ciphertext ) - plaintext_2, decrypted_message_header_2 = decrypt( - KMSMasterKey(key_id=key_arn_2), ciphertext + plaintext_2, decrypted_message_header_2 = aws_encryption_sdk.decrypt( + key_provider=KMSMasterKey(key_id=key_arn_2), source=ciphertext ) # Check that the original message and the decrypted message are the same if not isinstance(source_plaintext, bytes): - plaintext1 = plaintext_1.decode("utf-8") - plaintext2 = plaintext_2.decode("utf-8") + plaintext_1 = plaintext_1.decode("utf-8") + plaintext_2 = plaintext_2.decode("utf-8") assert source_plaintext == plaintext_1 assert source_plaintext == plaintext_2 From 886341cfa80ca99f9eefbae4859909aa968e9165 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Sun, 8 Mar 2020 16:22:22 -0700 Subject: [PATCH 34/54] chore: convert multi-CMK example to new test runner signature and move into legacy examples --- .../{ => legacy}/multiple_kms_cmk_regions.py | 29 ++++++----------- .../test/test_i_multiple_kms_cmk_regions.py | 32 ------------------- 2 files changed, 10 insertions(+), 51 deletions(-) rename examples/src/{ => legacy}/multiple_kms_cmk_regions.py (69%) delete mode 100644 examples/test/test_i_multiple_kms_cmk_regions.py diff --git a/examples/src/multiple_kms_cmk_regions.py b/examples/src/legacy/multiple_kms_cmk_regions.py similarity index 69% rename from examples/src/multiple_kms_cmk_regions.py rename to examples/src/legacy/multiple_kms_cmk_regions.py index 40031ae39..16c0170bc 100644 --- a/examples/src/multiple_kms_cmk_regions.py +++ b/examples/src/legacy/multiple_kms_cmk_regions.py @@ -1,15 +1,5 @@ -# Copyright 2019 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """ Example showing basic encryption and decryption of a value already in memory using multiple KMS CMKs in multiple regions. @@ -18,19 +8,20 @@ from aws_encryption_sdk.key_providers.kms import KMSMasterKey, KMSMasterKeyProvider -def multiple_kms_cmk_regions(key_arn_1, key_arn_2, source_plaintext, botocore_session=None): +def run(aws_kms_generator_cmk, aws_kms_child_cmks, source_plaintext, botocore_session=None): """Encrypts and then decrypts a string under multiple KMS customer master keys (CMKs) in multiple regions. - :param str key_arn_1: Amazon Resource Name (ARN) of the KMS CMK - :param str key_arn_2: Amazon Resource Name (ARN) of another KMS CMK + :param str aws_kms_generator_cmk: Amazon Resource Name (ARN) of the primary KMS CMK + :param List[str] aws_kms_child_cmks: Additional Amazon Resource Names (ARNs) of secondary KMS CMKs :param bytes source_plaintext: Data to encrypt :param botocore_session: existing botocore session instance :type botocore_session: botocore.session.Session """ + child_cmk = aws_kms_child_cmks[0] # Check that these keys are in different regions - assert not key_arn_1.split(":")[3] == key_arn_2.split(":")[3] + assert not aws_kms_generator_cmk.split(":")[3] == child_cmk.split(":")[3] - kwargs = dict(key_ids=[key_arn_1, key_arn_2]) + kwargs = dict(key_ids=[aws_kms_generator_cmk, child_cmk]) if botocore_session is not None: kwargs["botocore_session"] = botocore_session @@ -49,10 +40,10 @@ def multiple_kms_cmk_regions(key_arn_1, key_arn_2, source_plaintext, botocore_se # Decrypt the encrypted message using the AWS Encryption SDK. It returns the decrypted message and the header # Either of our keys can be used to decrypt the message plaintext_1, decrypted_message_header_1 = aws_encryption_sdk.decrypt( - key_provider=KMSMasterKey(key_id=key_arn_1), source=ciphertext + key_provider=KMSMasterKey(key_id=aws_kms_generator_cmk), source=ciphertext ) plaintext_2, decrypted_message_header_2 = aws_encryption_sdk.decrypt( - key_provider=KMSMasterKey(key_id=key_arn_2), source=ciphertext + key_provider=KMSMasterKey(key_id=child_cmk), source=ciphertext ) # Check that the original message and the decrypted message are the same diff --git a/examples/test/test_i_multiple_kms_cmk_regions.py b/examples/test/test_i_multiple_kms_cmk_regions.py deleted file mode 100644 index cdbc52ee7..000000000 --- a/examples/test/test_i_multiple_kms_cmk_regions.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright 2019 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. -"""Unit test suite for the encryption and decryption using multiple KMS CMKs in multiple regions example.""" - -import botocore.session -import pytest - -from ..src.multiple_kms_cmk_regions import multiple_kms_cmk_regions - -from .examples_test_utils import get_all_cmk_arns -from .examples_test_utils import static_plaintext - - -pytestmark = [pytest.mark.examples] - - -def test_multiple_kms_cmk_regions(): - plaintext = static_plaintext - cmk_arn_1, cmk_arn_2 = get_all_cmk_arns()[:2] - multiple_kms_cmk_regions( - cmk_arn_1, cmk_arn_2, source_plaintext=plaintext, botocore_session=botocore.session.Session() - ) From 460e6e10666822490c849a1e11f54ad277562311 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Sun, 8 Mar 2020 17:27:11 -0700 Subject: [PATCH 35/54] chore: make examples linting always run across both source and tests --- tox.ini | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tox.ini b/tox.ini index 0104196f0..c073dd2b1 100644 --- a/tox.ini +++ b/tox.ini @@ -141,6 +141,10 @@ commands = [testenv:flake8-examples] basepython = {[testenv:flake8]basepython} +# This does not actually ignore errors, +# it just runs all commands regardless of whether any fail. +# If any fail, the final result is still a fail. +ignore_errors = true deps = {[testenv:flake8]deps} commands = flake8 examples/src/ @@ -165,6 +169,10 @@ commands = [testenv:pylint-examples] basepython = {[testenv:pylint]basepython} +# This does not actually ignore errors, +# it just runs all commands regardless of whether any fail. +# If any fail, the final result is still a fail. +ignore_errors = true deps = {[testenv:pylint]deps} commands = pylint --rcfile=examples/src/pylintrc examples/src/ From 7a81af49334be5830cfbac9eda5b7c03583c5d62 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Sun, 8 Mar 2020 17:31:28 -0700 Subject: [PATCH 36/54] fix: linting fixes --- examples/test/examples_test_utils.py | 4 ++-- examples/test/test_run_examples.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/test/examples_test_utils.py b/examples/test/examples_test_utils.py index 736e9145d..6c438cc0e 100644 --- a/examples/test/examples_test_utils.py +++ b/examples/test/examples_test_utils.py @@ -63,7 +63,7 @@ def all_examples(): # type: () -> Iterable[pytest.param] - for (dirpath, dirnames, filenames) in os.walk(EXAMPLES_SOURCE): + for (dirpath, _dirnames, filenames) in os.walk(EXAMPLES_SOURCE): for testfile in filenames: split_path = testfile.rsplit(".", 1) if len(split_path) != 2: @@ -84,7 +84,7 @@ def get_arg_names(function): # type: (Callable) -> List[str] if six.PY2: # getargspec was deprecated in CPython 3.0 but 2.7 does not have either of the new options - spec = inspect.getargspec(function) + spec = inspect.getargspec(function) # pylint: disable=deprecated-method return spec.args spec = inspect.getfullargspec(function) diff --git a/examples/test/test_run_examples.py b/examples/test/test_run_examples.py index 2edc68626..781f341ad 100644 --- a/examples/test/test_run_examples.py +++ b/examples/test/test_run_examples.py @@ -14,7 +14,7 @@ def test_examples(import_path, tmp_path): module = import_module(name=import_path, package=__package__) try: - run_function = getattr(module, "run") + run_function = module.run except AttributeError: pytest.skip("Module lacks 'run' function.") return From 78484eb12283e12e90125148d6fadc879446e33f Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Sun, 8 Mar 2020 17:32:01 -0700 Subject: [PATCH 37/54] docs: add docstring description for legacy examples module --- examples/src/legacy/__init__.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/examples/src/legacy/__init__.py b/examples/src/legacy/__init__.py index f94fd12a2..f91696b88 100644 --- a/examples/src/legacy/__init__.py +++ b/examples/src/legacy/__init__.py @@ -1,2 +1,11 @@ # Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0 +""" +Legacy examples. + +These are any examples that were already defined +before we started revamping our examples. +We are keeping them around for anyone who needs them as reference material, +but we recommend looking at the newer examples +that should provide a clearer picture of how to use this library. +""" From a818ea59c2c646435e471b54b7f55fa7a3a7a4e8 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Sun, 8 Mar 2020 17:48:17 -0700 Subject: [PATCH 38/54] chore: add initial new-format examples --- examples/src/file_streaming_defaults.py | 79 ++++++++++++++++++++ examples/src/in_memory_streaming_defaults.py | 75 +++++++++++++++++++ examples/src/oneshot_defaults.py | 56 ++++++++++++++ examples/src/oneshot_unsigned.py | 76 +++++++++++++++++++ 4 files changed, 286 insertions(+) create mode 100644 examples/src/file_streaming_defaults.py create mode 100644 examples/src/in_memory_streaming_defaults.py create mode 100644 examples/src/oneshot_defaults.py create mode 100644 examples/src/oneshot_unsigned.py diff --git a/examples/src/file_streaming_defaults.py b/examples/src/file_streaming_defaults.py new file mode 100644 index 000000000..8d98b77e8 --- /dev/null +++ b/examples/src/file_streaming_defaults.py @@ -0,0 +1,79 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +This example shows how to use the streaming encrypt and decrypt APIs when working with files. + +For the purposes of this example, we demonstrate using AWS KMS, +but you can use other key management options with the AWS Encryption SDK. +Look in the ``keyring`` and ``master_key_provider`` directories +for examples that demonstrate how to use other key management configurations. +""" +import filecmp + +import aws_encryption_sdk +from aws_encryption_sdk.keyrings.aws_kms import KmsKeyring + + +def run(aws_kms_cmk_arn, source_plaintext_filename): + # type: (str, str) -> None + """Demonstrate an encrypt/decrypt cycle using the streaming encrypt/decrypt APIs to work with files. + + :param str aws_kms_cmk_arn: AWS KMS CMK ARN to use to protect data keys + :param str source_plaintext_filename: Path to plaintext file to encrypt + """ + # We assume that you can also write in the directory containing the plaintext file, + # so that is where we will put all of the results. + ciphertext_filename = source_plaintext_filename + ".encrypted" + decrypted_filename = ciphertext_filename + ".decrypted" + + # Prepare your encryption context. + encryption_context = { + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + + # Create the keyring that determines how your keys are protected. + keyring = KmsKeyring(generator_key_id=aws_kms_cmk_arn) + + # Open the files you want to work with. + with open(source_plaintext_filename, "rb") as plaintext, open(ciphertext_filename, "wb") as ciphertext: + # The streaming API provides you with a context manager + # that you can read from similar to how you would read from a file. + with aws_encryption_sdk.stream( + mode="encrypt", source=plaintext, encryption_context=encryption_context, keyring=keyring + ) as encryptor: + # Iterate through the chunks in the context manager + # and write the results to the ciphertext. + for chunk in encryptor: + ciphertext.write(chunk) + + # Verify that the ciphertext and plaintext are different. + assert not filecmp.cmp(source_plaintext_filename, ciphertext_filename) + + # Open the files you want to work with. + with open(ciphertext_filename, "rb") as ciphertext, open(decrypted_filename, "wb") as decrypted: + # Decrypt your encrypted data. + # + # We do not need to specify the encryption context on decrypt + # because the header message includes the encryption context. + with aws_encryption_sdk.stream(mode="decrypt", source=ciphertext, keyring=keyring) as decryptor: + # One benefit of using the streaming API is that + # we can check the encryption context in the header before we start decrypting. + # + # Verify that the encryption context used in the decrypt operation matches what you expect. + # The AWS Encryption SDK can add pairs, so don't require an exact match. + # + # In production, always use a meaningful encryption context. + assert set(encryption_context.items()) <= set(decryptor.header.encryption_context.items()) + + # Now that we are confident that the message is what we think it should be, + # we can start decrypting. + for chunk in decryptor: + decrypted.write(chunk) + + # Verify that the "cycled" (encrypted then decrypted) plaintext + # is identical to the original plaintext. + assert filecmp.cmp(source_plaintext_filename, decrypted_filename) diff --git a/examples/src/in_memory_streaming_defaults.py b/examples/src/in_memory_streaming_defaults.py new file mode 100644 index 000000000..be594f520 --- /dev/null +++ b/examples/src/in_memory_streaming_defaults.py @@ -0,0 +1,75 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +This example shows how to use the streaming encrypt and decrypt APIs when working in memory. + +For the purposes of this example, we demonstrate using AWS KMS, +but you can use other key management options with the AWS Encryption SDK. +Look in the ``keyring`` and ``master_key_provider`` directories +for examples that demonstrate how to use other key management configurations. +""" +import io + +import aws_encryption_sdk +from aws_encryption_sdk.keyrings.aws_kms import KmsKeyring + + +def run(aws_kms_cmk_arn, source_plaintext): + # type: (str, bytes) -> None + """Demonstrate an encrypt/decrypt cycle using the streaming encrypt/decrypt APIs in-memory. + + :param str aws_kms_cmk_arn: AWS KMS CMK ARN to use to protect data keys + :param bytes source_plaintext: Plaintext to encrypt + """ + # Prepare your encryption context. + encryption_context = { + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + + # Create the keyring that determines how your keys are protected. + keyring = KmsKeyring(generator_key_id=aws_kms_cmk_arn) + + ciphertext = io.BytesIO() + + # The streaming API provides you with a context manager + # that you can read from similar to how you would read from a file. + with aws_encryption_sdk.stream( + mode="encrypt", source=source_plaintext, encryption_context=encryption_context, keyring=keyring + ) as encryptor: + # Iterate through the chunks in the context manager + # and write the results to the ciphertext. + for chunk in encryptor: + ciphertext.write(chunk) + + assert ciphertext.getvalue() != source_plaintext + + # Reset the ciphertext stream position so that we can read from the beginning. + ciphertext.seek(0) + + # Decrypt your encrypted data. + # + # We do not need to specify the encryption context on decrypt + # because the header message includes the encryption context. + decrypted = io.BytesIO() + with aws_encryption_sdk.stream(mode="decrypt", source=ciphertext, keyring=keyring) as decryptor: + # One benefit of using the streaming API is that + # we can check the encryption context in the header before we start decrypting. + # + # Verify that the encryption context used in the decrypt operation matches what you expect. + # The AWS Encryption SDK can add pairs, so don't require an exact match. + # + # In production, always use a meaningful encryption context. + assert set(encryption_context.items()) <= set(decryptor.header.encryption_context.items()) + + # Now that we are confident that the message is what we think it should be, + # we can start decrypting. + for chunk in decryptor: + decrypted.write(chunk) + + # Verify that the "cycled" (encrypted then decrypted) plaintext + # is identical to the original plaintext. + assert decrypted.getvalue() == source_plaintext diff --git a/examples/src/oneshot_defaults.py b/examples/src/oneshot_defaults.py new file mode 100644 index 000000000..636b16936 --- /dev/null +++ b/examples/src/oneshot_defaults.py @@ -0,0 +1,56 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +This example shows how to use the one-shot encrypt and decrypt APIs. + +For the purposes of this example, we demonstrate using AWS KMS, +but you can use other key management options with the AWS Encryption SDK. +Look in the ``keyring`` and ``master_key_provider`` directories +for examples that demonstrate how to use other key management configurations. +""" +import aws_encryption_sdk +from aws_encryption_sdk.keyrings.aws_kms import KmsKeyring + + +def run(aws_kms_cmk_arn, source_plaintext): + # type: (str, bytes) -> None + """Demonstrate an encrypt/decrypt cycle using the one-shot encrypt/decrypt APIs. + + :param str aws_kms_cmk_arn: AWS KMS CMK ARN to use to protect data keys + :param bytes source_plaintext: Plaintext to encrypt + """ + # Prepare your encryption context. + encryption_context = { + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + + # Create the keyring that determines how your keys are protected. + keyring = KmsKeyring(generator_key_id=aws_kms_cmk_arn) + + # Encrypt your plaintext data. + ciphertext, _encrypt_header = aws_encryption_sdk.encrypt( + source=source_plaintext, encryption_context=encryption_context, keyring=keyring + ) + + # Verify that the ciphertext and plaintext are different. + assert ciphertext != source_plaintext + + # Decrypt your encrypted data. + # + # We do not need to specify the encryption context on decrypt + # because the header message includes the encryption context. + decrypted, decrypt_header = aws_encryption_sdk.decrypt(source=ciphertext, keyring=keyring) + + # Verify that the "cycled" (encrypted then decrypted) plaintext + # is identical to the original plaintext. + assert decrypted == source_plaintext + + # Verify that the encryption context used in the decrypt operation matches what you expect. + # The AWS Encryption SDK can add pairs, so don't require an exact match. + # + # In production, always use a meaningful encryption context. + assert set(encryption_context.items()) <= set(decrypt_header.encryption_context.items()) diff --git a/examples/src/oneshot_unsigned.py b/examples/src/oneshot_unsigned.py new file mode 100644 index 000000000..50c56e276 --- /dev/null +++ b/examples/src/oneshot_unsigned.py @@ -0,0 +1,76 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +This example shows how to specify an algorithm suite +when using the one-shot encrypt and decrypt APIs. + +For the purposes of this example, we demonstrate using AWS KMS, +but you can use other key management options with the AWS Encryption SDK. +Look in the ``keyring`` and ``master_key_provider`` directories +for examples that demonstrate how to use other key management configurations. + +The default algorithm suite includes a message-level signature +that protects you from an attacker who has *decrypt* but not *encrypt* capability +for a wrapping key that you used when encrypting a message +under multiple wrapping keys. + +However, if all of your readers and writers have the same permissions, +then this additional protection does not always add value. +This example shows you how to select another algorithm suite +that has all of the other properties of the default suite +but does not include a message-level signature. +""" +import aws_encryption_sdk +from aws_encryption_sdk.identifiers import AlgorithmSuite +from aws_encryption_sdk.keyrings.aws_kms import KmsKeyring + + +def run(aws_kms_cmk_arn, source_plaintext): + # type: (str, bytes) -> None + """Demonstrate requesting a specific algorithm suite through the one-shot encrypt/decrypt APIs. + + :param str aws_kms_cmk_arn: AWS KMS CMK ARN to use to protect data keys + :param bytes source_plaintext: Plaintext to encrypt + """ + # Prepare your encryption context. + encryption_context = { + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + + # Create the keyring that determines how your keys are protected. + keyring = KmsKeyring(generator_key_id=aws_kms_cmk_arn) + + # Encrypt your plaintext data. + ciphertext, _encrypt_header = aws_encryption_sdk.encrypt( + source=source_plaintext, + encryption_context=encryption_context, + keyring=keyring, + # Here we can specify the algorithm suite that we want to use. + algorithm=AlgorithmSuite.AES_256_GCM_IV12_TAG16_HKDF_SHA256, + ) + + # Verify that the ciphertext and plaintext are different. + assert ciphertext != source_plaintext + + # Decrypt your encrypted data. + # + # We do not need to specify the encryption context on decrypt + # because the header message includes the encryption context. + # + # We do not need to specify the algorithm suite on decrypt + # because the header message includes the algorithm suite identifier. + decrypted, decrypt_header = aws_encryption_sdk.decrypt(source=ciphertext, keyring=keyring) + + # Verify that the "cycled" (encrypted then decrypted) plaintext + # is identical to the original plaintext. + assert decrypted == source_plaintext + + # Verify that the encryption context used in the decrypt operation matches what you expect. + # The AWS Encryption SDK can add pairs, so don't require an exact match. + # + # In production, always use a meaningful encryption context. + assert set(encryption_context.items()) <= set(decrypt_header.encryption_context.items()) From 52906eb4ff929f38e342b53cadf2c05fb0615e81 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Sun, 8 Mar 2020 17:48:35 -0700 Subject: [PATCH 39/54] docs: add examples readme --- examples/README.md | 56 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 examples/README.md diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 000000000..38b7f8561 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,56 @@ +# AWS Encryption SDK Examples + +Here you can find some examples that show you +how to use the AWS Encryption SDK. +We demonstrate how to use the high-level APIs +as well as how to set up some common configuration patterns. + +## APIs + +The AWS Encryption SDK provides two high-level APIS: +one-shot APIs that process the entire operation in memory +and streaming APIs. + +You can find examples that demonstrate these APIs +in the [`examples/src/`](./src) directory root. + +## Configuration + +In order to use the library APIs, +you must provide some configuration that defines +how you want to protect your data keys. + +### Keyrings + +Keyrings are the most common way for you to configure that AWS Encryption SDK. +These let you define how you want the AWS Encryption SDK to protect your data keys. +You can find these examples in [`examples/src/keyring`](./src/keyring). + +### Cryptographic Materials Managers + +Keyrings define how you want to protect your data keys, +but there is more going on here than just data keys. + +Cryptographic materials managers give you higher-level controls +over how the AWS Encryption SDK protects your data. +This can include things like +enforcing certain algorithm suites or encryption context settings, +reusing data keys across messages, +or changing how you interact with keyrings. +You can find these examples in +[`examples/src/crypto_materials_managers`](./src/crypto_materials_manager). + +### Master Key Providers + +Before there were keyrings, there were master key providers. +Master key providers were the original configuration structure +that we defined for defining how you want to protect your data keys. +Keyrings provide a simpler experience and often more powerful configuration options, +but if you need to use master key providers, +need help migrating from master key providers to keyrings, +or simply want to see the difference between these configuration experiences, +you can find these examples in [`examples/src/master_key_provider`](./src/master_key_provider). + +## Legacy + +The examples in [`examples/src/legacy`](./src/legacy). From 36df978a3b0880d42cfc225a02140cccbd4bfe40 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Mon, 9 Mar 2020 13:03:14 -0700 Subject: [PATCH 40/54] docs: add instructions for writing examples --- examples/README.md | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/examples/README.md b/examples/README.md index 38b7f8561..0ff2dd229 100644 --- a/examples/README.md +++ b/examples/README.md @@ -53,4 +53,41 @@ you can find these examples in [`examples/src/master_key_provider`](./src/master ## Legacy -The examples in [`examples/src/legacy`](./src/legacy). +These are any examples that were already defined +before we started revamping our examples. +We are keeping them around for anyone who needs them as reference material, +but we recommend looking at the newer examples +that should provide a clearer picture of how to use this library. +You can find these examples in [`examples/src/legacy`](./src/legacy). + +# Writing Examples + +If you want to write a new example, that's awesome! +There are a couple things you need to keep in mind, though. +To make sure that your example is tested in our CI, +please make sure that it meets the following requirements: + +1. The example MUST be a distinct module in the [`examples/src/`](./src) directory. +1. The example MAY be nested arbitrarily deeply, + but every intermediate directory MUST contain a `__init__.py` file + so that CPython 2.7 will recognize it as a module. +1. Every example MUST be CPython 2.7 compatible. +1. Each example file MUST contain exactly one example. +1. Each example file MUST contain a function called `run` that runs the example. +1. If your `run` function needs any of the following inputs, + the parameters MUST have the following names: + * `aws_kms_cmk_arn` (`str`) : A single AWS KMS CMK ARN. + * NOTE: You can assume that automatically discovered credentials have + `kms:GenerateDataKey`, `kms:Encrypt`, and `kms:Decrypt` permissions on this CMK. + * `aws_kms_generator_cmk` (`str`) : A single AWS KMS CMK ARN to use as a generator key. + * NOTE: You can assume that automatically discovered credentials have + `kms:GenerateDataKey`, `kms:Encrypt`, and `kms:Decrypt` permissions on this CMK. + * `aws_kms_child_cmks` (`List[str]`) : A list of AWS KMS CMK ARNs to use as child keys. + * NOTE: You can assume that automatically discovered credentials have + `kms:Encrypt` and `kms:Decrypt` permissions on these CMKs. + * `source_plaintext` (`bytes`) : Plaintext data to encrypt. + * `source_plaintext_filename` (`str`) : A path to a file containing plaintext to encrypt. + * NOTE: You can assume that you have write access to the parent directory + and that anything you do in that directory will be cleaned up + by our test runners. +1. Any additional parameters MUST be optional. From 95047f164bab694fe94c4f37bc2e7b47a9020d6c Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Mon, 9 Mar 2020 14:57:09 -0700 Subject: [PATCH 41/54] chore: address PR comments --- examples/README.md | 6 +++--- tox.ini | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/README.md b/examples/README.md index 0ff2dd229..812150c2e 100644 --- a/examples/README.md +++ b/examples/README.md @@ -7,7 +7,7 @@ as well as how to set up some common configuration patterns. ## APIs -The AWS Encryption SDK provides two high-level APIS: +The AWS Encryption SDK provides two high-level APIs: one-shot APIs that process the entire operation in memory and streaming APIs. @@ -38,13 +38,13 @@ enforcing certain algorithm suites or encryption context settings, reusing data keys across messages, or changing how you interact with keyrings. You can find these examples in -[`examples/src/crypto_materials_managers`](./src/crypto_materials_manager). +[`examples/src/crypto_materials_manager`](./src/crypto_materials_manager). ### Master Key Providers Before there were keyrings, there were master key providers. Master key providers were the original configuration structure -that we defined for defining how you want to protect your data keys. +that we provided for defining how you want to protect your data keys. Keyrings provide a simpler experience and often more powerful configuration options, but if you need to use master key providers, need help migrating from master key providers to keyrings, diff --git a/tox.ini b/tox.ini index c073dd2b1..7895b3a0d 100644 --- a/tox.ini +++ b/tox.ini @@ -142,8 +142,8 @@ commands = [testenv:flake8-examples] basepython = {[testenv:flake8]basepython} # This does not actually ignore errors, -# it just runs all commands regardless of whether any fail. -# If any fail, the final result is still a fail. +# it just runs all commands regardless of whether any fail. +# If any fail, the final result will still fail. ignore_errors = true deps = {[testenv:flake8]deps} commands = @@ -170,8 +170,8 @@ commands = [testenv:pylint-examples] basepython = {[testenv:pylint]basepython} # This does not actually ignore errors, -# it just runs all commands regardless of whether any fail. -# If any fail, the final result is still a fail. +# it just runs all commands regardless of whether any fail. +# If any fail, the final result will still fail. ignore_errors = true deps = {[testenv:pylint]deps} commands = From 1815399c447bb805bd58c49bfbddbb2ad7545b4d Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Mon, 9 Mar 2020 14:58:06 -0700 Subject: [PATCH 42/54] chore: change examples parameter from aws_kms_cmk_arn to aws_kms_cmk for consistency --- examples/README.md | 2 +- examples/src/file_streaming_defaults.py | 6 +++--- examples/src/in_memory_streaming_defaults.py | 6 +++--- examples/src/legacy/basic_encryption.py | 6 +++--- .../legacy/basic_file_encryption_with_multiple_providers.py | 6 +++--- examples/src/legacy/data_key_caching_basic.py | 6 +++--- examples/src/legacy/one_kms_cmk.py | 6 +++--- examples/src/legacy/one_kms_cmk_streaming_data.py | 6 +++--- examples/src/legacy/one_kms_cmk_unsigned.py | 6 +++--- examples/src/oneshot_defaults.py | 6 +++--- examples/src/oneshot_unsigned.py | 6 +++--- examples/test/examples_test_utils.py | 2 +- 12 files changed, 32 insertions(+), 32 deletions(-) diff --git a/examples/README.md b/examples/README.md index 812150c2e..8d94a2a76 100644 --- a/examples/README.md +++ b/examples/README.md @@ -76,7 +76,7 @@ please make sure that it meets the following requirements: 1. Each example file MUST contain a function called `run` that runs the example. 1. If your `run` function needs any of the following inputs, the parameters MUST have the following names: - * `aws_kms_cmk_arn` (`str`) : A single AWS KMS CMK ARN. + * `aws_kms_cmk` (`str`) : A single AWS KMS CMK ARN. * NOTE: You can assume that automatically discovered credentials have `kms:GenerateDataKey`, `kms:Encrypt`, and `kms:Decrypt` permissions on this CMK. * `aws_kms_generator_cmk` (`str`) : A single AWS KMS CMK ARN to use as a generator key. diff --git a/examples/src/file_streaming_defaults.py b/examples/src/file_streaming_defaults.py index 8d98b77e8..c1cb7a235 100644 --- a/examples/src/file_streaming_defaults.py +++ b/examples/src/file_streaming_defaults.py @@ -14,11 +14,11 @@ from aws_encryption_sdk.keyrings.aws_kms import KmsKeyring -def run(aws_kms_cmk_arn, source_plaintext_filename): +def run(aws_kms_cmk, source_plaintext_filename): # type: (str, str) -> None """Demonstrate an encrypt/decrypt cycle using the streaming encrypt/decrypt APIs to work with files. - :param str aws_kms_cmk_arn: AWS KMS CMK ARN to use to protect data keys + :param str aws_kms_cmk: AWS KMS CMK ARN to use to protect data keys :param str source_plaintext_filename: Path to plaintext file to encrypt """ # We assume that you can also write in the directory containing the plaintext file, @@ -36,7 +36,7 @@ def run(aws_kms_cmk_arn, source_plaintext_filename): } # Create the keyring that determines how your keys are protected. - keyring = KmsKeyring(generator_key_id=aws_kms_cmk_arn) + keyring = KmsKeyring(generator_key_id=aws_kms_cmk) # Open the files you want to work with. with open(source_plaintext_filename, "rb") as plaintext, open(ciphertext_filename, "wb") as ciphertext: diff --git a/examples/src/in_memory_streaming_defaults.py b/examples/src/in_memory_streaming_defaults.py index be594f520..ff6c1d44e 100644 --- a/examples/src/in_memory_streaming_defaults.py +++ b/examples/src/in_memory_streaming_defaults.py @@ -14,11 +14,11 @@ from aws_encryption_sdk.keyrings.aws_kms import KmsKeyring -def run(aws_kms_cmk_arn, source_plaintext): +def run(aws_kms_cmk, source_plaintext): # type: (str, bytes) -> None """Demonstrate an encrypt/decrypt cycle using the streaming encrypt/decrypt APIs in-memory. - :param str aws_kms_cmk_arn: AWS KMS CMK ARN to use to protect data keys + :param str aws_kms_cmk: AWS KMS CMK ARN to use to protect data keys :param bytes source_plaintext: Plaintext to encrypt """ # Prepare your encryption context. @@ -31,7 +31,7 @@ def run(aws_kms_cmk_arn, source_plaintext): } # Create the keyring that determines how your keys are protected. - keyring = KmsKeyring(generator_key_id=aws_kms_cmk_arn) + keyring = KmsKeyring(generator_key_id=aws_kms_cmk) ciphertext = io.BytesIO() diff --git a/examples/src/legacy/basic_encryption.py b/examples/src/legacy/basic_encryption.py index bed46bd9d..19364a107 100644 --- a/examples/src/legacy/basic_encryption.py +++ b/examples/src/legacy/basic_encryption.py @@ -4,16 +4,16 @@ import aws_encryption_sdk -def run(aws_kms_cmk_arn, source_plaintext, botocore_session=None): +def run(aws_kms_cmk, source_plaintext, botocore_session=None): """Encrypts and then decrypts a string under a KMS customer master key (CMK). - :param str aws_kms_cmk_arn: Amazon Resource Name (ARN) of the KMS CMK + :param str aws_kms_cmk: Amazon Resource Name (ARN) of the KMS CMK :param bytes source_plaintext: Data to encrypt :param botocore_session: existing botocore session instance :type botocore_session: botocore.session.Session """ # Create a KMS master key provider - kms_kwargs = dict(key_ids=[aws_kms_cmk_arn]) + kms_kwargs = dict(key_ids=[aws_kms_cmk]) if botocore_session is not None: kms_kwargs["botocore_session"] = botocore_session master_key_provider = aws_encryption_sdk.KMSMasterKeyProvider(**kms_kwargs) diff --git a/examples/src/legacy/basic_file_encryption_with_multiple_providers.py b/examples/src/legacy/basic_file_encryption_with_multiple_providers.py index aee55c90c..1319025d9 100644 --- a/examples/src/legacy/basic_file_encryption_with_multiple_providers.py +++ b/examples/src/legacy/basic_file_encryption_with_multiple_providers.py @@ -50,12 +50,12 @@ def _get_raw_key(self, key_id): ) -def run(aws_kms_cmk_arn, source_plaintext_filename, botocore_session=None): +def run(aws_kms_cmk, source_plaintext_filename, botocore_session=None): """Encrypts and then decrypts a file using a KMS master key provider and a custom static master key provider. Both master key providers are used to encrypt the plaintext file, so either one alone can decrypt it. - :param str aws_kms_cmk_arn: Amazon Resource Name (ARN) of the KMS Customer Master Key (CMK) + :param str aws_kms_cmk: Amazon Resource Name (ARN) of the KMS Customer Master Key (CMK) (http://docs.aws.amazon.com/kms/latest/developerguide/viewing-keys.html) :param str source_plaintext_filename: Filename of file to encrypt :param botocore_session: existing botocore session instance @@ -67,7 +67,7 @@ def run(aws_kms_cmk_arn, source_plaintext_filename, botocore_session=None): cycled_static_plaintext_filename = source_plaintext_filename + ".static.decrypted" # Create a KMS master key provider - kms_kwargs = dict(key_ids=[aws_kms_cmk_arn]) + kms_kwargs = dict(key_ids=[aws_kms_cmk]) if botocore_session is not None: kms_kwargs["botocore_session"] = botocore_session kms_master_key_provider = aws_encryption_sdk.KMSMasterKeyProvider(**kms_kwargs) diff --git a/examples/src/legacy/data_key_caching_basic.py b/examples/src/legacy/data_key_caching_basic.py index 49f64adf8..899aed294 100644 --- a/examples/src/legacy/data_key_caching_basic.py +++ b/examples/src/legacy/data_key_caching_basic.py @@ -4,10 +4,10 @@ import aws_encryption_sdk -def run(aws_kms_cmk_arn, max_age_in_cache=10.0, cache_capacity=10): +def run(aws_kms_cmk, max_age_in_cache=10.0, cache_capacity=10): """Encrypts a string using an AWS KMS customer master key (CMK) and data key caching. - :param str aws_kms_cmk_arn: Amazon Resource Name (ARN) of the KMS customer master key + :param str aws_kms_cmk: Amazon Resource Name (ARN) of the KMS customer master key :param float max_age_in_cache: Maximum time in seconds that a cached entry can be used :param int cache_capacity: Maximum number of entries to retain in cache at once """ @@ -22,7 +22,7 @@ def run(aws_kms_cmk_arn, max_age_in_cache=10.0, cache_capacity=10): encryption_context = {"purpose": "test"} # Create a master key provider for the KMS customer master key (CMK) - key_provider = aws_encryption_sdk.KMSMasterKeyProvider(key_ids=[aws_kms_cmk_arn]) + key_provider = aws_encryption_sdk.KMSMasterKeyProvider(key_ids=[aws_kms_cmk]) # Create a local cache cache = aws_encryption_sdk.LocalCryptoMaterialsCache(cache_capacity) diff --git a/examples/src/legacy/one_kms_cmk.py b/examples/src/legacy/one_kms_cmk.py index 86611b2e0..7fb5f1431 100644 --- a/examples/src/legacy/one_kms_cmk.py +++ b/examples/src/legacy/one_kms_cmk.py @@ -4,15 +4,15 @@ import aws_encryption_sdk -def run(aws_kms_cmk_arn, source_plaintext, botocore_session=None): +def run(aws_kms_cmk, source_plaintext, botocore_session=None): """Encrypts and then decrypts a string under one KMS customer master key (CMK). - :param str aws_kms_cmk_arn: Amazon Resource Name (ARN) of the KMS CMK + :param str aws_kms_cmk: Amazon Resource Name (ARN) of the KMS CMK :param bytes source_plaintext: Data to encrypt :param botocore_session: existing botocore session instance :type botocore_session: botocore.session.Session """ - kwargs = dict(key_ids=[aws_kms_cmk_arn]) + kwargs = dict(key_ids=[aws_kms_cmk]) if botocore_session is not None: kwargs["botocore_session"] = botocore_session diff --git a/examples/src/legacy/one_kms_cmk_streaming_data.py b/examples/src/legacy/one_kms_cmk_streaming_data.py index 74a433391..90854398d 100644 --- a/examples/src/legacy/one_kms_cmk_streaming_data.py +++ b/examples/src/legacy/one_kms_cmk_streaming_data.py @@ -6,17 +6,17 @@ import aws_encryption_sdk -def run(aws_kms_cmk_arn, source_plaintext_filename, botocore_session=None): +def run(aws_kms_cmk, source_plaintext_filename, botocore_session=None): """Encrypts and then decrypts streaming data under one KMS customer master key (CMK). - :param str aws_kms_cmk_arn: Amazon Resource Name (ARN) of the KMS CMK + :param str aws_kms_cmk: Amazon Resource Name (ARN) of the KMS CMK :param str source_plaintext_filename: Filename of file to encrypt :param botocore_session: existing botocore session instance :type botocore_session: botocore.session.Session """ kwargs = dict() - kwargs["key_ids"] = [aws_kms_cmk_arn] + kwargs["key_ids"] = [aws_kms_cmk] if botocore_session is not None: kwargs["botocore_session"] = botocore_session diff --git a/examples/src/legacy/one_kms_cmk_unsigned.py b/examples/src/legacy/one_kms_cmk_unsigned.py index d8a98a3e2..a127261b7 100644 --- a/examples/src/legacy/one_kms_cmk_unsigned.py +++ b/examples/src/legacy/one_kms_cmk_unsigned.py @@ -7,15 +7,15 @@ from aws_encryption_sdk.identifiers import Algorithm -def run(aws_kms_cmk_arn, source_plaintext, botocore_session=None): +def run(aws_kms_cmk, source_plaintext, botocore_session=None): """Encrypts and then decrypts a string under one KMS customer master key (CMK) with an unsigned algorithm. - :param str aws_kms_cmk_arn: Amazon Resource Name (ARN) of the KMS CMK + :param str aws_kms_cmk: Amazon Resource Name (ARN) of the KMS CMK :param bytes source_plaintext: Data to encrypt :param botocore_session: existing botocore session instance :type botocore_session: botocore.session.Session """ - kwargs = dict(key_ids=[aws_kms_cmk_arn]) + kwargs = dict(key_ids=[aws_kms_cmk]) if botocore_session is not None: kwargs["botocore_session"] = botocore_session diff --git a/examples/src/oneshot_defaults.py b/examples/src/oneshot_defaults.py index 636b16936..74773f785 100644 --- a/examples/src/oneshot_defaults.py +++ b/examples/src/oneshot_defaults.py @@ -12,11 +12,11 @@ from aws_encryption_sdk.keyrings.aws_kms import KmsKeyring -def run(aws_kms_cmk_arn, source_plaintext): +def run(aws_kms_cmk, source_plaintext): # type: (str, bytes) -> None """Demonstrate an encrypt/decrypt cycle using the one-shot encrypt/decrypt APIs. - :param str aws_kms_cmk_arn: AWS KMS CMK ARN to use to protect data keys + :param str aws_kms_cmk: AWS KMS CMK ARN to use to protect data keys :param bytes source_plaintext: Plaintext to encrypt """ # Prepare your encryption context. @@ -29,7 +29,7 @@ def run(aws_kms_cmk_arn, source_plaintext): } # Create the keyring that determines how your keys are protected. - keyring = KmsKeyring(generator_key_id=aws_kms_cmk_arn) + keyring = KmsKeyring(generator_key_id=aws_kms_cmk) # Encrypt your plaintext data. ciphertext, _encrypt_header = aws_encryption_sdk.encrypt( diff --git a/examples/src/oneshot_unsigned.py b/examples/src/oneshot_unsigned.py index 50c56e276..7aa6bd3e5 100644 --- a/examples/src/oneshot_unsigned.py +++ b/examples/src/oneshot_unsigned.py @@ -25,11 +25,11 @@ from aws_encryption_sdk.keyrings.aws_kms import KmsKeyring -def run(aws_kms_cmk_arn, source_plaintext): +def run(aws_kms_cmk, source_plaintext): # type: (str, bytes) -> None """Demonstrate requesting a specific algorithm suite through the one-shot encrypt/decrypt APIs. - :param str aws_kms_cmk_arn: AWS KMS CMK ARN to use to protect data keys + :param str aws_kms_cmk: AWS KMS CMK ARN to use to protect data keys :param bytes source_plaintext: Plaintext to encrypt """ # Prepare your encryption context. @@ -42,7 +42,7 @@ def run(aws_kms_cmk_arn, source_plaintext): } # Create the keyring that determines how your keys are protected. - keyring = KmsKeyring(generator_key_id=aws_kms_cmk_arn) + keyring = KmsKeyring(generator_key_id=aws_kms_cmk) # Encrypt your plaintext data. ciphertext, _encrypt_header = aws_encryption_sdk.encrypt( diff --git a/examples/test/examples_test_utils.py b/examples/test/examples_test_utils.py index 6c438cc0e..a1aa0f6c8 100644 --- a/examples/test/examples_test_utils.py +++ b/examples/test/examples_test_utils.py @@ -19,7 +19,7 @@ HERE = os.path.abspath(os.path.dirname(__file__)) EXAMPLES_SOURCE = os.path.join(HERE, "..", "src") -SINGLE_CMK_ARG = "aws_kms_cmk_arn" +SINGLE_CMK_ARG = "aws_kms_cmk" GENERATOR_CMK_ARG = "aws_kms_generator_cmk" CHILD_CMK_ARG = "aws_kms_child_cmks" PLAINTEXT_ARG = "source_plaintext" From b0c7c805baacae06dc3360d040b62c0267e875ab Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Mon, 9 Mar 2020 15:00:58 -0700 Subject: [PATCH 43/54] docs: clarify integration tests readme --- test/integration/README.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/integration/README.rst b/test/integration/README.rst index eb3453e25..a7dcdd5ac 100644 --- a/test/integration/README.rst +++ b/test/integration/README.rst @@ -8,6 +8,8 @@ In order to run these integration tests successfully, these things must be confi #. Set environment variable ``AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID`` and ``AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2`` to valid `AWS KMS key id`_ to use for integration tests. + These should be AWS KMS CMK ARNs in two different regions. + They will be used for integration tests. .. _automatically discoverable credential locations: http://boto3.readthedocs.io/en/latest/guide/configuration.html .. _AWS KMS key id: http://docs.aws.amazon.com/kms/latest/APIReference/API_Encrypt.html From c54f21aea4d5826ac674b0c2c8d5ae32d390d07a Mon Sep 17 00:00:00 2001 From: Matt Bullock Date: Mon, 9 Mar 2020 16:55:27 -0700 Subject: [PATCH 44/54] Apply suggestions from code review Co-Authored-By: June Blender --- examples/README.md | 28 ++++++++++++------------- examples/src/file_streaming_defaults.py | 16 +++++++------- 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/examples/README.md b/examples/README.md index 8d94a2a76..4f0dc0c91 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,9 +1,9 @@ # AWS Encryption SDK Examples -Here you can find some examples that show you +This section features examples that show you how to use the AWS Encryption SDK. -We demonstrate how to use the high-level APIs -as well as how to set up some common configuration patterns. +We demonstrate how to use the encryption and decryption APIs +and how to set up some common configuration patterns. ## APIs @@ -17,24 +17,24 @@ in the [`examples/src/`](./src) directory root. ## Configuration In order to use the library APIs, -you must provide some configuration that defines +you must provide a configuration that defines how you want to protect your data keys. ### Keyrings -Keyrings are the most common way for you to configure that AWS Encryption SDK. -These let you define how you want the AWS Encryption SDK to protect your data keys. +Keyrings are the most common way for you to configure the AWS Encryption SDK. +They determine how the AWS Encryption SDK protects your data. You can find these examples in [`examples/src/keyring`](./src/keyring). ### Cryptographic Materials Managers -Keyrings define how you want to protect your data keys, -but there is more going on here than just data keys. +Keyrings define how your data keys are protected, +but there is more going on here than just protecting data keys. Cryptographic materials managers give you higher-level controls over how the AWS Encryption SDK protects your data. This can include things like -enforcing certain algorithm suites or encryption context settings, +enforcing the use of certain algorithm suites or encryption context settings, reusing data keys across messages, or changing how you interact with keyrings. You can find these examples in @@ -53,17 +53,15 @@ you can find these examples in [`examples/src/master_key_provider`](./src/master ## Legacy -These are any examples that were already defined -before we started revamping our examples. -We are keeping them around for anyone who needs them as reference material, +This section includes older examples, including examples of using master keys and master key providers in Java and Python. +You can use them as a reference, but we recommend looking at the newer examples -that should provide a clearer picture of how to use this library. +but we recommend looking at the newer examples, which explain the preferred ways of using this library. You can find these examples in [`examples/src/legacy`](./src/legacy). # Writing Examples -If you want to write a new example, that's awesome! -There are a couple things you need to keep in mind, though. +If you want to contribute a new example, that's awesome! To make sure that your example is tested in our CI, please make sure that it meets the following requirements: diff --git a/examples/src/file_streaming_defaults.py b/examples/src/file_streaming_defaults.py index c1cb7a235..9509aaf51 100644 --- a/examples/src/file_streaming_defaults.py +++ b/examples/src/file_streaming_defaults.py @@ -3,7 +3,7 @@ """ This example shows how to use the streaming encrypt and decrypt APIs when working with files. -For the purposes of this example, we demonstrate using AWS KMS, +This example uses an AWS KMS CMK, but you can use other key management options with the AWS Encryption SDK. Look in the ``keyring`` and ``master_key_provider`` directories for examples that demonstrate how to use other key management configurations. @@ -21,7 +21,7 @@ def run(aws_kms_cmk, source_plaintext_filename): :param str aws_kms_cmk: AWS KMS CMK ARN to use to protect data keys :param str source_plaintext_filename: Path to plaintext file to encrypt """ - # We assume that you can also write in the directory containing the plaintext file, + # We assume that you can also write to the directory containing the plaintext file, # so that is where we will put all of the results. ciphertext_filename = source_plaintext_filename + ".encrypted" decrypted_filename = ciphertext_filename + ".decrypted" @@ -35,13 +35,13 @@ def run(aws_kms_cmk, source_plaintext_filename): "the data you are handling": "is what you think it is", } - # Create the keyring that determines how your keys are protected. + # Create the keyring that determines how your data keys are protected. keyring = KmsKeyring(generator_key_id=aws_kms_cmk) # Open the files you want to work with. with open(source_plaintext_filename, "rb") as plaintext, open(ciphertext_filename, "wb") as ciphertext: - # The streaming API provides you with a context manager - # that you can read from similar to how you would read from a file. + # The streaming API provides a context manager. + # You can read from it just as you read from a file. with aws_encryption_sdk.stream( mode="encrypt", source=plaintext, encryption_context=encryption_context, keyring=keyring ) as encryptor: @@ -63,17 +63,17 @@ def run(aws_kms_cmk, source_plaintext_filename): # One benefit of using the streaming API is that # we can check the encryption context in the header before we start decrypting. # - # Verify that the encryption context used in the decrypt operation matches what you expect. + # Verify that the encryption context used in the decrypt operation includes the encryption context that you specified when encrypting. # The AWS Encryption SDK can add pairs, so don't require an exact match. # # In production, always use a meaningful encryption context. assert set(encryption_context.items()) <= set(decryptor.header.encryption_context.items()) - # Now that we are confident that the message is what we think it should be, + # Now that we are more confident that we will decrypt the right message, # we can start decrypting. for chunk in decryptor: decrypted.write(chunk) - # Verify that the "cycled" (encrypted then decrypted) plaintext + # Verify that the decrypted plaintext # is identical to the original plaintext. assert filecmp.cmp(source_plaintext_filename, decrypted_filename) From 727ebab4e55548ce54975148ccc69b5ff47b443c Mon Sep 17 00:00:00 2001 From: Matt Bullock Date: Mon, 9 Mar 2020 17:03:31 -0700 Subject: [PATCH 45/54] Apply suggestions from code review Co-Authored-By: June Blender --- examples/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/README.md b/examples/README.md index 4f0dc0c91..a425b183b 100644 --- a/examples/README.md +++ b/examples/README.md @@ -16,7 +16,7 @@ in the [`examples/src/`](./src) directory root. ## Configuration -In order to use the library APIs, +To use the library APIs, you must provide a configuration that defines how you want to protect your data keys. From 0af1cf9ff434fa8cc7a675d1318529cb60c47dfc Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Mon, 9 Mar 2020 17:10:53 -0700 Subject: [PATCH 46/54] docs: change from "one-shot" term to "one-step" --- examples/README.md | 2 +- examples/src/{oneshot_defaults.py => onestep_defaults.py} | 4 ++-- examples/src/{oneshot_unsigned.py => onestep_unsigned.py} | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) rename examples/src/{oneshot_defaults.py => onestep_defaults.py} (94%) rename examples/src/{oneshot_unsigned.py => onestep_unsigned.py} (97%) diff --git a/examples/README.md b/examples/README.md index a425b183b..60b1e201f 100644 --- a/examples/README.md +++ b/examples/README.md @@ -8,7 +8,7 @@ and how to set up some common configuration patterns. ## APIs The AWS Encryption SDK provides two high-level APIs: -one-shot APIs that process the entire operation in memory +one-step APIs that process the entire operation in memory and streaming APIs. You can find examples that demonstrate these APIs diff --git a/examples/src/oneshot_defaults.py b/examples/src/onestep_defaults.py similarity index 94% rename from examples/src/oneshot_defaults.py rename to examples/src/onestep_defaults.py index 74773f785..0b14b27ba 100644 --- a/examples/src/oneshot_defaults.py +++ b/examples/src/onestep_defaults.py @@ -1,7 +1,7 @@ # Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0 """ -This example shows how to use the one-shot encrypt and decrypt APIs. +This example shows how to use the one-step encrypt and decrypt APIs. For the purposes of this example, we demonstrate using AWS KMS, but you can use other key management options with the AWS Encryption SDK. @@ -14,7 +14,7 @@ def run(aws_kms_cmk, source_plaintext): # type: (str, bytes) -> None - """Demonstrate an encrypt/decrypt cycle using the one-shot encrypt/decrypt APIs. + """Demonstrate an encrypt/decrypt cycle using the one-step encrypt/decrypt APIs. :param str aws_kms_cmk: AWS KMS CMK ARN to use to protect data keys :param bytes source_plaintext: Plaintext to encrypt diff --git a/examples/src/oneshot_unsigned.py b/examples/src/onestep_unsigned.py similarity index 97% rename from examples/src/oneshot_unsigned.py rename to examples/src/onestep_unsigned.py index 7aa6bd3e5..96e4016b1 100644 --- a/examples/src/oneshot_unsigned.py +++ b/examples/src/onestep_unsigned.py @@ -2,7 +2,7 @@ # SPDX-License-Identifier: Apache-2.0 """ This example shows how to specify an algorithm suite -when using the one-shot encrypt and decrypt APIs. +when using the one-step encrypt and decrypt APIs. For the purposes of this example, we demonstrate using AWS KMS, but you can use other key management options with the AWS Encryption SDK. @@ -27,7 +27,7 @@ def run(aws_kms_cmk, source_plaintext): # type: (str, bytes) -> None - """Demonstrate requesting a specific algorithm suite through the one-shot encrypt/decrypt APIs. + """Demonstrate requesting a specific algorithm suite through the one-step encrypt/decrypt APIs. :param str aws_kms_cmk: AWS KMS CMK ARN to use to protect data keys :param bytes source_plaintext: Plaintext to encrypt From 68b35c39bfa2fdd50d10be40fe336fb71fd671df Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Mon, 9 Mar 2020 17:27:18 -0700 Subject: [PATCH 47/54] chore: apply changes based on PR comments --- examples/src/file_streaming_defaults.py | 27 ++++++++------- examples/src/in_memory_streaming_defaults.py | 36 +++++++++++--------- examples/src/onestep_defaults.py | 15 ++++---- examples/src/onestep_unsigned.py | 15 ++++---- 4 files changed, 51 insertions(+), 42 deletions(-) diff --git a/examples/src/file_streaming_defaults.py b/examples/src/file_streaming_defaults.py index 9509aaf51..f7c349019 100644 --- a/examples/src/file_streaming_defaults.py +++ b/examples/src/file_streaming_defaults.py @@ -3,10 +3,13 @@ """ This example shows how to use the streaming encrypt and decrypt APIs when working with files. +One benefit of using the streaming API is that +we can check the encryption context in the header before we start decrypting. + This example uses an AWS KMS CMK, but you can use other key management options with the AWS Encryption SDK. -Look in the ``keyring`` and ``master_key_provider`` directories -for examples that demonstrate how to use other key management configurations. +For examples that demonstrate how to use other key management configurations, +see the ``keyring`` and ``mater_key_provider`` directories. """ import filecmp @@ -27,6 +30,7 @@ def run(aws_kms_cmk, source_plaintext_filename): decrypted_filename = ciphertext_filename + ".decrypted" # Prepare your encryption context. + # https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context encryption_context = { "encryption": "context", "is not": "secret", @@ -45,10 +49,10 @@ def run(aws_kms_cmk, source_plaintext_filename): with aws_encryption_sdk.stream( mode="encrypt", source=plaintext, encryption_context=encryption_context, keyring=keyring ) as encryptor: - # Iterate through the chunks in the context manager + # Iterate through the segments in the context manager # and write the results to the ciphertext. - for chunk in encryptor: - ciphertext.write(chunk) + for segment in encryptor: + ciphertext.write(segment) # Verify that the ciphertext and plaintext are different. assert not filecmp.cmp(source_plaintext_filename, ciphertext_filename) @@ -60,10 +64,10 @@ def run(aws_kms_cmk, source_plaintext_filename): # We do not need to specify the encryption context on decrypt # because the header message includes the encryption context. with aws_encryption_sdk.stream(mode="decrypt", source=ciphertext, keyring=keyring) as decryptor: - # One benefit of using the streaming API is that - # we can check the encryption context in the header before we start decrypting. + # Check the encryption context in the header before we start decrypting. # - # Verify that the encryption context used in the decrypt operation includes the encryption context that you specified when encrypting. + # Verify that the encryption context used in the decrypt operation includes + # the encryption context that you specified when encrypting. # The AWS Encryption SDK can add pairs, so don't require an exact match. # # In production, always use a meaningful encryption context. @@ -71,9 +75,8 @@ def run(aws_kms_cmk, source_plaintext_filename): # Now that we are more confident that we will decrypt the right message, # we can start decrypting. - for chunk in decryptor: - decrypted.write(chunk) + for segment in decryptor: + decrypted.write(segment) - # Verify that the decrypted plaintext - # is identical to the original plaintext. + # Verify that the decrypted plaintext is identical to the original plaintext. assert filecmp.cmp(source_plaintext_filename, decrypted_filename) diff --git a/examples/src/in_memory_streaming_defaults.py b/examples/src/in_memory_streaming_defaults.py index ff6c1d44e..38f00cf32 100644 --- a/examples/src/in_memory_streaming_defaults.py +++ b/examples/src/in_memory_streaming_defaults.py @@ -3,10 +3,13 @@ """ This example shows how to use the streaming encrypt and decrypt APIs when working in memory. +One benefit of using the streaming API is that +we can check the encryption context in the header before we start decrypting. + For the purposes of this example, we demonstrate using AWS KMS, but you can use other key management options with the AWS Encryption SDK. -Look in the ``keyring`` and ``master_key_provider`` directories -for examples that demonstrate how to use other key management configurations. +For examples that demonstrate how to use other key management configurations, +see the ``keyring`` and ``mater_key_provider`` directories. """ import io @@ -22,6 +25,7 @@ def run(aws_kms_cmk, source_plaintext): :param bytes source_plaintext: Plaintext to encrypt """ # Prepare your encryption context. + # https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context encryption_context = { "encryption": "context", "is not": "secret", @@ -30,21 +34,22 @@ def run(aws_kms_cmk, source_plaintext): "the data you are handling": "is what you think it is", } - # Create the keyring that determines how your keys are protected. + # Create the keyring that determines how your data keys are protected. keyring = KmsKeyring(generator_key_id=aws_kms_cmk) ciphertext = io.BytesIO() - # The streaming API provides you with a context manager - # that you can read from similar to how you would read from a file. + # The streaming API provides a context manager. + # You can read from it just as you read from a file. with aws_encryption_sdk.stream( mode="encrypt", source=source_plaintext, encryption_context=encryption_context, keyring=keyring ) as encryptor: - # Iterate through the chunks in the context manager + # Iterate through the segments in the context manager # and write the results to the ciphertext. - for chunk in encryptor: - ciphertext.write(chunk) + for segment in encryptor: + ciphertext.write(segment) + # Verify that the ciphertext and plaintext are different. assert ciphertext.getvalue() != source_plaintext # Reset the ciphertext stream position so that we can read from the beginning. @@ -56,20 +61,19 @@ def run(aws_kms_cmk, source_plaintext): # because the header message includes the encryption context. decrypted = io.BytesIO() with aws_encryption_sdk.stream(mode="decrypt", source=ciphertext, keyring=keyring) as decryptor: - # One benefit of using the streaming API is that - # we can check the encryption context in the header before we start decrypting. + # Check the encryption context in the header before we start decrypting. # - # Verify that the encryption context used in the decrypt operation matches what you expect. + # Verify that the encryption context used in the decrypt operation includes + # the encryption context that you specified when encrypting. # The AWS Encryption SDK can add pairs, so don't require an exact match. # # In production, always use a meaningful encryption context. assert set(encryption_context.items()) <= set(decryptor.header.encryption_context.items()) - # Now that we are confident that the message is what we think it should be, + # Now that we are more confident that we will decrypt the right message, # we can start decrypting. - for chunk in decryptor: - decrypted.write(chunk) + for segment in decryptor: + decrypted.write(segment) - # Verify that the "cycled" (encrypted then decrypted) plaintext - # is identical to the original plaintext. + # Verify that the decrypted plaintext is identical to the original plaintext. assert decrypted.getvalue() == source_plaintext diff --git a/examples/src/onestep_defaults.py b/examples/src/onestep_defaults.py index 0b14b27ba..fe5652ced 100644 --- a/examples/src/onestep_defaults.py +++ b/examples/src/onestep_defaults.py @@ -3,10 +3,10 @@ """ This example shows how to use the one-step encrypt and decrypt APIs. -For the purposes of this example, we demonstrate using AWS KMS, +This example uses an AWS KMS CMK, but you can use other key management options with the AWS Encryption SDK. -Look in the ``keyring`` and ``master_key_provider`` directories -for examples that demonstrate how to use other key management configurations. +For examples that demonstrate how to use other key management configurations, +see the ``keyring`` and ``mater_key_provider`` directories. """ import aws_encryption_sdk from aws_encryption_sdk.keyrings.aws_kms import KmsKeyring @@ -20,6 +20,7 @@ def run(aws_kms_cmk, source_plaintext): :param bytes source_plaintext: Plaintext to encrypt """ # Prepare your encryption context. + # https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context encryption_context = { "encryption": "context", "is not": "secret", @@ -28,7 +29,7 @@ def run(aws_kms_cmk, source_plaintext): "the data you are handling": "is what you think it is", } - # Create the keyring that determines how your keys are protected. + # Create the keyring that determines how your data keys are protected. keyring = KmsKeyring(generator_key_id=aws_kms_cmk) # Encrypt your plaintext data. @@ -45,11 +46,11 @@ def run(aws_kms_cmk, source_plaintext): # because the header message includes the encryption context. decrypted, decrypt_header = aws_encryption_sdk.decrypt(source=ciphertext, keyring=keyring) - # Verify that the "cycled" (encrypted then decrypted) plaintext - # is identical to the original plaintext. + # Verify that the decrypted plaintext is identical to the original plaintext. assert decrypted == source_plaintext - # Verify that the encryption context used in the decrypt operation matches what you expect. + # Verify that the encryption context used in the decrypt operation includes + # the encryption context that you specified when encrypting. # The AWS Encryption SDK can add pairs, so don't require an exact match. # # In production, always use a meaningful encryption context. diff --git a/examples/src/onestep_unsigned.py b/examples/src/onestep_unsigned.py index 96e4016b1..db32deb11 100644 --- a/examples/src/onestep_unsigned.py +++ b/examples/src/onestep_unsigned.py @@ -4,10 +4,10 @@ This example shows how to specify an algorithm suite when using the one-step encrypt and decrypt APIs. -For the purposes of this example, we demonstrate using AWS KMS, +This example uses an AWS KMS CMK, but you can use other key management options with the AWS Encryption SDK. -Look in the ``keyring`` and ``master_key_provider`` directories -for examples that demonstrate how to use other key management configurations. +For examples that demonstrate how to use other key management configurations, +see the ``keyring`` and ``mater_key_provider`` directories. The default algorithm suite includes a message-level signature that protects you from an attacker who has *decrypt* but not *encrypt* capability @@ -33,6 +33,7 @@ def run(aws_kms_cmk, source_plaintext): :param bytes source_plaintext: Plaintext to encrypt """ # Prepare your encryption context. + # https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context encryption_context = { "encryption": "context", "is not": "secret", @@ -41,7 +42,7 @@ def run(aws_kms_cmk, source_plaintext): "the data you are handling": "is what you think it is", } - # Create the keyring that determines how your keys are protected. + # Create the keyring that determines how your data keys are protected. keyring = KmsKeyring(generator_key_id=aws_kms_cmk) # Encrypt your plaintext data. @@ -65,11 +66,11 @@ def run(aws_kms_cmk, source_plaintext): # because the header message includes the algorithm suite identifier. decrypted, decrypt_header = aws_encryption_sdk.decrypt(source=ciphertext, keyring=keyring) - # Verify that the "cycled" (encrypted then decrypted) plaintext - # is identical to the original plaintext. + # Verify that the decrypted plaintext is identical to the original plaintext. assert decrypted == source_plaintext - # Verify that the encryption context used in the decrypt operation matches what you expect. + # Verify that the encryption context used in the decrypt operation includes + # the encryption context that you specified when encrypting. # The AWS Encryption SDK can add pairs, so don't require an exact match. # # In production, always use a meaningful encryption context. From f054b123fb6f9a5d2381d3f3be45b74f5be67e6d Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Tue, 10 Mar 2020 10:17:06 -0700 Subject: [PATCH 48/54] docs: reword parameter description --- examples/src/file_streaming_defaults.py | 2 +- examples/src/in_memory_streaming_defaults.py | 2 +- examples/src/onestep_defaults.py | 2 +- examples/src/onestep_unsigned.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/src/file_streaming_defaults.py b/examples/src/file_streaming_defaults.py index f7c349019..d900e3b83 100644 --- a/examples/src/file_streaming_defaults.py +++ b/examples/src/file_streaming_defaults.py @@ -21,7 +21,7 @@ def run(aws_kms_cmk, source_plaintext_filename): # type: (str, str) -> None """Demonstrate an encrypt/decrypt cycle using the streaming encrypt/decrypt APIs to work with files. - :param str aws_kms_cmk: AWS KMS CMK ARN to use to protect data keys + :param str aws_kms_cmk: The ARN of an AWS KMS CMK that protects data keys :param str source_plaintext_filename: Path to plaintext file to encrypt """ # We assume that you can also write to the directory containing the plaintext file, diff --git a/examples/src/in_memory_streaming_defaults.py b/examples/src/in_memory_streaming_defaults.py index 38f00cf32..21ec1c20c 100644 --- a/examples/src/in_memory_streaming_defaults.py +++ b/examples/src/in_memory_streaming_defaults.py @@ -21,7 +21,7 @@ def run(aws_kms_cmk, source_plaintext): # type: (str, bytes) -> None """Demonstrate an encrypt/decrypt cycle using the streaming encrypt/decrypt APIs in-memory. - :param str aws_kms_cmk: AWS KMS CMK ARN to use to protect data keys + :param str aws_kms_cmk: The ARN of an AWS KMS CMK that protects data keys :param bytes source_plaintext: Plaintext to encrypt """ # Prepare your encryption context. diff --git a/examples/src/onestep_defaults.py b/examples/src/onestep_defaults.py index fe5652ced..066898c55 100644 --- a/examples/src/onestep_defaults.py +++ b/examples/src/onestep_defaults.py @@ -16,7 +16,7 @@ def run(aws_kms_cmk, source_plaintext): # type: (str, bytes) -> None """Demonstrate an encrypt/decrypt cycle using the one-step encrypt/decrypt APIs. - :param str aws_kms_cmk: AWS KMS CMK ARN to use to protect data keys + :param str aws_kms_cmk: The ARN of an AWS KMS CMK that protects data keys :param bytes source_plaintext: Plaintext to encrypt """ # Prepare your encryption context. diff --git a/examples/src/onestep_unsigned.py b/examples/src/onestep_unsigned.py index db32deb11..47876b711 100644 --- a/examples/src/onestep_unsigned.py +++ b/examples/src/onestep_unsigned.py @@ -29,7 +29,7 @@ def run(aws_kms_cmk, source_plaintext): # type: (str, bytes) -> None """Demonstrate requesting a specific algorithm suite through the one-step encrypt/decrypt APIs. - :param str aws_kms_cmk: AWS KMS CMK ARN to use to protect data keys + :param str aws_kms_cmk: The ARN of an AWS KMS CMK that protects data keys :param bytes source_plaintext: Plaintext to encrypt """ # Prepare your encryption context. From d31b40d434038a9e11d0c659650c874dcd03b252 Mon Sep 17 00:00:00 2001 From: Matt Bullock Date: Tue, 10 Mar 2020 11:45:15 -0700 Subject: [PATCH 49/54] Apply suggestions from code review Co-Authored-By: June Blender --- examples/README.md | 3 +-- examples/src/file_streaming_defaults.py | 6 +++--- examples/src/in_memory_streaming_defaults.py | 4 ++-- examples/src/legacy/__init__.py | 6 +----- examples/src/legacy/basic_encryption.py | 4 ++-- 5 files changed, 9 insertions(+), 14 deletions(-) diff --git a/examples/README.md b/examples/README.md index 60b1e201f..388bb927c 100644 --- a/examples/README.md +++ b/examples/README.md @@ -12,7 +12,7 @@ one-step APIs that process the entire operation in memory and streaming APIs. You can find examples that demonstrate these APIs -in the [`examples/src/`](./src) directory root. +in the [`examples/src/`](./src) directory. ## Configuration @@ -55,7 +55,6 @@ you can find these examples in [`examples/src/master_key_provider`](./src/master This section includes older examples, including examples of using master keys and master key providers in Java and Python. You can use them as a reference, -but we recommend looking at the newer examples but we recommend looking at the newer examples, which explain the preferred ways of using this library. You can find these examples in [`examples/src/legacy`](./src/legacy). diff --git a/examples/src/file_streaming_defaults.py b/examples/src/file_streaming_defaults.py index d900e3b83..6a0f88220 100644 --- a/examples/src/file_streaming_defaults.py +++ b/examples/src/file_streaming_defaults.py @@ -9,7 +9,7 @@ This example uses an AWS KMS CMK, but you can use other key management options with the AWS Encryption SDK. For examples that demonstrate how to use other key management configurations, -see the ``keyring`` and ``mater_key_provider`` directories. +see the ``keyring`` and ``master_key_provider`` directories. """ import filecmp @@ -19,7 +19,7 @@ def run(aws_kms_cmk, source_plaintext_filename): # type: (str, str) -> None - """Demonstrate an encrypt/decrypt cycle using the streaming encrypt/decrypt APIs to work with files. + """Demonstrate an encrypt/decrypt cycle using the streaming encrypt/decrypt APIs with files. :param str aws_kms_cmk: The ARN of an AWS KMS CMK that protects data keys :param str source_plaintext_filename: Path to plaintext file to encrypt @@ -62,7 +62,7 @@ def run(aws_kms_cmk, source_plaintext_filename): # Decrypt your encrypted data. # # We do not need to specify the encryption context on decrypt - # because the header message includes the encryption context. + # because the message header includes the encryption context. with aws_encryption_sdk.stream(mode="decrypt", source=ciphertext, keyring=keyring) as decryptor: # Check the encryption context in the header before we start decrypting. # diff --git a/examples/src/in_memory_streaming_defaults.py b/examples/src/in_memory_streaming_defaults.py index 21ec1c20c..bf4cb4aef 100644 --- a/examples/src/in_memory_streaming_defaults.py +++ b/examples/src/in_memory_streaming_defaults.py @@ -1,12 +1,12 @@ # Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0 """ -This example shows how to use the streaming encrypt and decrypt APIs when working in memory. +This example shows how to use the streaming encrypt and decrypt APIs on data in memory. One benefit of using the streaming API is that we can check the encryption context in the header before we start decrypting. -For the purposes of this example, we demonstrate using AWS KMS, +In this example, we use an AWS KMS customer master key (CMK), but you can use other key management options with the AWS Encryption SDK. For examples that demonstrate how to use other key management configurations, see the ``keyring`` and ``mater_key_provider`` directories. diff --git a/examples/src/legacy/__init__.py b/examples/src/legacy/__init__.py index f91696b88..58f6279a1 100644 --- a/examples/src/legacy/__init__.py +++ b/examples/src/legacy/__init__.py @@ -3,9 +3,5 @@ """ Legacy examples. -These are any examples that were already defined -before we started revamping our examples. -We are keeping them around for anyone who needs them as reference material, -but we recommend looking at the newer examples -that should provide a clearer picture of how to use this library. +We keep these older examples as reference material, but we recommend that you use the new examples. They new examples reflect our current guidance for using the library. """ diff --git a/examples/src/legacy/basic_encryption.py b/examples/src/legacy/basic_encryption.py index 19364a107..81db7a102 100644 --- a/examples/src/legacy/basic_encryption.py +++ b/examples/src/legacy/basic_encryption.py @@ -1,13 +1,13 @@ # Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0 -"""Example showing basic encryption and decryption of a value already in memory.""" +"""Example showing how to encrypt and decrypt a value in memory.""" import aws_encryption_sdk def run(aws_kms_cmk, source_plaintext, botocore_session=None): """Encrypts and then decrypts a string under a KMS customer master key (CMK). - :param str aws_kms_cmk: Amazon Resource Name (ARN) of the KMS CMK + :param str aws_kms_cmk: Amazon Resource Name (ARN) of the AWS KMS CMK :param bytes source_plaintext: Data to encrypt :param botocore_session: existing botocore session instance :type botocore_session: botocore.session.Session From 57077b1486caff618ab46693f041875ab11e94d8 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Tue, 10 Mar 2020 11:54:58 -0700 Subject: [PATCH 50/54] chore: rename examples input parameters to move from "child" to "additional" naming --- examples/README.md | 3 ++- examples/src/legacy/multiple_kms_cmk_regions.py | 13 +++++++------ examples/test/examples_test_utils.py | 4 ++-- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/examples/README.md b/examples/README.md index 388bb927c..60d57d5fe 100644 --- a/examples/README.md +++ b/examples/README.md @@ -79,7 +79,8 @@ please make sure that it meets the following requirements: * `aws_kms_generator_cmk` (`str`) : A single AWS KMS CMK ARN to use as a generator key. * NOTE: You can assume that automatically discovered credentials have `kms:GenerateDataKey`, `kms:Encrypt`, and `kms:Decrypt` permissions on this CMK. - * `aws_kms_child_cmks` (`List[str]`) : A list of AWS KMS CMK ARNs to use as child keys. + * `aws_kms_additional_cmks` (`List[str]`) : + A list of AWS KMS CMK ARNs to use for encrypting and decrypting data keys. * NOTE: You can assume that automatically discovered credentials have `kms:Encrypt` and `kms:Decrypt` permissions on these CMKs. * `source_plaintext` (`bytes`) : Plaintext data to encrypt. diff --git a/examples/src/legacy/multiple_kms_cmk_regions.py b/examples/src/legacy/multiple_kms_cmk_regions.py index 16c0170bc..deefd73e9 100644 --- a/examples/src/legacy/multiple_kms_cmk_regions.py +++ b/examples/src/legacy/multiple_kms_cmk_regions.py @@ -8,20 +8,21 @@ from aws_encryption_sdk.key_providers.kms import KMSMasterKey, KMSMasterKeyProvider -def run(aws_kms_generator_cmk, aws_kms_child_cmks, source_plaintext, botocore_session=None): +def run(aws_kms_generator_cmk, aws_kms_additional_cmks, source_plaintext, botocore_session=None): """Encrypts and then decrypts a string under multiple KMS customer master keys (CMKs) in multiple regions. :param str aws_kms_generator_cmk: Amazon Resource Name (ARN) of the primary KMS CMK - :param List[str] aws_kms_child_cmks: Additional Amazon Resource Names (ARNs) of secondary KMS CMKs + :param List[str] aws_kms_additional_cmks: Additional Amazon Resource Names (ARNs) of secondary KMS CMKs :param bytes source_plaintext: Data to encrypt :param botocore_session: existing botocore session instance :type botocore_session: botocore.session.Session """ - child_cmk = aws_kms_child_cmks[0] + encrypt_cmk = aws_kms_additional_cmks[0] + # Check that these keys are in different regions - assert not aws_kms_generator_cmk.split(":")[3] == child_cmk.split(":")[3] + assert not aws_kms_generator_cmk.split(":")[3] == encrypt_cmk.split(":")[3] - kwargs = dict(key_ids=[aws_kms_generator_cmk, child_cmk]) + kwargs = dict(key_ids=[aws_kms_generator_cmk, encrypt_cmk]) if botocore_session is not None: kwargs["botocore_session"] = botocore_session @@ -43,7 +44,7 @@ def run(aws_kms_generator_cmk, aws_kms_child_cmks, source_plaintext, botocore_se key_provider=KMSMasterKey(key_id=aws_kms_generator_cmk), source=ciphertext ) plaintext_2, decrypted_message_header_2 = aws_encryption_sdk.decrypt( - key_provider=KMSMasterKey(key_id=child_cmk), source=ciphertext + key_provider=KMSMasterKey(key_id=encrypt_cmk), source=ciphertext ) # Check that the original message and the decrypted message are the same diff --git a/examples/test/examples_test_utils.py b/examples/test/examples_test_utils.py index a1aa0f6c8..4f9aadef5 100644 --- a/examples/test/examples_test_utils.py +++ b/examples/test/examples_test_utils.py @@ -21,7 +21,7 @@ EXAMPLES_SOURCE = os.path.join(HERE, "..", "src") SINGLE_CMK_ARG = "aws_kms_cmk" GENERATOR_CMK_ARG = "aws_kms_generator_cmk" -CHILD_CMK_ARG = "aws_kms_child_cmks" +ADDITIONAL_CMKS_ARG = "aws_kms_additional_cmks" PLAINTEXT_ARG = "source_plaintext" PLAINTEXT_FILE_ARG = "source_plaintext_filename" @@ -103,7 +103,7 @@ def build_kwargs(function, temp_dir): possible_kwargs = { SINGLE_CMK_ARG: cmk_arns[0], GENERATOR_CMK_ARG: cmk_arns[0], - CHILD_CMK_ARG: cmk_arns[1:], + ADDITIONAL_CMKS_ARG: cmk_arns[1:], PLAINTEXT_ARG: static_plaintext, PLAINTEXT_FILE_ARG: str(plaintext_file.absolute()), } From f1469f11d3ba785593bfe410d0b2cca231f81f38 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Tue, 10 Mar 2020 12:27:05 -0700 Subject: [PATCH 51/54] docs: clarify configuration intro --- examples/README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/examples/README.md b/examples/README.md index 60d57d5fe..f370f7407 100644 --- a/examples/README.md +++ b/examples/README.md @@ -17,8 +17,11 @@ in the [`examples/src/`](./src) directory. ## Configuration To use the library APIs, -you must provide a configuration that defines -how you want to protect your data keys. +you need to describe how you want the library to protect your data keys. +You can do this using +[keyrings][#keyrings] or [cryptographic materials managers][#cryptographic-materials-managers], +or using [master key providers][#master-key-providers]. +These examples will show you how. ### Keyrings From 586f88e5bdaabef42487bd4f5e2f3000cba18074 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Tue, 10 Mar 2020 12:29:06 -0700 Subject: [PATCH 52/54] docs: apply examples comments consistently --- examples/src/file_streaming_defaults.py | 4 ++-- examples/src/in_memory_streaming_defaults.py | 2 +- examples/src/onestep_defaults.py | 4 ++-- examples/src/onestep_unsigned.py | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/src/file_streaming_defaults.py b/examples/src/file_streaming_defaults.py index 6a0f88220..983bfc90d 100644 --- a/examples/src/file_streaming_defaults.py +++ b/examples/src/file_streaming_defaults.py @@ -6,7 +6,7 @@ One benefit of using the streaming API is that we can check the encryption context in the header before we start decrypting. -This example uses an AWS KMS CMK, +In this example, we use an AWS KMS customer master key (CMK), but you can use other key management options with the AWS Encryption SDK. For examples that demonstrate how to use other key management configurations, see the ``keyring`` and ``master_key_provider`` directories. @@ -59,7 +59,7 @@ def run(aws_kms_cmk, source_plaintext_filename): # Open the files you want to work with. with open(ciphertext_filename, "rb") as ciphertext, open(decrypted_filename, "wb") as decrypted: - # Decrypt your encrypted data. + # Decrypt your encrypted data using the same keyring you used on encrypt. # # We do not need to specify the encryption context on decrypt # because the message header includes the encryption context. diff --git a/examples/src/in_memory_streaming_defaults.py b/examples/src/in_memory_streaming_defaults.py index bf4cb4aef..1a2824b94 100644 --- a/examples/src/in_memory_streaming_defaults.py +++ b/examples/src/in_memory_streaming_defaults.py @@ -55,7 +55,7 @@ def run(aws_kms_cmk, source_plaintext): # Reset the ciphertext stream position so that we can read from the beginning. ciphertext.seek(0) - # Decrypt your encrypted data. + # Decrypt your encrypted data using the same keyring you used on encrypt. # # We do not need to specify the encryption context on decrypt # because the header message includes the encryption context. diff --git a/examples/src/onestep_defaults.py b/examples/src/onestep_defaults.py index 066898c55..a1d6e3dbd 100644 --- a/examples/src/onestep_defaults.py +++ b/examples/src/onestep_defaults.py @@ -3,7 +3,7 @@ """ This example shows how to use the one-step encrypt and decrypt APIs. -This example uses an AWS KMS CMK, +In this example, we use an AWS KMS customer master key (CMK), but you can use other key management options with the AWS Encryption SDK. For examples that demonstrate how to use other key management configurations, see the ``keyring`` and ``mater_key_provider`` directories. @@ -40,7 +40,7 @@ def run(aws_kms_cmk, source_plaintext): # Verify that the ciphertext and plaintext are different. assert ciphertext != source_plaintext - # Decrypt your encrypted data. + # Decrypt your encrypted data using the same keyring you used on encrypt. # # We do not need to specify the encryption context on decrypt # because the header message includes the encryption context. diff --git a/examples/src/onestep_unsigned.py b/examples/src/onestep_unsigned.py index 47876b711..aafb09feb 100644 --- a/examples/src/onestep_unsigned.py +++ b/examples/src/onestep_unsigned.py @@ -4,7 +4,7 @@ This example shows how to specify an algorithm suite when using the one-step encrypt and decrypt APIs. -This example uses an AWS KMS CMK, +In this example, we use an AWS KMS customer master key (CMK), but you can use other key management options with the AWS Encryption SDK. For examples that demonstrate how to use other key management configurations, see the ``keyring`` and ``mater_key_provider`` directories. @@ -57,7 +57,7 @@ def run(aws_kms_cmk, source_plaintext): # Verify that the ciphertext and plaintext are different. assert ciphertext != source_plaintext - # Decrypt your encrypted data. + # Decrypt your encrypted data using the same keyring you used on encrypt. # # We do not need to specify the encryption context on decrypt # because the header message includes the encryption context. From bf2e6c9125c82724bb08fa436dca890d3b4ca7cf Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Tue, 10 Mar 2020 13:46:37 -0700 Subject: [PATCH 53/54] chore: fix line length --- examples/src/legacy/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/src/legacy/__init__.py b/examples/src/legacy/__init__.py index 58f6279a1..5e21d1fb9 100644 --- a/examples/src/legacy/__init__.py +++ b/examples/src/legacy/__init__.py @@ -3,5 +3,7 @@ """ Legacy examples. -We keep these older examples as reference material, but we recommend that you use the new examples. They new examples reflect our current guidance for using the library. +We keep these older examples as reference material, +but we recommend that you use the new examples. +They new examples reflect our current guidance for using the library. """ From 3c19f1f3979dadbc7d4c96b7ecb0fb5ae54db39e Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Tue, 10 Mar 2020 18:48:55 -0700 Subject: [PATCH 54/54] fix: fix typo --- examples/src/legacy/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/src/legacy/__init__.py b/examples/src/legacy/__init__.py index 5e21d1fb9..e4646257c 100644 --- a/examples/src/legacy/__init__.py +++ b/examples/src/legacy/__init__.py @@ -5,5 +5,5 @@ We keep these older examples as reference material, but we recommend that you use the new examples. -They new examples reflect our current guidance for using the library. +The new examples reflect our current guidance for using the library. """