From 558563fba14240bbbf70c1d3708d730476a83ae8 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Tue, 10 Mar 2020 18:47:59 -0700 Subject: [PATCH 01/35] docs: add raw RSA keyring examples --- examples/src/keyring/__init__.py | 7 + examples/src/keyring/raw_rsa/__init__.py | 7 + .../src/keyring/raw_rsa/private_key_only.py | 85 ++++++++++++ .../raw_rsa/private_key_only_from_pem.py | 98 ++++++++++++++ .../raw_rsa/public_private_key_separate.py | 122 ++++++++++++++++++ 5 files changed, 319 insertions(+) create mode 100644 examples/src/keyring/__init__.py create mode 100644 examples/src/keyring/raw_rsa/__init__.py create mode 100644 examples/src/keyring/raw_rsa/private_key_only.py create mode 100644 examples/src/keyring/raw_rsa/private_key_only_from_pem.py create mode 100644 examples/src/keyring/raw_rsa/public_private_key_separate.py diff --git a/examples/src/keyring/__init__.py b/examples/src/keyring/__init__.py new file mode 100644 index 000000000..c718f08e8 --- /dev/null +++ b/examples/src/keyring/__init__.py @@ -0,0 +1,7 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +Keyring examples. + +These examples show how to use keyrings. +""" diff --git a/examples/src/keyring/raw_rsa/__init__.py b/examples/src/keyring/raw_rsa/__init__.py new file mode 100644 index 000000000..742761bbe --- /dev/null +++ b/examples/src/keyring/raw_rsa/__init__.py @@ -0,0 +1,7 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +Raw RSA keyring examples. + +These examples show how to use the raw RSA keyring. +""" diff --git a/examples/src/keyring/raw_rsa/private_key_only.py b/examples/src/keyring/raw_rsa/private_key_only.py new file mode 100644 index 000000000..ad281753f --- /dev/null +++ b/examples/src/keyring/raw_rsa/private_key_only.py @@ -0,0 +1,85 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +This examples shows how to configure and use a raw RSA keyring using a pre-loaded RSA private key. + +If your RSA key is in PEM or DER format, +see the ``keyring/raw_rsa/private_key_only_from_pem`` example. + +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/choose-keyring.html#use-raw-rsa-keyring + +In this example, we use the one-step encrypt and decrypt APIs. +""" +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives.asymmetric import rsa + +import aws_encryption_sdk +from aws_encryption_sdk.identifiers import WrappingAlgorithm +from aws_encryption_sdk.keyrings.raw import RawRSAKeyring + + +def run(source_plaintext): + # type: (bytes) -> None + """Demonstrate an encrypt/decrypt cycle using a raw RSA keyring. + + :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", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + + # Generate an RSA private key to use with your keyring. + # In practice, you should get this key from a secure key management system. + # + # Why did we use this public exponent? + # https://latacora.singles/2018/04/03/cryptographic-right-answers.html + private_key = rsa.generate_private_key(public_exponent=65537, key_size=4096, backend=default_backend()) + + # Create the keyring that determines how your data keys are protected. + keyring = RawRSAKeyring( + # The key namespace and key name are defined by you + # and are used by the raw RSA keyring + # to determine whether it should attempt to decrypt + # an encrypted data key. + # + # https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/choose-keyring.html#use-raw-rsa-keyring + key_namespace="some managed raw keys", + key_name=b"my RSA wrapping key", + private_wrapping_key=private_key, + # The wrapping algorithm tells the raw RSA keyring + # how to use your wrapping key to encrypt data keys. + # + # Why did we use this wrapping algorithm? + # https://latacora.singles/2018/04/03/cryptographic-right-answers.html + wrapping_algorithm=WrappingAlgorithm.RSA_OAEP_SHA256_MGF1, + ) + + # 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 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. + decrypted, decrypt_header = aws_encryption_sdk.decrypt(source=ciphertext, keyring=keyring) + + # 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 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(decrypt_header.encryption_context.items()) diff --git a/examples/src/keyring/raw_rsa/private_key_only_from_pem.py b/examples/src/keyring/raw_rsa/private_key_only_from_pem.py new file mode 100644 index 000000000..a27cbd867 --- /dev/null +++ b/examples/src/keyring/raw_rsa/private_key_only_from_pem.py @@ -0,0 +1,98 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +When you store RSA keys, you have to serialize them somehow. + +This examples shows how to configure and use a raw RSA keyring using a PEM-encoded RSA private key. + +The most commonly used encodings for RSA keys tend to be PEM and DER. +The raw RSA keyring supports loading both public and private keys from these encodings. + +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/choose-keyring.html#use-raw-rsa-keyring + +In this example, we use the one-step encrypt and decrypt APIs. +""" +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric import rsa + +import aws_encryption_sdk +from aws_encryption_sdk.identifiers import WrappingAlgorithm +from aws_encryption_sdk.keyrings.raw import RawRSAKeyring + + +def run(source_plaintext): + # type: (bytes) -> None + """Demonstrate an encrypt/decrypt cycle using a raw RSA keyring loaded from a PEM-encoded key. + + :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", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + + # Generate an RSA private key to use with your keyring. + # In practice, you should get this key from a secure key management system. + # + # Why did we use this public exponent? + # https://latacora.singles/2018/04/03/cryptographic-right-answers.html + private_key = rsa.generate_private_key(public_exponent=65537, key_size=4096, backend=default_backend()) + + # Serialize the RSA private key to PEM encoding. + # This or DER encoding likely to be what you get from your key management system in practice. + private_key_pem = private_key.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.PKCS8, + encryption_algorithm=serialization.NoEncryption(), + ) + + # Create the keyring that determines how your data keys are protected. + # + # If your key is encoded using DER, you can use RawRSAKeyring.from_der_encoding + keyring = RawRSAKeyring.from_pem_encoding( + # The key namespace and key name are defined by you + # and are used by the raw RSA keyring + # to determine whether it should attempt to decrypt + # an encrypted data key. + # + # https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/choose-keyring.html#use-raw-rsa-keyring + key_namespace="some managed raw keys", + key_name=b"my RSA wrapping key", + private_encoded_key=private_key_pem, + # The wrapping algorithm tells the raw RSA keyring + # how to use your wrapping key to encrypt data keys. + # + # Why did we use this wrapping algorithm? + # https://latacora.singles/2018/04/03/cryptographic-right-answers.html + wrapping_algorithm=WrappingAlgorithm.RSA_OAEP_SHA256_MGF1, + ) + + # 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 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. + decrypted, decrypt_header = aws_encryption_sdk.decrypt(source=ciphertext, keyring=keyring) + + # 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 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(decrypt_header.encryption_context.items()) diff --git a/examples/src/keyring/raw_rsa/public_private_key_separate.py b/examples/src/keyring/raw_rsa/public_private_key_separate.py new file mode 100644 index 000000000..536aa6917 --- /dev/null +++ b/examples/src/keyring/raw_rsa/public_private_key_separate.py @@ -0,0 +1,122 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +One of the benefits of asymmetric encryption +is that you can encrypt with just the public key. +This means that you give someone the ability to encrypt +without giving them the ability to decrypt. + +The raw RSA keyring supports encrypt-only operations +when it only has access to a public key. + +This example shows how to construct and use the raw RSA keyring +to encrypt with only public key and decrypt with the private key. + +If your RSA key is in PEM or DER format, +see the ``keyring/raw_rsa/private_key_only_from_pem`` example. + +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/choose-keyring.html#use-raw-rsa-keyring + +In this example, we use the one-step encrypt and decrypt APIs. +""" +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives.asymmetric import rsa + +import aws_encryption_sdk +from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError +from aws_encryption_sdk.identifiers import WrappingAlgorithm +from aws_encryption_sdk.keyrings.raw import RawRSAKeyring + + +def run(source_plaintext): + # type: (bytes) -> None + """Demonstrate an encrypt/decrypt cycle using separate public and private raw RSA keyrings. + + :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", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + + # Generate an RSA private key to use with your keyring. + # In practice, you should get this key from a secure key management system. + # + # Why did we use this public exponent? + # https://latacora.singles/2018/04/03/cryptographic-right-answers.html + private_key = rsa.generate_private_key(public_exponent=65537, key_size=4096, backend=default_backend()) + + # Collect the public key from the private key. + public_key = private_key.public_key() + + # The keyring determines how your data keys are protected. + # + # Create the encrypt keyring that only has access to the public key. + public_key_keyring = RawRSAKeyring( + # The key namespace and key name are defined by you + # and are used by the raw RSA keyring + # to determine whether it should attempt to decrypt + # an encrypted data key. + # + # https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/choose-keyring.html#use-raw-rsa-keyring + key_namespace="some managed raw keys", + key_name=b"my RSA wrapping key", + public_wrapping_key=public_key, + # The wrapping algorithm tells the raw RSA keyring + # how to use your wrapping key to encrypt data keys. + # + # Why did we use this wrapping algorithm? + # https://latacora.singles/2018/04/03/cryptographic-right-answers.html + wrapping_algorithm=WrappingAlgorithm.RSA_OAEP_SHA256_MGF1, + ) + + # Create the decrypt keyring that has access to the private key. + private_key_keyring = RawRSAKeyring( + # The key namespace and key name MUST match the encrypt keyring. + key_namespace="some managed raw keys", + key_name=b"my RSA wrapping key", + private_wrapping_key=private_key, + # The wrapping algorithm MUST match the encrypt keyring. + wrapping_algorithm=WrappingAlgorithm.RSA_OAEP_SHA256_MGF1, + ) + + # Encrypt your plaintext data using the encrypt keyring. + ciphertext, _encrypt_header = aws_encryption_sdk.encrypt( + source=source_plaintext, encryption_context=encryption_context, keyring=public_key_keyring + ) + + # Verify that the ciphertext and plaintext are different. + assert ciphertext != source_plaintext + + # Decrypt your encrypted data using the decrypt keyring. + # + # 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=private_key_keyring) + + # Try to decrypt your encrypted data using the *encrypt* keyring. + # This demonstrates that you cannot decrypt using the public key. + try: + aws_encryption_sdk.decrypt(source=ciphertext, keyring=public_key_keyring) + except AWSEncryptionSDKClientError: + # The public key cannot decrypt. + # Reaching this point means everything is working as expected. + pass + else: + # Show that the public keyring could not decrypt. + raise AssertionError("This will never happen!") + + # 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 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(decrypt_header.encryption_context.items()) From 7b6b920d48c083c7e19b7dcc0f9ad9e8c2f7b0ad Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Tue, 10 Mar 2020 18:48:33 -0700 Subject: [PATCH 02/35] fix: fix type signatures in raw RSA keyring --- src/aws_encryption_sdk/keyrings/raw.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/aws_encryption_sdk/keyrings/raw.py b/src/aws_encryption_sdk/keyrings/raw.py index 1f4eebe14..b183f33c9 100644 --- a/src/aws_encryption_sdk/keyrings/raw.py +++ b/src/aws_encryption_sdk/keyrings/raw.py @@ -31,11 +31,8 @@ _LOGGER = logging.getLogger(__name__) -def _generate_data_key( - encryption_materials, # type: EncryptionMaterials - key_provider, # type: MasterKeyInfo -): - # type: (...) -> bytes +def _generate_data_key(encryption_materials, key_provider): + # type: (EncryptionMaterials, MasterKeyInfo) -> bytes """Generates plaintext data key for the keyring. :param EncryptionMaterials encryption_materials: Encryption materials for the keyring to modify. @@ -299,6 +296,7 @@ def from_der_encoding( private_encoded_key=None, # type: bytes password=None, # type: bytes ): + # type: (...) -> RawRSAKeyring """Generate a raw RSA keyring using DER Encoded public and private keys :param str key_namespace: String defining the keyring ID From 05266a893608b2afbb0e268c5e7bc5a4ab0fd52b Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Tue, 10 Mar 2020 18:56:05 -0700 Subject: [PATCH 03/35] docs: add raw AES keyring example --- examples/src/keyring/raw_aes/__init__.py | 7 +++ examples/src/keyring/raw_aes/raw_aes.py | 76 ++++++++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 examples/src/keyring/raw_aes/__init__.py create mode 100644 examples/src/keyring/raw_aes/raw_aes.py diff --git a/examples/src/keyring/raw_aes/__init__.py b/examples/src/keyring/raw_aes/__init__.py new file mode 100644 index 000000000..2159bf30d --- /dev/null +++ b/examples/src/keyring/raw_aes/__init__.py @@ -0,0 +1,7 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +Raw AES keyring examples. + +These examples show how to use the raw AES keyring. +""" diff --git a/examples/src/keyring/raw_aes/raw_aes.py b/examples/src/keyring/raw_aes/raw_aes.py new file mode 100644 index 000000000..bf56f24c6 --- /dev/null +++ b/examples/src/keyring/raw_aes/raw_aes.py @@ -0,0 +1,76 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +This examples shows how to configure and use a raw AES keyring. + +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/choose-keyring.html#use-raw-aes-keyring + +In this example, we use the one-step encrypt and decrypt APIs. +""" +import os + +import aws_encryption_sdk +from aws_encryption_sdk.identifiers import WrappingAlgorithm +from aws_encryption_sdk.keyrings.raw import RawAESKeyring + + +def run(source_plaintext): + # type: (bytes) -> None + """Demonstrate an encrypt/decrypt cycle using a raw RSA keyring. + + :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", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + + # Choose the wrapping algorithm for the keyring to use. + wrapping_algorithm = WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING + + # Generate an AES key to use with your keyring. + # The key size depends on the wrapping algorithm. + key = os.urandom(wrapping_algorithm.algorithm.kdf_input_len) + + # Create the keyring that determines how your data keys are protected. + keyring = RawAESKeyring( + # The key namespace and key name are defined by you + # and are used by the raw RSA keyring + # to determine whether it should attempt to decrypt + # an encrypted data key. + # + # https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/choose-keyring.html#use-raw-aes-keyring + key_namespace="some managed raw keys", + key_name=b"my AES wrapping key", + wrapping_key=key, + wrapping_algorithm=wrapping_algorithm, + ) + + # 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 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. + decrypted, decrypt_header = aws_encryption_sdk.decrypt(source=ciphertext, keyring=keyring) + + # 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 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(decrypt_header.encryption_context.items()) From f368f09b7f2629ac755cee1a1bcdd65e8b72fc3c Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Tue, 10 Mar 2020 19:07:54 -0700 Subject: [PATCH 04/35] chore: move single-CMK example to new location --- .../aws_kms/single_cmk.py} | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) rename examples/src/{legacy/one_kms_cmk.py => keyring/aws_kms/single_cmk.py} (70%) diff --git a/examples/src/legacy/one_kms_cmk.py b/examples/src/keyring/aws_kms/single_cmk.py similarity index 70% rename from examples/src/legacy/one_kms_cmk.py rename to examples/src/keyring/aws_kms/single_cmk.py index 7fb5f1431..8a0bd37c9 100644 --- a/examples/src/legacy/one_kms_cmk.py +++ b/examples/src/keyring/aws_kms/single_cmk.py @@ -1,6 +1,21 @@ # 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.""" +""" +This example shows how to configure and use a KMS keyring with a single KMS CMK. + +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/choose-keyring.html#use-kms-keyring + +For an example of how to use the KMS keyring with CMKs in multiple regions, +see the ``keyring/aws_kms/multiple_regions`` example. + +For examples of how to use the KMS keyring with custom client configurations, +see the ``keyring/aws_kms/custom_client_supplier`` +and ``keyring/aws_kms/custom_kms_client_config`` examples. + +For examples of how to use the KMS keyring in discovery mode on decrypt, +see the ``keyring/aws_kms/discovery_decrypt`` +and ``keyring/aws_kms/discovery_decrypt_with_preferred_region`` examples. +""" import aws_encryption_sdk From 47094092291d68f31f403566a4ea7c8aa961d877 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Tue, 10 Mar 2020 19:08:35 -0700 Subject: [PATCH 05/35] docs: update single-CMK example to match new template --- examples/src/keyring/aws_kms/single_cmk.py | 60 +++++++++++++--------- 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/examples/src/keyring/aws_kms/single_cmk.py b/examples/src/keyring/aws_kms/single_cmk.py index 8a0bd37c9..197851a0a 100644 --- a/examples/src/keyring/aws_kms/single_cmk.py +++ b/examples/src/keyring/aws_kms/single_cmk.py @@ -17,37 +17,49 @@ and ``keyring/aws_kms/discovery_decrypt_with_preferred_region`` examples. """ import aws_encryption_sdk +from aws_encryption_sdk.keyrings.aws_kms import KmsKeyring -def run(aws_kms_cmk, source_plaintext, botocore_session=None): - """Encrypts and then decrypts a string under one KMS customer master key (CMK). +def run(aws_kms_cmk, source_plaintext): + # type: (str, bytes) -> None + """Demonstrate an encrypt/decrypt cycle using a KMS keyring. - :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 + :param str aws_kms_cmk: The ARN of an AWS KMS CMK that protects data keys + :param bytes source_plaintext: Plaintext to encrypt """ - kwargs = dict(key_ids=[aws_kms_cmk]) + # 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", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } - if botocore_session is not None: - kwargs["botocore_session"] = botocore_session + # Create the keyring that determines how your data keys are protected. + keyring = KmsKeyring(generator_key_id=aws_kms_cmk) - # 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 + # Encrypt your plaintext data. + ciphertext, _encrypt_header = aws_encryption_sdk.encrypt( + source=source_plaintext, encryption_context=encryption_context, keyring=keyring ) - # 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) + # Verify that the ciphertext and plaintext are different. + assert ciphertext != source_plaintext - # Check if the original message and the decrypted message are the same - assert source_plaintext == plaintext + # 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. + decrypted, decrypt_header = aws_encryption_sdk.decrypt(source=ciphertext, keyring=keyring) - # 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() - ) + # 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 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(decrypt_header.encryption_context.items()) From 5fb2d3e5f2c3ac9de5fe85a19c3ab851dc28f5cf Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Tue, 10 Mar 2020 19:09:18 -0700 Subject: [PATCH 06/35] fix: fix docstring in raw AES keyring example --- examples/src/keyring/raw_aes/raw_aes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/src/keyring/raw_aes/raw_aes.py b/examples/src/keyring/raw_aes/raw_aes.py index bf56f24c6..503662c35 100644 --- a/examples/src/keyring/raw_aes/raw_aes.py +++ b/examples/src/keyring/raw_aes/raw_aes.py @@ -16,7 +16,7 @@ def run(source_plaintext): # type: (bytes) -> None - """Demonstrate an encrypt/decrypt cycle using a raw RSA keyring. + """Demonstrate an encrypt/decrypt cycle using a raw AES keyring. :param bytes source_plaintext: Plaintext to encrypt """ From f6af7ee4f6f05221e71372ef3a19393b7480d577 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Tue, 10 Mar 2020 19:11:13 -0700 Subject: [PATCH 07/35] chore: move multi-region KMS example to new location --- .../aws_kms/multiple_regions.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/src/{legacy/multiple_kms_cmk_regions.py => keyring/aws_kms/multiple_regions.py} (100%) diff --git a/examples/src/legacy/multiple_kms_cmk_regions.py b/examples/src/keyring/aws_kms/multiple_regions.py similarity index 100% rename from examples/src/legacy/multiple_kms_cmk_regions.py rename to examples/src/keyring/aws_kms/multiple_regions.py From 52f831289d7f04a88cc581bdb96d2112ca866104 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Wed, 11 Mar 2020 11:34:54 -0700 Subject: [PATCH 08/35] chore: update multi-region AWS keyring example to new format --- .../src/keyring/aws_kms/multiple_regions.py | 121 +++++++++++------- examples/src/keyring/aws_kms/single_cmk.py | 2 +- 2 files changed, 75 insertions(+), 48 deletions(-) diff --git a/examples/src/keyring/aws_kms/multiple_regions.py b/examples/src/keyring/aws_kms/multiple_regions.py index deefd73e9..5b48deaf1 100644 --- a/examples/src/keyring/aws_kms/multiple_regions.py +++ b/examples/src/keyring/aws_kms/multiple_regions.py @@ -1,65 +1,92 @@ # 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. +This example shows how to configure and use a KMS keyring with a single KMS CMK. + +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/choose-keyring.html#use-kms-keyring + +For an example of how to use the KMS keyring with a single CMK, +see the ``keyring/aws_kms/single_cmk`` example. + +For examples of how to use the KMS keyring with custom client configurations, +see the ``keyring/aws_kms/custom_client_supplier`` +and ``keyring/aws_kms/custom_kms_client_config`` examples. + +For examples of how to use the KMS keyring in discovery mode on decrypt, +see the ``keyring/aws_kms/discovery_decrypt`` +and ``keyring/aws_kms/discovery_decrypt_with_preferred_region`` examples. """ import aws_encryption_sdk -from aws_encryption_sdk.key_providers.kms import KMSMasterKey, KMSMasterKeyProvider - +from aws_encryption_sdk.keyrings.aws_kms import KmsKeyring -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. +try: # Python 3.5.0 and 3.5.1 have incompatible typing modules + from typing import Sequence # noqa pylint: disable=unused-import +except ImportError: # pragma: no cover + # We only actually need these imports when running the mypy checks + pass - :param str aws_kms_generator_cmk: Amazon Resource Name (ARN) of the primary KMS CMK - :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 - """ - encrypt_cmk = aws_kms_additional_cmks[0] - # Check that these keys are in different regions - assert not aws_kms_generator_cmk.split(":")[3] == encrypt_cmk.split(":")[3] +def run(aws_kms_generator_cmk, aws_kms_additional_cmks, source_plaintext): + # type: (str, Sequence[str], bytes) -> None + """Demonstrate an encrypt/decrypt cycle using a KMS keyring with CMKs in multiple regions. - kwargs = dict(key_ids=[aws_kms_generator_cmk, encrypt_cmk]) + :param str aws_kms_generator_cmk: The ARN of the primary AWS KMS CMK + :param List[str] aws_kms_additional_cmks: Additional ARNs of secondary KMS CMKs + :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", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } - if botocore_session is not None: - kwargs["botocore_session"] = botocore_session + # Create the keyring that will encrypt your data keys under all requested CMKs. + many_cmks_keyring = KmsKeyring(generator_key_id=aws_kms_generator_cmk, child_key_ids=aws_kms_additional_cmks) - # Create master key provider using the ARNs of the keys and the session (botocore_session) - kms_key_provider = KMSMasterKeyProvider(**kwargs) + # Create keyrings that each only use one of the CMKs. + # We will use these later to demonstrate that any of the CMKs can be used to decrypt the message. + # + # We provide these in "child_key_ids" rather than "generator_key_id" + # so that these keyrings cannot be used to generate a new data key. + # We will only be using them on decrypt. + single_cmk_keyring_that_generated = KmsKeyring(child_key_ids=[aws_kms_generator_cmk]) + single_cmk_keyring_that_encrypted = KmsKeyring(child_key_ids=[aws_kms_additional_cmks[0]]) - # Encrypt the plaintext using the AWS Encryption SDK. It returns the encrypted message and the header - ciphertext, encrypted_message_header = aws_encryption_sdk.encrypt( - key_provider=kms_key_provider, source=source_plaintext + # Encrypt your plaintext data using the keyring that uses all requests CMKs. + ciphertext, encrypt_header = aws_encryption_sdk.encrypt( + source=source_plaintext, encryption_context=encryption_context, keyring=many_cmks_keyring ) - # Check that both key ARNs are in the message headers - assert len(encrypted_message_header.encrypted_data_keys) == 2 + # Verify that the header contains the expected number of encrypted data keys (EDKs). + # It should contain one EDK for each CMK. + assert len(encrypt_header.encrypted_data_keys) == len(aws_kms_additional_cmks) + 1 - # 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=aws_kms_generator_cmk), source=ciphertext - ) - plaintext_2, decrypted_message_header_2 = aws_encryption_sdk.decrypt( - key_provider=KMSMasterKey(key_id=encrypt_cmk), source=ciphertext - ) + # Verify that the ciphertext and plaintext are different. + assert ciphertext != source_plaintext - # Check that the original message and the decrypted message are the same - if not isinstance(source_plaintext, bytes): - plaintext_1 = plaintext_1.decode("utf-8") - plaintext_2 = 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_header_1.encryption_context.items() + # Decrypt your encrypted data separately using the single-CMK keyrings. + # + # We do not need to specify the encryption context on decrypt + # because the header message includes the encryption context. + decrypted_1, decrypt_header_1 = aws_encryption_sdk.decrypt( + source=ciphertext, keyring=single_cmk_keyring_that_generated ) - assert all( - pair in encrypted_message_header.encryption_context.items() - for pair in decrypted_message_header_2.encryption_context.items() + decrypted_2, decrypt_header_2 = aws_encryption_sdk.decrypt( + source=ciphertext, keyring=single_cmk_keyring_that_encrypted ) + + # Verify that the decrypted plaintext is identical to the original plaintext. + assert decrypted_1 == source_plaintext + assert decrypted_2 == source_plaintext + + # 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(decrypt_header_1.encryption_context.items()) + assert set(encryption_context.items()) <= set(decrypt_header_2.encryption_context.items()) diff --git a/examples/src/keyring/aws_kms/single_cmk.py b/examples/src/keyring/aws_kms/single_cmk.py index 197851a0a..c34aceab6 100644 --- a/examples/src/keyring/aws_kms/single_cmk.py +++ b/examples/src/keyring/aws_kms/single_cmk.py @@ -22,7 +22,7 @@ def run(aws_kms_cmk, source_plaintext): # type: (str, bytes) -> None - """Demonstrate an encrypt/decrypt cycle using a KMS keyring. + """Demonstrate an encrypt/decrypt cycle using a KMS keyring with a single CMK. :param str aws_kms_cmk: The ARN of an AWS KMS CMK that protects data keys :param bytes source_plaintext: Plaintext to encrypt From 0047ddf2137a1ba8cb75f4a2d21562e2a7884328 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Wed, 11 Mar 2020 12:08:43 -0700 Subject: [PATCH 09/35] docs: add multi-keyring example --- examples/src/keyring/multi/__init__.py | 7 + .../src/keyring/multi/aws_kms_with_escrow.py | 122 ++++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 examples/src/keyring/multi/__init__.py create mode 100644 examples/src/keyring/multi/aws_kms_with_escrow.py diff --git a/examples/src/keyring/multi/__init__.py b/examples/src/keyring/multi/__init__.py new file mode 100644 index 000000000..e5703355a --- /dev/null +++ b/examples/src/keyring/multi/__init__.py @@ -0,0 +1,7 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +Multi-keyring examples. + +These examples show how to use the multi-keyring. +""" diff --git a/examples/src/keyring/multi/aws_kms_with_escrow.py b/examples/src/keyring/multi/aws_kms_with_escrow.py new file mode 100644 index 000000000..2464ceac2 --- /dev/null +++ b/examples/src/keyring/multi/aws_kms_with_escrow.py @@ -0,0 +1,122 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +One use-case that we have seen customers need is +the ability to enjoy the benefits of AWS KMS during normal operation +but retain the ability to decrypt encrypted messages offline. +This examples shows how you can use the multi-keyring to achieve this +by combining a KMS keyring with a raw RSA keyring. + +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/choose-keyring.html#use-multi-keyring + +For more examples of how to use the KMS keyring, see the ``keyring/aws_kms`` examples. + +For more examples of how to use the raw RSA keyring, see the ``keyring/raw_rsa`` examples. + +In this example, we use the one-step encrypt and decrypt APIs. +""" +# TODO: Expand this description +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives.asymmetric import rsa + +import aws_encryption_sdk +from aws_encryption_sdk.identifiers import WrappingAlgorithm +from aws_encryption_sdk.keyrings.aws_kms import KmsKeyring +from aws_encryption_sdk.keyrings.multi import MultiKeyring +from aws_encryption_sdk.keyrings.raw import RawRSAKeyring + + +def run(aws_kms_cmk, source_plaintext): + # type: (str, bytes) -> None + """Demonstrate an encrypt/decrypt cycle using a KMS keyring with a single CMK. + + :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. + # https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#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", + } + + # Generate an RSA private key to use with your keyring. + # In practice, you should get this key from a secure key management system. + # + # Why did we use this public exponent? + # https://latacora.singles/2018/04/03/cryptographic-right-answers.html + private_key = rsa.generate_private_key(public_exponent=65537, key_size=4096, backend=default_backend()) + + # Collect the public key from the private key. + public_key = private_key.public_key() + + # Create the encrypt keyring that only has access to the public key. + escrow_encrypt_keyring = RawRSAKeyring( + # The key namespace and key name are defined by you + # and are used by the raw RSA keyring + # to determine whether it should attempt to decrypt + # an encrypted data key. + # + # https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/choose-keyring.html#use-raw-rsa-keyring + key_namespace="some managed raw keys", + key_name=b"my RSA wrapping key", + public_wrapping_key=public_key, + # The wrapping algorithm tells the raw RSA keyring + # how to use your wrapping key to encrypt data keys. + # + # Why did we use this wrapping algorithm? + # https://latacora.singles/2018/04/03/cryptographic-right-answers.html + wrapping_algorithm=WrappingAlgorithm.RSA_OAEP_SHA256_MGF1, + ) + + # Create the decrypt keyring that has access to the private key. + escrow_decrypt_keyring = RawRSAKeyring( + # The key namespace and key name MUST match the encrypt keyring. + key_namespace="some managed raw keys", + key_name=b"my RSA wrapping key", + private_wrapping_key=private_key, + # The wrapping algorithm MUST match the encrypt keyring. + wrapping_algorithm=WrappingAlgorithm.RSA_OAEP_SHA256_MGF1, + ) + + # Create the KMS keyring that you will use for decryption during normal operations. + kms_keyring = KmsKeyring(generator_key_id=aws_kms_cmk) + + # Combine the KMS keyring and the escrow encrypt keyring using the multi-keyring. + encrypt_keyring = MultiKeyring(generator=kms_keyring, children=[escrow_encrypt_keyring]) + + # Encrypt your plaintext data using the multi-keyring. + ciphertext, encrypt_header = aws_encryption_sdk.encrypt( + source=source_plaintext, encryption_context=encryption_context, keyring=encrypt_keyring + ) + + # Verify that the header contains the expected number of encrypted data keys (EDKs). + # It should contain one EDK for KMS and one for the escrow key. + assert len(encrypt_header.encrypted_data_keys) == 2 + + # Verify that the ciphertext and plaintext are different. + assert ciphertext != source_plaintext + + # Decrypt your encrypted data separately using the KMS keyring and the escrow decrypt keyring. + # + # We do not need to specify the encryption context on decrypt + # because the header message includes the encryption context. + decrypted_kms, decrypt_header_kms = aws_encryption_sdk.decrypt(source=ciphertext, keyring=kms_keyring) + decrypted_escrow, decrypt_header_escrow = aws_encryption_sdk.decrypt( + source=ciphertext, keyring=escrow_decrypt_keyring + ) + + # Verify that the decrypted plaintext is identical to the original plaintext. + assert decrypted_kms == source_plaintext + assert decrypted_escrow == source_plaintext + + # 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(decrypt_header_kms.encryption_context.items()) + assert set(encryption_context.items()) <= set(decrypt_header_escrow.encryption_context.items()) From 5925b77698f4f4e04d7926eed7524382cfe13537 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Wed, 11 Mar 2020 12:27:04 -0700 Subject: [PATCH 10/35] docs: add custom KMS client config keyring example --- examples/src/keyring/aws_kms/__init__.py | 7 ++ .../aws_kms/custom_kms_client_config.py | 87 +++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 examples/src/keyring/aws_kms/__init__.py create mode 100644 examples/src/keyring/aws_kms/custom_kms_client_config.py diff --git a/examples/src/keyring/aws_kms/__init__.py b/examples/src/keyring/aws_kms/__init__.py new file mode 100644 index 000000000..e9d9452c8 --- /dev/null +++ b/examples/src/keyring/aws_kms/__init__.py @@ -0,0 +1,7 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +AWS KMS keyring examples. + +These examples show how to use the KMS keyring. +""" diff --git a/examples/src/keyring/aws_kms/custom_kms_client_config.py b/examples/src/keyring/aws_kms/custom_kms_client_config.py new file mode 100644 index 000000000..b8b6f3a43 --- /dev/null +++ b/examples/src/keyring/aws_kms/custom_kms_client_config.py @@ -0,0 +1,87 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +By default, the KMS keyring will use the default configurations +for all KMS clients and will use the default discoverable credentials. +If you need to change these configurations, +you can do that using the client supplier. + +This example shows how to use custom-configured clients with the KMS keyring. + +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/choose-keyring.html#use-kms-keyring + +For an example of how to use the KMS keyring with CMKs in multiple regions, +see the ``keyring/aws_kms/multiple_regions`` example. + +For another example of how to use the KMS keyring with custom client configuration, +see the ``keyring/aws_kms/custom_client_supplier`` example. + +For examples of how to use the KMS keyring in discovery mode on decrypt, +see the ``keyring/aws_kms/discovery_decrypt`` +and ``keyring/aws_kms/discovery_decrypt_with_preferred_region`` examples. +""" +from botocore.config import Config +from botocore.session import Session + +import aws_encryption_sdk +from aws_encryption_sdk.identifiers import USER_AGENT_SUFFIX +from aws_encryption_sdk.keyrings.aws_kms import KmsKeyring +from aws_encryption_sdk.keyrings.aws_kms.client_suppliers import DefaultClientSupplier + + +def run(aws_kms_cmk, source_plaintext): + # type: (str, bytes) -> None + """Demonstrate an encrypt/decrypt cycle using a KMS keyring with custom KMS client configuration. + + :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. + # https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#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", + } + + # Prepare your custom configuration values. + # + # Set your custom connection timeout value. + # https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html + custom_client_config = Config(connect_timeout=10.0, user_agent_extra=USER_AGENT_SUFFIX) + # For this example we will just use the default botocore session configuration + # but if you need to you can set custom credentials in the botocore session. + custom_session = Session() + + # Use your custom configuration values to configure your client supplier. + client_supplier = DefaultClientSupplier(botocore_session=custom_session, client_config=custom_client_config) + + # Create the keyring that determines how your data keys are protected, + # providing the client supplier that you created. + keyring = KmsKeyring(generator_key_id=aws_kms_cmk, client_supplier=client_supplier) + + # 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 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. + decrypted, decrypt_header = aws_encryption_sdk.decrypt(source=ciphertext, keyring=keyring) + + # 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 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(decrypt_header.encryption_context.items()) From a6519d2a888f2c6055c06f24dd96795c68659fba Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Thu, 12 Mar 2020 11:40:58 -0700 Subject: [PATCH 11/35] docs: add multi-partition client supplier example --- .../keyring/aws_kms/custom_client_supplier.py | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 examples/src/keyring/aws_kms/custom_client_supplier.py diff --git a/examples/src/keyring/aws_kms/custom_client_supplier.py b/examples/src/keyring/aws_kms/custom_client_supplier.py new file mode 100644 index 000000000..c41197cae --- /dev/null +++ b/examples/src/keyring/aws_kms/custom_client_supplier.py @@ -0,0 +1,116 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +By default, the KMS keyring uses a client supplier that +supplies a client with the same configuration for every region. +If you need different behavior, you can write your own client supplier. + +One use-case where you might need this is if you need to interact with AWS KMS across partitions. +AWS regions are organized into different "partitions" (ex: ``aws`` and ``aws-cn``) +and credentials only work within a single partition. +If you need to work with AWS KMS CMKs in multiple partitions +then you need to always get credentials that are valid +for the partition that you are talking to. + +This example shows how to create a client supplier +that will supply KMS clients with valid credentials for the target region +even when working with regions across different partitions. + +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/choose-keyring.html#use-kms-keyring + +For an example of how to use the KMS keyring with CMKs in multiple regions, +see the ``keyring/aws_kms/multiple_regions`` example. + +For another example of how to use the KMS keyring with custom client configuration, +see the ``keyring/aws_kms/custom_kms_client_config`` example. + +For examples of how to use the KMS keyring in discovery mode on decrypt, +see the ``keyring/aws_kms/discovery_decrypt`` +and ``keyring/aws_kms/discovery_decrypt_with_preferred_region`` examples. +""" +# TODO: Add explanation of partitions +from botocore.client import BaseClient +from botocore.session import Session + +import aws_encryption_sdk +from aws_encryption_sdk.keyrings.aws_kms import KmsKeyring +from aws_encryption_sdk.keyrings.aws_kms.client_suppliers import ClientSupplier, DefaultClientSupplier + +try: # Python 3.5.0 and 3.5.1 have incompatible typing modules + from typing import Union # noqa pylint: disable=unused-import +except ImportError: # pragma: no cover + # We only actually need these imports when running the mypy checks + pass + + +class MultiPartitionClientSupplier(ClientSupplier): + """Client supplier that supplies clients across AWS partitions and identity silos.""" + + def __init__(self): + """Set up default client suppliers for identity silos.""" + self._china_supplier = DefaultClientSupplier(botocore_session=Session(profile="china")) + self._middle_east_supplier = DefaultClientSupplier(botocore_session=Session(profile="middle-east")) + self._hong_kong_supplier = DefaultClientSupplier(botocore_session=Session(profile="hong-kong")) + self._default_supplier = DefaultClientSupplier() + + def __call__(self, region_name): + # type: (Union[None, str]) -> BaseClient + """Return a client for the requested region. + + :rtype: BaseClient + """ + if region_name.startswith("cn-"): + return self._china_supplier(region_name) + + if region_name.startswith("me-"): + return self._middle_east_supplier(region_name) + + if region_name == "ap-east-1": + return self._hong_kong_supplier(region_name) + + return self._default_supplier(region_name) + + +def run(aws_kms_cmk, source_plaintext): + # type: (str, bytes) -> None + """Demonstrate an encrypt/decrypt cycle using a KMS keyring with a custom client supplier. + + :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. + # https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#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 data keys are protected. + keyring = KmsKeyring(generator_key_id=aws_kms_cmk, client_supplier=MultiPartitionClientSupplier()) + + # 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 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. + decrypted, decrypt_header = aws_encryption_sdk.decrypt(source=ciphertext, keyring=keyring) + + # 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 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(decrypt_header.encryption_context.items()) From 2fb99d09d8c89effca13199cf323729265e53417 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Thu, 12 Mar 2020 11:47:08 -0700 Subject: [PATCH 12/35] docs: clarify custom client supplier example intro --- examples/src/keyring/aws_kms/custom_client_supplier.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/examples/src/keyring/aws_kms/custom_client_supplier.py b/examples/src/keyring/aws_kms/custom_client_supplier.py index c41197cae..20bce0057 100644 --- a/examples/src/keyring/aws_kms/custom_client_supplier.py +++ b/examples/src/keyring/aws_kms/custom_client_supplier.py @@ -8,6 +8,10 @@ One use-case where you might need this is if you need to interact with AWS KMS across partitions. AWS regions are organized into different "partitions" (ex: ``aws`` and ``aws-cn``) and credentials only work within a single partition. +There are also some regions, +such as Asia Pacific (Hong Kong) and Middle East (Bahrain) +that are in the same partition (``aws``) as other regions +but can still require different credentials. If you need to work with AWS KMS CMKs in multiple partitions then you need to always get credentials that are valid for the partition that you are talking to. From 154ce5d9ac093ddeab5e1b12e7f2ff6ba1e6cf5f Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Thu, 12 Mar 2020 12:25:13 -0700 Subject: [PATCH 13/35] docs: add reference to in-region-discovery example --- examples/src/keyring/aws_kms/custom_client_supplier.py | 3 ++- examples/src/keyring/aws_kms/custom_kms_client_config.py | 3 ++- examples/src/keyring/aws_kms/multiple_regions.py | 3 ++- examples/src/keyring/aws_kms/single_cmk.py | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/examples/src/keyring/aws_kms/custom_client_supplier.py b/examples/src/keyring/aws_kms/custom_client_supplier.py index 20bce0057..aa8b92553 100644 --- a/examples/src/keyring/aws_kms/custom_client_supplier.py +++ b/examples/src/keyring/aws_kms/custom_client_supplier.py @@ -29,7 +29,8 @@ see the ``keyring/aws_kms/custom_kms_client_config`` example. For examples of how to use the KMS keyring in discovery mode on decrypt, -see the ``keyring/aws_kms/discovery_decrypt`` +see the ``keyring/aws_kms/discovery_decrypt``, +``keyring/aws_kms/discovery_decrypt_in_region_only``, and ``keyring/aws_kms/discovery_decrypt_with_preferred_region`` examples. """ # TODO: Add explanation of partitions diff --git a/examples/src/keyring/aws_kms/custom_kms_client_config.py b/examples/src/keyring/aws_kms/custom_kms_client_config.py index b8b6f3a43..5aedaeaee 100644 --- a/examples/src/keyring/aws_kms/custom_kms_client_config.py +++ b/examples/src/keyring/aws_kms/custom_kms_client_config.py @@ -17,7 +17,8 @@ see the ``keyring/aws_kms/custom_client_supplier`` example. For examples of how to use the KMS keyring in discovery mode on decrypt, -see the ``keyring/aws_kms/discovery_decrypt`` +see the ``keyring/aws_kms/discovery_decrypt``, +``keyring/aws_kms/discovery_decrypt_in_region_only``, and ``keyring/aws_kms/discovery_decrypt_with_preferred_region`` examples. """ from botocore.config import Config diff --git a/examples/src/keyring/aws_kms/multiple_regions.py b/examples/src/keyring/aws_kms/multiple_regions.py index 5b48deaf1..894251002 100644 --- a/examples/src/keyring/aws_kms/multiple_regions.py +++ b/examples/src/keyring/aws_kms/multiple_regions.py @@ -13,7 +13,8 @@ and ``keyring/aws_kms/custom_kms_client_config`` examples. For examples of how to use the KMS keyring in discovery mode on decrypt, -see the ``keyring/aws_kms/discovery_decrypt`` +see the ``keyring/aws_kms/discovery_decrypt``, +``keyring/aws_kms/discovery_decrypt_in_region_only``, and ``keyring/aws_kms/discovery_decrypt_with_preferred_region`` examples. """ import aws_encryption_sdk diff --git a/examples/src/keyring/aws_kms/single_cmk.py b/examples/src/keyring/aws_kms/single_cmk.py index c34aceab6..2bf0a1e0f 100644 --- a/examples/src/keyring/aws_kms/single_cmk.py +++ b/examples/src/keyring/aws_kms/single_cmk.py @@ -13,7 +13,8 @@ and ``keyring/aws_kms/custom_kms_client_config`` examples. For examples of how to use the KMS keyring in discovery mode on decrypt, -see the ``keyring/aws_kms/discovery_decrypt`` +see the ``keyring/aws_kms/discovery_decrypt``, +``keyring/aws_kms/discovery_decrypt_in_region_only``, and ``keyring/aws_kms/discovery_decrypt_with_preferred_region`` examples. """ import aws_encryption_sdk From 3fc892a2ab5b061e3f5f96bfe79f6cbb6da4d4af Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Thu, 12 Mar 2020 12:30:53 -0700 Subject: [PATCH 14/35] docs: add basic KMS discovery keyring example --- .../src/keyring/aws_kms/discovery_decrypt.py | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 examples/src/keyring/aws_kms/discovery_decrypt.py diff --git a/examples/src/keyring/aws_kms/discovery_decrypt.py b/examples/src/keyring/aws_kms/discovery_decrypt.py new file mode 100644 index 000000000..c675eb847 --- /dev/null +++ b/examples/src/keyring/aws_kms/discovery_decrypt.py @@ -0,0 +1,78 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +When you give the KMS keyring specific key IDs it will use those CMKs and nothing else. +This is true both on encrypt and on decrypt. +However, sometimes you need more flexibility on decrypt, +especially if you might not know beforehand which CMK was used to encrypt a message. +To address this need, the KMS keyring also supports "discovery" mode. +In discovery mode, the KMS keyring will do nothing on encrypt +but will attempt to decrypt *any* data keys that were encrypted under a KMS CMK. + +This example shows how to configure and use a KMS keyring in discovery mode. + +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/choose-keyring.html#use-kms-keyring + +For an example of how to use the KMS keyring with CMKs in multiple regions, +see the ``keyring/aws_kms/multiple_regions`` example. + +For examples of how to use the KMS keyring with custom client configurations, +see the ``keyring/aws_kms/custom_client_supplier`` +and ``keyring/aws_kms/custom_kms_client_config`` examples. + +For examples of how to use the KMS keyring in discovery mode on decrypt, +see the ``keyring/aws_kms/discovery_decrypt_in_region_only`` +and ``keyring/aws_kms/discovery_decrypt_with_preferred_region`` examples. +""" +import aws_encryption_sdk +from aws_encryption_sdk.keyrings.aws_kms import KmsKeyring + + +def run(aws_kms_cmk, source_plaintext): + # type: (str, bytes) -> None + """Demonstrate an encrypt/decrypt cycle using a KMS keyring with a single CMK. + + :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. + # https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#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 data keys are protected. + encrypt_keyring = KmsKeyring(generator_key_id=aws_kms_cmk) + + # Create the KMS discovery keyring that we will use on decrypt. + # + # Because we do not specify any key IDs, this keyring is created in discovery mode. + decrypt_keyring = KmsKeyring() + + # Encrypt your plaintext data. + ciphertext, _encrypt_header = aws_encryption_sdk.encrypt( + source=source_plaintext, encryption_context=encryption_context, keyring=encrypt_keyring + ) + + # Verify that the ciphertext and plaintext are different. + assert ciphertext != source_plaintext + + # Decrypt your encrypted data using the KMS discovery keyring. + # + # 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=decrypt_keyring) + + # 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 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(decrypt_header.encryption_context.items()) From 47351a794b6ab2c3906e32e85243ac9f5a83259f Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Thu, 12 Mar 2020 14:09:02 -0700 Subject: [PATCH 15/35] docs: add examples showing region-scoped discovery keyrings --- .../discovery_decrypt_in_region_only.py | 86 ++++++++++++++ ...iscovery_decrypt_with_preferred_regions.py | 109 ++++++++++++++++++ 2 files changed, 195 insertions(+) create mode 100644 examples/src/keyring/aws_kms/discovery_decrypt_in_region_only.py create mode 100644 examples/src/keyring/aws_kms/discovery_decrypt_with_preferred_regions.py diff --git a/examples/src/keyring/aws_kms/discovery_decrypt_in_region_only.py b/examples/src/keyring/aws_kms/discovery_decrypt_in_region_only.py new file mode 100644 index 000000000..d06575609 --- /dev/null +++ b/examples/src/keyring/aws_kms/discovery_decrypt_in_region_only.py @@ -0,0 +1,86 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +When you give the KMS keyring specific key IDs it will use those CMKs and nothing else. +This is true both on encrypt and on decrypt. +However, sometimes you need more flexibility on decrypt, +especially if you might not know beforehand which CMK was used to encrypt a message. +To address this need, the KMS keyring also supports "discovery" mode. +In discovery mode, the KMS keyring will do nothing on encrypt +but will attempt to decrypt *any* data keys that were encrypted under a KMS CMK. + +However, sometimes you need to be a *bit* more restrictive than that. +To address this need, you can use a client supplier to restrict what regions a KMS keyring can talk to. + +This example shows how to configure and use a KMS keyring in discovery mode that is restricted to one region. + +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/choose-keyring.html#use-kms-keyring + +For an example of how to use the KMS keyring with CMKs in multiple regions, +see the ``keyring/aws_kms/multiple_regions`` example. + +For examples of how to use the KMS keyring with custom client configurations, +see the ``keyring/aws_kms/custom_client_supplier`` +and ``keyring/aws_kms/custom_kms_client_config`` examples. + +For examples of how to use the KMS keyring in discovery mode on decrypt, +see the ``keyring/aws_kms/discovery_decrypt`` +and ``keyring/aws_kms/discovery_decrypt_with_preferred_region`` examples. +""" +import aws_encryption_sdk +from aws_encryption_sdk.keyrings.aws_kms import KmsKeyring +from aws_encryption_sdk.keyrings.aws_kms.client_suppliers import AllowRegionsClientSupplier + + +def run(aws_kms_cmk, source_plaintext): + # type: (str, bytes) -> None + """Demonstrate an encrypt/decrypt cycle using a KMS keyring with a single CMK. + + :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. + # https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#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 data keys are protected. + encrypt_keyring = KmsKeyring(generator_key_id=aws_kms_cmk) + + # Create the KMS discovery keyring that we will use on decrypt. + # + # Because we do not specify any key IDs, this keyring is created in discovery mode. + # + # The client supplier that we specify here will only supply clients for the specified region. + # The keyring only attempts to decrypt data keys if it can get a client for that region, + # so this keyring will now ignore any data keys that were encrypted under a CMK in another region. + decrypt_keyring = KmsKeyring(client_supplier=AllowRegionsClientSupplier(allowed_regions=["us-west-2"])) + + # Encrypt your plaintext data. + ciphertext, _encrypt_header = aws_encryption_sdk.encrypt( + source=source_plaintext, encryption_context=encryption_context, keyring=encrypt_keyring + ) + + # Verify that the ciphertext and plaintext are different. + assert ciphertext != source_plaintext + + # Decrypt your encrypted data using the KMS discovery keyring. + # + # 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=decrypt_keyring) + + # 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 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(decrypt_header.encryption_context.items()) diff --git a/examples/src/keyring/aws_kms/discovery_decrypt_with_preferred_regions.py b/examples/src/keyring/aws_kms/discovery_decrypt_with_preferred_regions.py new file mode 100644 index 000000000..8a68535de --- /dev/null +++ b/examples/src/keyring/aws_kms/discovery_decrypt_with_preferred_regions.py @@ -0,0 +1,109 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +When you give the KMS keyring specific key IDs it will use those CMKs and nothing else. +This is true both on encrypt and on decrypt. +However, sometimes you need more flexibility on decrypt, +especially if you might not know beforehand which CMK was used to encrypt a message. +To address this need, the KMS keyring also supports "discovery" mode. +In discovery mode, the KMS keyring will do nothing on encrypt +but will attempt to decrypt *any* data keys that were encrypted under a KMS CMK. + +However, sometimes you need to be a *bit* more restrictive than that. +To address this need, you can use a client supplier to restrict what regions a KMS keyring can talk to. + +A more complex but more common use-case is that you would *prefer* to stay within a region, +but you would rather make calls to other regions than fail to decrypt the message. +In this case, you want a keyring that will try to decrypt data keys in this region first, +then try other regions. + +This example shows how to configure and use a multi-keyring with the KMS keyring +to prefer the current AWS region while also failing over to other AWS regions. + +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/choose-keyring.html#use-kms-keyring + +For an example of how to use the KMS keyring with CMKs in multiple regions, +see the ``keyring/aws_kms/multiple_regions`` example. + +For examples of how to use the KMS keyring with custom client configurations, +see the ``keyring/aws_kms/custom_client_supplier`` +and ``keyring/aws_kms/custom_kms_client_config`` examples. + +For examples of how to use the KMS keyring in discovery mode on decrypt, +see the ``keyring/aws_kms/discovery_decrypt`` +and ``keyring/aws_kms/discovery_decrypt_in_region_only`` examples. +""" +from boto3.session import Session + +import aws_encryption_sdk +from aws_encryption_sdk.keyrings.aws_kms import KmsKeyring +from aws_encryption_sdk.keyrings.aws_kms.client_suppliers import AllowRegionsClientSupplier, DenyRegionsClientSupplier +from aws_encryption_sdk.keyrings.multi import MultiKeyring + + +def run(aws_kms_cmk, source_plaintext): + # type: (str, bytes) -> None + """Demonstrate an encrypt/decrypt cycle using a KMS keyring with a single CMK. + + :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. + # https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#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 data keys are protected. + encrypt_keyring = KmsKeyring(generator_key_id=aws_kms_cmk) + + # To create our decrypt keyring, we need to know our current default AWS region. + # + # Create a throw-away boto3 session to discover the default region. + boto3_session = Session() + local_region = boto3_session.region_name + + # Now, use that region name to create two KMS discovery keyrings: + # + # One that only works in the local region + local_region_decrypt_keyring = KmsKeyring( + client_supplier=AllowRegionsClientSupplier(allowed_regions=[local_region]) + ) + # and one that will work in any other region but NOT the local region. + other_regions_decrypt_keyring = KmsKeyring(client_supplier=DenyRegionsClientSupplier(denied_regions=[local_region])) + + # Finally, combine those two keyrings into a multi-keyring. + # + # The multi-keyring steps through its member keyrings in the order that you provider them, + # attempting to decrypt every encrypted data key with each keyring before moving on to the next keyring. + # Because of this, other_regions_decrypt_keyring will not be called + # unless local_region_decrypt_keyring fails to decrypt every encrypted data key. + decrypt_keyring = MultiKeyring(children=[local_region_decrypt_keyring, other_regions_decrypt_keyring]) + + # Encrypt your plaintext data. + ciphertext, _encrypt_header = aws_encryption_sdk.encrypt( + source=source_plaintext, encryption_context=encryption_context, keyring=encrypt_keyring + ) + + # Verify that the ciphertext and plaintext are different. + assert ciphertext != source_plaintext + + # Decrypt your encrypted data using the KMS discovery keyring. + # + # 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=decrypt_keyring) + + # 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 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(decrypt_header.encryption_context.items()) From 7770186ca7a61d65c150df637c01c0ba3317905e Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Thu, 12 Mar 2020 14:23:16 -0700 Subject: [PATCH 16/35] docs: add listing of keyring examples --- examples/README.md | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/examples/README.md b/examples/README.md index f370f7407..6ae5bb1e1 100644 --- a/examples/README.md +++ b/examples/README.md @@ -19,8 +19,8 @@ in the [`examples/src/`](./src) directory. To use the library APIs, 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]. +[keyrings](#keyrings) or [cryptographic materials managers](#cryptographic-materials-managers), +or using [master key providers](#master-key-providers). These examples will show you how. ### Keyrings @@ -29,6 +29,20 @@ 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). +* AWS KMS keyring + * [How to use the KMS keyring with a single CMK](./src/keyring/aws_kms/single_cmk.py) + * [How to use the KMS keyring with multiple CMKs in different regions](./src/keyring/aws_kms/multiple_regions.py) + * [How to use the KMS keyring in discovery mode](./src/keyring/aws_kms/discovery_decrypt.py) + * [How to restrict the KMS discovery keyring to a single region](./src/keyring/aws_kms/discovery_decrypt_in_region_only.py) + * [How to prefer the local AWS region but fail over to others with the KMS discovery keyring](./src/keyring/aws_kms/discovery_decrypt_with_preferred_regions.py) +* Multi-keyring + * [How to combine AWS KMS with an offline escrow key](./src/keyring/multi/aws_kms_with_escrow.py) +* Raw keyrings + * [How to use the Raw AES keyring](./src/keyring/raw_aes/raw_aes.py) + * [How to use the Raw RSA keyring](./src/keyring/raw_rsa/private_key_only.py) + * [How to use the Raw RSA keyring with PEM or DER encoded keys](./src/keyring/raw_rsa/private_key_only_from_pem.py) + * [How to use the Raw RSA keyring to encrypt with only the public key](./src/keyring/raw_rsa/public_private_key_separate.py) + ### Cryptographic Materials Managers Keyrings define how your data keys are protected, From 0186e611a927ecb7e44a6ace4f90a24f8e3c0eb9 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Thu, 12 Mar 2020 14:26:32 -0700 Subject: [PATCH 17/35] chore: remove reminder to write more because I already did --- examples/src/keyring/aws_kms/custom_client_supplier.py | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/src/keyring/aws_kms/custom_client_supplier.py b/examples/src/keyring/aws_kms/custom_client_supplier.py index aa8b92553..3e7c5ad4f 100644 --- a/examples/src/keyring/aws_kms/custom_client_supplier.py +++ b/examples/src/keyring/aws_kms/custom_client_supplier.py @@ -33,7 +33,6 @@ ``keyring/aws_kms/discovery_decrypt_in_region_only``, and ``keyring/aws_kms/discovery_decrypt_with_preferred_region`` examples. """ -# TODO: Add explanation of partitions from botocore.client import BaseClient from botocore.session import Session From 23c3766cefc638cbbf2ecbd1b2106fd3200ca12d Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Thu, 12 Mar 2020 15:55:10 -0700 Subject: [PATCH 18/35] docs: clean up multikeyring description --- examples/src/keyring/multi/aws_kms_with_escrow.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/examples/src/keyring/multi/aws_kms_with_escrow.py b/examples/src/keyring/multi/aws_kms_with_escrow.py index 2464ceac2..4fae8455d 100644 --- a/examples/src/keyring/multi/aws_kms_with_escrow.py +++ b/examples/src/keyring/multi/aws_kms_with_escrow.py @@ -4,7 +4,7 @@ One use-case that we have seen customers need is the ability to enjoy the benefits of AWS KMS during normal operation but retain the ability to decrypt encrypted messages offline. -This examples shows how you can use the multi-keyring to achieve this +This example shows how you can use the multi-keyring to achieve this by combining a KMS keyring with a raw RSA keyring. https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/choose-keyring.html#use-multi-keyring @@ -13,9 +13,12 @@ For more examples of how to use the raw RSA keyring, see the ``keyring/raw_rsa`` examples. +In this example we generate a RSA keypair +but in practice you would want to keep your private key in an HSM +or other key management system. + In this example, we use the one-step encrypt and decrypt APIs. """ -# TODO: Expand this description from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.asymmetric import rsa From 59b09df3a1e37a86ad44c26bd97f3e98d960f2aa Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Thu, 12 Mar 2020 15:56:58 -0700 Subject: [PATCH 19/35] docs: rearrange examples readme --- examples/README.md | 43 +++++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/examples/README.md b/examples/README.md index 6ae5bb1e1..4ac1bd629 100644 --- a/examples/README.md +++ b/examples/README.md @@ -14,6 +14,11 @@ and streaming APIs. You can find examples that demonstrate these APIs in the [`examples/src/`](./src) directory. +* [How to use the one-step APIs](./src/onestep_defaults.py) +* [How to change the algorithm suite](./src/onestep_unsigned.py) +* [How to stream data in memory](./src/in_memory_streaming_defaults.py) +* [How to stream data between files](./src/file_streaming_defaults.py) + ## Configuration To use the library APIs, @@ -23,26 +28,36 @@ You can do this using or using [master key providers](#master-key-providers). These examples will show you how. +* AWS KMS + * How to use a single AWS KMS CMK + * [with keyrings](./src/keyring/aws_kms/single_cmk.py) + * How to use multiple AWS KMS CMKs in different regions + * [with keyrings](./src/keyring/aws_kms/multiple_regions.py) + * How to decrypt when you don't know the CMK beforehand + * [with keyrings](./src/keyring/aws_kms/discovery_decrypt.py) + * How to decrypt only within a region + * [with keyrings](./src/keyring/aws_kms/discovery_decrypt_in_region_only.py) + * How to decrypt with a preferred region but fail other to others + * [with keyrings](./src/keyring/aws_kms/discovery_decrypt_with_preferred_regions.py) +* Raw wrapping keys + * How to use a raw AES wrapping key + * [with keyrings](./src/keyring/raw_aes/raw_aes.py) + * How to use a raw RSA wrapping key + * [with keyrings](./src/keyring/raw_rsa/private_key_only.py) + * How to use a raw RSA wrapping key when the key is PEM or DER encoded + * [with keyrings](./src/keyring/raw_rsa/private_key_only_from_pem.py) + * How to encrypt with a raw RSA public key wrapping key without access to the private key + * [with keyrings](./src/keyring/raw_rsa/public_private_key_separate.py) +* Combining wrapping keys + * How to combine AWS KMS with an offline escrow key + * [with keyrings](./src/keyring/multi/aws_kms_with_escrow.py) + ### Keyrings 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). -* AWS KMS keyring - * [How to use the KMS keyring with a single CMK](./src/keyring/aws_kms/single_cmk.py) - * [How to use the KMS keyring with multiple CMKs in different regions](./src/keyring/aws_kms/multiple_regions.py) - * [How to use the KMS keyring in discovery mode](./src/keyring/aws_kms/discovery_decrypt.py) - * [How to restrict the KMS discovery keyring to a single region](./src/keyring/aws_kms/discovery_decrypt_in_region_only.py) - * [How to prefer the local AWS region but fail over to others with the KMS discovery keyring](./src/keyring/aws_kms/discovery_decrypt_with_preferred_regions.py) -* Multi-keyring - * [How to combine AWS KMS with an offline escrow key](./src/keyring/multi/aws_kms_with_escrow.py) -* Raw keyrings - * [How to use the Raw AES keyring](./src/keyring/raw_aes/raw_aes.py) - * [How to use the Raw RSA keyring](./src/keyring/raw_rsa/private_key_only.py) - * [How to use the Raw RSA keyring with PEM or DER encoded keys](./src/keyring/raw_rsa/private_key_only_from_pem.py) - * [How to use the Raw RSA keyring to encrypt with only the public key](./src/keyring/raw_rsa/public_private_key_separate.py) - ### Cryptographic Materials Managers Keyrings define how your data keys are protected, From 9234c019cd125838f671a1bf72bbcf8307b42389 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Thu, 12 Mar 2020 16:21:34 -0700 Subject: [PATCH 20/35] fix: set AWS_DEFAULT_REGION to region of primary CMK for examples tets --- examples/test/examples_test_utils.py | 10 +++++++--- examples/test/test_run_examples.py | 6 ++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/examples/test/examples_test_utils.py b/examples/test/examples_test_utils.py index 4f9aadef5..d46c75699 100644 --- a/examples/test/examples_test_utils.py +++ b/examples/test/examples_test_utils.py @@ -27,6 +27,7 @@ os.environ["AWS_ENCRYPTION_SDK_EXAMPLES_TESTING"] = "yes" sys.path.extend([os.sep.join([os.path.dirname(__file__), "..", "..", "test", "integration"])]) +from integration_test_utils import get_all_cmk_arns # noqa pylint: disable=unused-import,import-error static_plaintext = ( b"Lorem ipsum dolor sit amet, consectetur adipiscing elit. " @@ -58,9 +59,6 @@ ) -from integration_test_utils import get_all_cmk_arns # noqa pylint: disable=unused-import,import-error - - def all_examples(): # type: () -> Iterable[pytest.param] for (dirpath, _dirnames, filenames) in os.walk(EXAMPLES_SOURCE): @@ -114,3 +112,9 @@ def build_kwargs(function, temp_dir): except KeyError: pass return kwargs + + +def default_region(): + # type: () -> str + primary_cmk = get_all_cmk_arns()[0] + return primary_cmk.split(":", 4)[3] diff --git a/examples/test/test_run_examples.py b/examples/test/test_run_examples.py index 781f341ad..210c0119c 100644 --- a/examples/test/test_run_examples.py +++ b/examples/test/test_run_examples.py @@ -5,13 +5,13 @@ import pytest -from .examples_test_utils import all_examples, build_kwargs +from .examples_test_utils import all_examples, build_kwargs, default_region pytestmark = [pytest.mark.examples] @pytest.mark.parametrize("import_path", all_examples()) -def test_examples(import_path, tmp_path): +def test_examples(import_path, tmp_path, monkeypatch): module = import_module(name=import_path, package=__package__) try: run_function = module.run @@ -21,4 +21,6 @@ def test_examples(import_path, tmp_path): kwargs = build_kwargs(function=run_function, temp_dir=tmp_path) + monkeypatch.setenv("AWS_DEFAULT_REGION", default_region()) + run_function(**kwargs) From def80a5efc711b22149aa82b6c755ed429c32f7e Mon Sep 17 00:00:00 2001 From: Matt Bullock Date: Fri, 13 Mar 2020 15:55:05 -0700 Subject: [PATCH 21/35] Apply suggestions from code review Co-Authored-By: June Blender Co-Authored-By: Wesley Rosenblum <55108558+WesleyRosenblum@users.noreply.github.com> --- examples/README.md | 16 ++++++++-------- .../keyring/aws_kms/custom_client_supplier.py | 2 +- .../keyring/aws_kms/custom_kms_client_config.py | 2 +- .../keyring/raw_rsa/private_key_only_from_pem.py | 4 ++-- .../raw_rsa/public_private_key_separate.py | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/examples/README.md b/examples/README.md index 4ac1bd629..ab8005fe6 100644 --- a/examples/README.md +++ b/examples/README.md @@ -14,10 +14,10 @@ and streaming APIs. You can find examples that demonstrate these APIs in the [`examples/src/`](./src) directory. -* [How to use the one-step APIs](./src/onestep_defaults.py) +* [How to encrypt and decrypt](./src/onestep_defaults.py) * [How to change the algorithm suite](./src/onestep_unsigned.py) -* [How to stream data in memory](./src/in_memory_streaming_defaults.py) -* [How to stream data between files](./src/file_streaming_defaults.py) +* [How to encrypt and decrypt data streams in memory](./src/in_memory_streaming_defaults.py) +* [How to encrypt and decrypt data streamed between files](./src/file_streaming_defaults.py) ## Configuration @@ -28,18 +28,18 @@ You can do this using or using [master key providers](#master-key-providers). These examples will show you how. -* AWS KMS +* Use AWS Key Management Service (AWS KMS) * How to use a single AWS KMS CMK * [with keyrings](./src/keyring/aws_kms/single_cmk.py) * How to use multiple AWS KMS CMKs in different regions * [with keyrings](./src/keyring/aws_kms/multiple_regions.py) - * How to decrypt when you don't know the CMK beforehand + * How to decrypt when you don't know the CMK * [with keyrings](./src/keyring/aws_kms/discovery_decrypt.py) - * How to decrypt only within a region + * How to decrypt within a region * [with keyrings](./src/keyring/aws_kms/discovery_decrypt_in_region_only.py) - * How to decrypt with a preferred region but fail other to others + * How to decrypt with a preferred region but failover to others * [with keyrings](./src/keyring/aws_kms/discovery_decrypt_with_preferred_regions.py) -* Raw wrapping keys +* Using raw wrapping keys * How to use a raw AES wrapping key * [with keyrings](./src/keyring/raw_aes/raw_aes.py) * How to use a raw RSA wrapping key diff --git a/examples/src/keyring/aws_kms/custom_client_supplier.py b/examples/src/keyring/aws_kms/custom_client_supplier.py index 3e7c5ad4f..5258e7923 100644 --- a/examples/src/keyring/aws_kms/custom_client_supplier.py +++ b/examples/src/keyring/aws_kms/custom_client_supplier.py @@ -25,7 +25,7 @@ For an example of how to use the KMS keyring with CMKs in multiple regions, see the ``keyring/aws_kms/multiple_regions`` example. -For another example of how to use the KMS keyring with custom client configuration, +For another example of how to use the KMS keyring with a custom client configuration, see the ``keyring/aws_kms/custom_kms_client_config`` example. For examples of how to use the KMS keyring in discovery mode on decrypt, diff --git a/examples/src/keyring/aws_kms/custom_kms_client_config.py b/examples/src/keyring/aws_kms/custom_kms_client_config.py index 5aedaeaee..0d519887a 100644 --- a/examples/src/keyring/aws_kms/custom_kms_client_config.py +++ b/examples/src/keyring/aws_kms/custom_kms_client_config.py @@ -53,7 +53,7 @@ def run(aws_kms_cmk, source_plaintext): # https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html custom_client_config = Config(connect_timeout=10.0, user_agent_extra=USER_AGENT_SUFFIX) # For this example we will just use the default botocore session configuration - # but if you need to you can set custom credentials in the botocore session. + # but if you need to, you can set custom credentials in the botocore session. custom_session = Session() # Use your custom configuration values to configure your client supplier. diff --git a/examples/src/keyring/raw_rsa/private_key_only_from_pem.py b/examples/src/keyring/raw_rsa/private_key_only_from_pem.py index a27cbd867..5d5e42eed 100644 --- a/examples/src/keyring/raw_rsa/private_key_only_from_pem.py +++ b/examples/src/keyring/raw_rsa/private_key_only_from_pem.py @@ -3,7 +3,7 @@ """ When you store RSA keys, you have to serialize them somehow. -This examples shows how to configure and use a raw RSA keyring using a PEM-encoded RSA private key. +This example shows how to configure and use a raw RSA keyring using a PEM-encoded RSA private key. The most commonly used encodings for RSA keys tend to be PEM and DER. The raw RSA keyring supports loading both public and private keys from these encodings. @@ -45,7 +45,7 @@ def run(source_plaintext): private_key = rsa.generate_private_key(public_exponent=65537, key_size=4096, backend=default_backend()) # Serialize the RSA private key to PEM encoding. - # This or DER encoding likely to be what you get from your key management system in practice. + # This or DER encoding is likely to be what you get from your key management system in practice. private_key_pem = private_key.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.PKCS8, diff --git a/examples/src/keyring/raw_rsa/public_private_key_separate.py b/examples/src/keyring/raw_rsa/public_private_key_separate.py index 536aa6917..7a3b10f16 100644 --- a/examples/src/keyring/raw_rsa/public_private_key_separate.py +++ b/examples/src/keyring/raw_rsa/public_private_key_separate.py @@ -10,7 +10,7 @@ when it only has access to a public key. This example shows how to construct and use the raw RSA keyring -to encrypt with only public key and decrypt with the private key. +to encrypt with only the public key and decrypt with the private key. If your RSA key is in PEM or DER format, see the ``keyring/raw_rsa/private_key_only_from_pem`` example. From d0cca29ededd45c12f8a3f09353702faa43cf16b Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Mon, 16 Mar 2020 15:35:09 -0700 Subject: [PATCH 22/35] fix: fix examples test runner for Windows --- examples/test/examples_test_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/test/examples_test_utils.py b/examples/test/examples_test_utils.py index d46c75699..49379ff1b 100644 --- a/examples/test/examples_test_utils.py +++ b/examples/test/examples_test_utils.py @@ -68,7 +68,7 @@ def all_examples(): continue stem, suffix = split_path if suffix == "py" and stem != "__init__": - module_parent = dirpath[len(EXAMPLES_SOURCE) + 1 :].replace("/", ".") + module_parent = dirpath[len(EXAMPLES_SOURCE) + 1 :].replace(os.path.sep, ".") module_name = stem if module_parent: import_path = "..src.{base}.{name}".format(base=module_parent, name=module_name) From ee23080e61da4999e950cc09136a5564f0e28ce0 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Wed, 18 Mar 2020 13:51:10 -0700 Subject: [PATCH 23/35] docs: update examples docs and comments with feedback --- .../keyring/aws_kms/custom_client_supplier.py | 17 ++++++----------- .../src/keyring/aws_kms/discovery_decrypt.py | 2 +- .../aws_kms/discovery_decrypt_in_region_only.py | 2 +- .../discovery_decrypt_with_preferred_regions.py | 2 +- .../src/keyring/aws_kms/multiple_regions.py | 2 +- .../src/keyring/multi/aws_kms_with_escrow.py | 12 ++++++------ examples/src/keyring/raw_aes/raw_aes.py | 2 ++ .../src/keyring/raw_rsa/private_key_only.py | 8 ++++---- .../raw_rsa/private_key_only_from_pem.py | 8 ++++---- .../raw_rsa/public_private_key_separate.py | 8 ++++---- 10 files changed, 30 insertions(+), 33 deletions(-) diff --git a/examples/src/keyring/aws_kms/custom_client_supplier.py b/examples/src/keyring/aws_kms/custom_client_supplier.py index 5258e7923..c07810356 100644 --- a/examples/src/keyring/aws_kms/custom_client_supplier.py +++ b/examples/src/keyring/aws_kms/custom_client_supplier.py @@ -5,20 +5,15 @@ supplies a client with the same configuration for every region. If you need different behavior, you can write your own client supplier. -One use-case where you might need this is if you need to interact with AWS KMS across partitions. -AWS regions are organized into different "partitions" (ex: ``aws`` and ``aws-cn``) -and credentials only work within a single partition. -There are also some regions, -such as Asia Pacific (Hong Kong) and Middle East (Bahrain) -that are in the same partition (``aws``) as other regions -but can still require different credentials. -If you need to work with AWS KMS CMKs in multiple partitions -then you need to always get credentials that are valid -for the partition that you are talking to. +One use-case where you might need this is +if you need different credentials to talk to different AWS regions. +This might be because you are crossing partitions (ex: ``aws`` and ``aws-cn``) +or if you are working with regions that have separate authentication silos +like ``ap-east-1`` and ``me-south-1``. This example shows how to create a client supplier that will supply KMS clients with valid credentials for the target region -even when working with regions across different partitions. +even when working with regions that need different credentials. https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/choose-keyring.html#use-kms-keyring diff --git a/examples/src/keyring/aws_kms/discovery_decrypt.py b/examples/src/keyring/aws_kms/discovery_decrypt.py index c675eb847..f48f4b409 100644 --- a/examples/src/keyring/aws_kms/discovery_decrypt.py +++ b/examples/src/keyring/aws_kms/discovery_decrypt.py @@ -30,7 +30,7 @@ def run(aws_kms_cmk, source_plaintext): # type: (str, bytes) -> None - """Demonstrate an encrypt/decrypt cycle using a KMS keyring with a single CMK. + """Demonstrate configuring a KMS keyring to use discovery mode for decryption. :param str aws_kms_cmk: The ARN of an AWS KMS CMK that protects data keys :param bytes source_plaintext: Plaintext to encrypt diff --git a/examples/src/keyring/aws_kms/discovery_decrypt_in_region_only.py b/examples/src/keyring/aws_kms/discovery_decrypt_in_region_only.py index d06575609..28ac1029c 100644 --- a/examples/src/keyring/aws_kms/discovery_decrypt_in_region_only.py +++ b/examples/src/keyring/aws_kms/discovery_decrypt_in_region_only.py @@ -34,7 +34,7 @@ def run(aws_kms_cmk, source_plaintext): # type: (str, bytes) -> None - """Demonstrate an encrypt/decrypt cycle using a KMS keyring with a single CMK. + """Demonstrate configuring a KMS keyring to only work within a single region. :param str aws_kms_cmk: The ARN of an AWS KMS CMK that protects data keys :param bytes source_plaintext: Plaintext to encrypt diff --git a/examples/src/keyring/aws_kms/discovery_decrypt_with_preferred_regions.py b/examples/src/keyring/aws_kms/discovery_decrypt_with_preferred_regions.py index 8a68535de..25cdcf10e 100644 --- a/examples/src/keyring/aws_kms/discovery_decrypt_with_preferred_regions.py +++ b/examples/src/keyring/aws_kms/discovery_decrypt_with_preferred_regions.py @@ -43,7 +43,7 @@ def run(aws_kms_cmk, source_plaintext): # type: (str, bytes) -> None - """Demonstrate an encrypt/decrypt cycle using a KMS keyring with a single CMK. + """Demonstrate configuring a keyring prefer a particular AWS region and failover to others. :param str aws_kms_cmk: The ARN of an AWS KMS CMK that protects data keys :param bytes source_plaintext: Plaintext to encrypt diff --git a/examples/src/keyring/aws_kms/multiple_regions.py b/examples/src/keyring/aws_kms/multiple_regions.py index 894251002..76d504501 100644 --- a/examples/src/keyring/aws_kms/multiple_regions.py +++ b/examples/src/keyring/aws_kms/multiple_regions.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 configure and use a KMS keyring with a single KMS CMK. +This example shows how to configure and use a KMS keyring with with CMKs in multiple regions. https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/choose-keyring.html#use-kms-keyring diff --git a/examples/src/keyring/multi/aws_kms_with_escrow.py b/examples/src/keyring/multi/aws_kms_with_escrow.py index 4fae8455d..94e58a075 100644 --- a/examples/src/keyring/multi/aws_kms_with_escrow.py +++ b/examples/src/keyring/multi/aws_kms_with_escrow.py @@ -3,7 +3,7 @@ """ One use-case that we have seen customers need is the ability to enjoy the benefits of AWS KMS during normal operation -but retain the ability to decrypt encrypted messages offline. +but retain the ability to decrypt encrypted messages without access to AWS KMS. This example shows how you can use the multi-keyring to achieve this by combining a KMS keyring with a raw RSA keyring. @@ -31,7 +31,7 @@ def run(aws_kms_cmk, source_plaintext): # type: (str, bytes) -> None - """Demonstrate an encrypt/decrypt cycle using a KMS keyring with a single CMK. + """Demonstrate configuring a keyring to use an AWS KMS CMK and a RSA wrapping key. :param str aws_kms_cmk: The ARN of an AWS KMS CMK that protects data keys :param bytes source_plaintext: Plaintext to encrypt @@ -47,10 +47,10 @@ def run(aws_kms_cmk, source_plaintext): } # Generate an RSA private key to use with your keyring. - # In practice, you should get this key from a secure key management system. + # In practice, you should get this key from a secure key management system such as an HSM. # # Why did we use this public exponent? - # https://latacora.singles/2018/04/03/cryptographic-right-answers.html + # https://crypto.stanford.edu/~dabo/pubs/papers/RSA-survey.pdf private_key = rsa.generate_private_key(public_exponent=65537, key_size=4096, backend=default_backend()) # Collect the public key from the private key. @@ -70,8 +70,8 @@ def run(aws_kms_cmk, source_plaintext): # The wrapping algorithm tells the raw RSA keyring # how to use your wrapping key to encrypt data keys. # - # Why did we use this wrapping algorithm? - # https://latacora.singles/2018/04/03/cryptographic-right-answers.html + # We recommend using RSA_OAEP_SHA256_MGF1. + # You should not use RSA_PKCS1 unless you require it for backwards compatibility. wrapping_algorithm=WrappingAlgorithm.RSA_OAEP_SHA256_MGF1, ) diff --git a/examples/src/keyring/raw_aes/raw_aes.py b/examples/src/keyring/raw_aes/raw_aes.py index 503662c35..612dc615b 100644 --- a/examples/src/keyring/raw_aes/raw_aes.py +++ b/examples/src/keyring/raw_aes/raw_aes.py @@ -35,6 +35,8 @@ def run(source_plaintext): # Generate an AES key to use with your keyring. # The key size depends on the wrapping algorithm. + # + # In practice, you should get this key from a secure key management system such as an HSM. key = os.urandom(wrapping_algorithm.algorithm.kdf_input_len) # Create the keyring that determines how your data keys are protected. diff --git a/examples/src/keyring/raw_rsa/private_key_only.py b/examples/src/keyring/raw_rsa/private_key_only.py index ad281753f..8cf731194 100644 --- a/examples/src/keyring/raw_rsa/private_key_only.py +++ b/examples/src/keyring/raw_rsa/private_key_only.py @@ -35,10 +35,10 @@ def run(source_plaintext): } # Generate an RSA private key to use with your keyring. - # In practice, you should get this key from a secure key management system. + # In practice, you should get this key from a secure key management system such as an HSM. # # Why did we use this public exponent? - # https://latacora.singles/2018/04/03/cryptographic-right-answers.html + # https://crypto.stanford.edu/~dabo/pubs/papers/RSA-survey.pdf private_key = rsa.generate_private_key(public_exponent=65537, key_size=4096, backend=default_backend()) # Create the keyring that determines how your data keys are protected. @@ -55,8 +55,8 @@ def run(source_plaintext): # The wrapping algorithm tells the raw RSA keyring # how to use your wrapping key to encrypt data keys. # - # Why did we use this wrapping algorithm? - # https://latacora.singles/2018/04/03/cryptographic-right-answers.html + # We recommend using RSA_OAEP_SHA256_MGF1. + # You should not use RSA_PKCS1 unless you require it for backwards compatibility. wrapping_algorithm=WrappingAlgorithm.RSA_OAEP_SHA256_MGF1, ) diff --git a/examples/src/keyring/raw_rsa/private_key_only_from_pem.py b/examples/src/keyring/raw_rsa/private_key_only_from_pem.py index 5d5e42eed..aa7a0a791 100644 --- a/examples/src/keyring/raw_rsa/private_key_only_from_pem.py +++ b/examples/src/keyring/raw_rsa/private_key_only_from_pem.py @@ -38,10 +38,10 @@ def run(source_plaintext): } # Generate an RSA private key to use with your keyring. - # In practice, you should get this key from a secure key management system. + # In practice, you should get this key from a secure key management system such as an HSM. # # Why did we use this public exponent? - # https://latacora.singles/2018/04/03/cryptographic-right-answers.html + # https://crypto.stanford.edu/~dabo/pubs/papers/RSA-survey.pdf private_key = rsa.generate_private_key(public_exponent=65537, key_size=4096, backend=default_backend()) # Serialize the RSA private key to PEM encoding. @@ -68,8 +68,8 @@ def run(source_plaintext): # The wrapping algorithm tells the raw RSA keyring # how to use your wrapping key to encrypt data keys. # - # Why did we use this wrapping algorithm? - # https://latacora.singles/2018/04/03/cryptographic-right-answers.html + # We recommend using RSA_OAEP_SHA256_MGF1. + # You should not use RSA_PKCS1 unless you require it for backwards compatibility. wrapping_algorithm=WrappingAlgorithm.RSA_OAEP_SHA256_MGF1, ) diff --git a/examples/src/keyring/raw_rsa/public_private_key_separate.py b/examples/src/keyring/raw_rsa/public_private_key_separate.py index 7a3b10f16..66aa6f799 100644 --- a/examples/src/keyring/raw_rsa/public_private_key_separate.py +++ b/examples/src/keyring/raw_rsa/public_private_key_separate.py @@ -45,10 +45,10 @@ def run(source_plaintext): } # Generate an RSA private key to use with your keyring. - # In practice, you should get this key from a secure key management system. + # In practice, you should get this key from a secure key management system such as an HSM. # # Why did we use this public exponent? - # https://latacora.singles/2018/04/03/cryptographic-right-answers.html + # https://crypto.stanford.edu/~dabo/pubs/papers/RSA-survey.pdf private_key = rsa.generate_private_key(public_exponent=65537, key_size=4096, backend=default_backend()) # Collect the public key from the private key. @@ -70,8 +70,8 @@ def run(source_plaintext): # The wrapping algorithm tells the raw RSA keyring # how to use your wrapping key to encrypt data keys. # - # Why did we use this wrapping algorithm? - # https://latacora.singles/2018/04/03/cryptographic-right-answers.html + # We recommend using RSA_OAEP_SHA256_MGF1. + # You should not use RSA_PKCS1 unless you require it for backwards compatibility. wrapping_algorithm=WrappingAlgorithm.RSA_OAEP_SHA256_MGF1, ) From edc57ca01ddee8c2ef468c4aee2876ef02f90672 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Wed, 18 Mar 2020 14:03:18 -0700 Subject: [PATCH 24/35] docs: re-order operations in RSA example for clarity --- .../keyring/raw_rsa/public_private_key_separate.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/src/keyring/raw_rsa/public_private_key_separate.py b/examples/src/keyring/raw_rsa/public_private_key_separate.py index 66aa6f799..a96c8fb36 100644 --- a/examples/src/keyring/raw_rsa/public_private_key_separate.py +++ b/examples/src/keyring/raw_rsa/public_private_key_separate.py @@ -93,12 +93,6 @@ def run(source_plaintext): # Verify that the ciphertext and plaintext are different. assert ciphertext != source_plaintext - # Decrypt your encrypted data using the decrypt keyring. - # - # 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=private_key_keyring) - # Try to decrypt your encrypted data using the *encrypt* keyring. # This demonstrates that you cannot decrypt using the public key. try: @@ -111,6 +105,12 @@ def run(source_plaintext): # Show that the public keyring could not decrypt. raise AssertionError("This will never happen!") + # Decrypt your encrypted data using the decrypt keyring. + # + # 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=private_key_keyring) + # Verify that the decrypted plaintext is identical to the original plaintext. assert decrypted == source_plaintext From 341c5b9cb9d07b8e0303eb1139bdc2a635492da2 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Wed, 18 Mar 2020 14:09:45 -0700 Subject: [PATCH 25/35] docs: add framing for examples in readme --- examples/README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/README.md b/examples/README.md index ab8005fe6..7fe35bb1a 100644 --- a/examples/README.md +++ b/examples/README.md @@ -26,9 +26,11 @@ 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. +These examples will show you how to use the configuration tools that we include for you +as well as how to create some of your own. +We start with AWS KMS examples, then show how to use other wrapping keys. -* Use AWS Key Management Service (AWS KMS) +* Using AWS Key Management Service (AWS KMS) * How to use a single AWS KMS CMK * [with keyrings](./src/keyring/aws_kms/single_cmk.py) * How to use multiple AWS KMS CMKs in different regions From 5a364b9284a7bd41c3bed2a7684d72cc023b23c5 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Wed, 18 Mar 2020 21:15:56 -0700 Subject: [PATCH 26/35] docs: adjust wording --- examples/README.md | 3 ++- examples/src/file_streaming_defaults.py | 4 ++-- examples/src/in_memory_streaming_defaults.py | 4 ++-- examples/src/keyring/aws_kms/custom_client_supplier.py | 4 ++-- examples/src/keyring/aws_kms/custom_kms_client_config.py | 4 ++-- examples/src/keyring/aws_kms/discovery_decrypt.py | 4 ++-- .../src/keyring/aws_kms/discovery_decrypt_in_region_only.py | 4 ++-- .../aws_kms/discovery_decrypt_with_preferred_regions.py | 4 ++-- examples/src/keyring/aws_kms/multiple_regions.py | 4 ++-- examples/src/keyring/aws_kms/single_cmk.py | 4 ++-- examples/src/keyring/multi/aws_kms_with_escrow.py | 4 ++-- examples/src/keyring/raw_aes/raw_aes.py | 4 ++-- examples/src/keyring/raw_rsa/private_key_only.py | 4 ++-- examples/src/keyring/raw_rsa/private_key_only_from_pem.py | 4 ++-- examples/src/keyring/raw_rsa/public_private_key_separate.py | 4 ++-- examples/src/onestep_defaults.py | 4 ++-- examples/src/onestep_unsigned.py | 4 ++-- 17 files changed, 34 insertions(+), 33 deletions(-) diff --git a/examples/README.md b/examples/README.md index 7fe35bb1a..a7cf080de 100644 --- a/examples/README.md +++ b/examples/README.md @@ -87,7 +87,8 @@ you can find these examples in [`examples/src/master_key_provider`](./src/master ## Legacy -This section includes older examples, including examples of using master keys and master key providers in Java and Python. +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, 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 983bfc90d..b9200406f 100644 --- a/examples/src/file_streaming_defaults.py +++ b/examples/src/file_streaming_defaults.py @@ -54,7 +54,7 @@ def run(aws_kms_cmk, source_plaintext_filename): for segment in encryptor: ciphertext.write(segment) - # Verify that the ciphertext and plaintext are different. + # Demonstrate that the ciphertext and plaintext are different. assert not filecmp.cmp(source_plaintext_filename, ciphertext_filename) # Open the files you want to work with. @@ -78,5 +78,5 @@ def run(aws_kms_cmk, source_plaintext_filename): for segment in decryptor: decrypted.write(segment) - # Verify that the decrypted plaintext is identical to the original plaintext. + # Demonstrate 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 1a2824b94..3daa0d22b 100644 --- a/examples/src/in_memory_streaming_defaults.py +++ b/examples/src/in_memory_streaming_defaults.py @@ -49,7 +49,7 @@ def run(aws_kms_cmk, source_plaintext): for segment in encryptor: ciphertext.write(segment) - # Verify that the ciphertext and plaintext are different. + # Demonstrate 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. @@ -75,5 +75,5 @@ def run(aws_kms_cmk, source_plaintext): for segment in decryptor: decrypted.write(segment) - # Verify that the decrypted plaintext is identical to the original plaintext. + # Demonstrate that the decrypted plaintext is identical to the original plaintext. assert decrypted.getvalue() == source_plaintext diff --git a/examples/src/keyring/aws_kms/custom_client_supplier.py b/examples/src/keyring/aws_kms/custom_client_supplier.py index c07810356..d662e4653 100644 --- a/examples/src/keyring/aws_kms/custom_client_supplier.py +++ b/examples/src/keyring/aws_kms/custom_client_supplier.py @@ -95,7 +95,7 @@ def run(aws_kms_cmk, source_plaintext): source=source_plaintext, encryption_context=encryption_context, keyring=keyring ) - # Verify that the ciphertext and plaintext are different. + # Demonstrate that the ciphertext and plaintext are different. assert ciphertext != source_plaintext # Decrypt your encrypted data using the same keyring you used on encrypt. @@ -104,7 +104,7 @@ 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 decrypted plaintext is identical to the original plaintext. + # Demonstrate that the decrypted plaintext is identical to the original plaintext. assert decrypted == source_plaintext # Verify that the encryption context used in the decrypt operation includes diff --git a/examples/src/keyring/aws_kms/custom_kms_client_config.py b/examples/src/keyring/aws_kms/custom_kms_client_config.py index 0d519887a..dc622ad3a 100644 --- a/examples/src/keyring/aws_kms/custom_kms_client_config.py +++ b/examples/src/keyring/aws_kms/custom_kms_client_config.py @@ -68,7 +68,7 @@ def run(aws_kms_cmk, source_plaintext): source=source_plaintext, encryption_context=encryption_context, keyring=keyring ) - # Verify that the ciphertext and plaintext are different. + # Demonstrate that the ciphertext and plaintext are different. assert ciphertext != source_plaintext # Decrypt your encrypted data using the same keyring you used on encrypt. @@ -77,7 +77,7 @@ 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 decrypted plaintext is identical to the original plaintext. + # Demonstrate that the decrypted plaintext is identical to the original plaintext. assert decrypted == source_plaintext # Verify that the encryption context used in the decrypt operation includes diff --git a/examples/src/keyring/aws_kms/discovery_decrypt.py b/examples/src/keyring/aws_kms/discovery_decrypt.py index f48f4b409..f5ac3cfff 100644 --- a/examples/src/keyring/aws_kms/discovery_decrypt.py +++ b/examples/src/keyring/aws_kms/discovery_decrypt.py @@ -58,7 +58,7 @@ def run(aws_kms_cmk, source_plaintext): source=source_plaintext, encryption_context=encryption_context, keyring=encrypt_keyring ) - # Verify that the ciphertext and plaintext are different. + # Demonstrate that the ciphertext and plaintext are different. assert ciphertext != source_plaintext # Decrypt your encrypted data using the KMS discovery keyring. @@ -67,7 +67,7 @@ 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=decrypt_keyring) - # Verify that the decrypted plaintext is identical to the original plaintext. + # Demonstrate that the decrypted plaintext is identical to the original plaintext. assert decrypted == source_plaintext # Verify that the encryption context used in the decrypt operation includes diff --git a/examples/src/keyring/aws_kms/discovery_decrypt_in_region_only.py b/examples/src/keyring/aws_kms/discovery_decrypt_in_region_only.py index 28ac1029c..0041eddc3 100644 --- a/examples/src/keyring/aws_kms/discovery_decrypt_in_region_only.py +++ b/examples/src/keyring/aws_kms/discovery_decrypt_in_region_only.py @@ -66,7 +66,7 @@ def run(aws_kms_cmk, source_plaintext): source=source_plaintext, encryption_context=encryption_context, keyring=encrypt_keyring ) - # Verify that the ciphertext and plaintext are different. + # Demonstrate that the ciphertext and plaintext are different. assert ciphertext != source_plaintext # Decrypt your encrypted data using the KMS discovery keyring. @@ -75,7 +75,7 @@ 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=decrypt_keyring) - # Verify that the decrypted plaintext is identical to the original plaintext. + # Demonstrate that the decrypted plaintext is identical to the original plaintext. assert decrypted == source_plaintext # Verify that the encryption context used in the decrypt operation includes diff --git a/examples/src/keyring/aws_kms/discovery_decrypt_with_preferred_regions.py b/examples/src/keyring/aws_kms/discovery_decrypt_with_preferred_regions.py index 25cdcf10e..52399f86f 100644 --- a/examples/src/keyring/aws_kms/discovery_decrypt_with_preferred_regions.py +++ b/examples/src/keyring/aws_kms/discovery_decrypt_with_preferred_regions.py @@ -89,7 +89,7 @@ def run(aws_kms_cmk, source_plaintext): source=source_plaintext, encryption_context=encryption_context, keyring=encrypt_keyring ) - # Verify that the ciphertext and plaintext are different. + # Demonstrate that the ciphertext and plaintext are different. assert ciphertext != source_plaintext # Decrypt your encrypted data using the KMS discovery keyring. @@ -98,7 +98,7 @@ 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=decrypt_keyring) - # Verify that the decrypted plaintext is identical to the original plaintext. + # Demonstrate that the decrypted plaintext is identical to the original plaintext. assert decrypted == source_plaintext # Verify that the encryption context used in the decrypt operation includes diff --git a/examples/src/keyring/aws_kms/multiple_regions.py b/examples/src/keyring/aws_kms/multiple_regions.py index 76d504501..3aa6c812d 100644 --- a/examples/src/keyring/aws_kms/multiple_regions.py +++ b/examples/src/keyring/aws_kms/multiple_regions.py @@ -66,7 +66,7 @@ def run(aws_kms_generator_cmk, aws_kms_additional_cmks, source_plaintext): # It should contain one EDK for each CMK. assert len(encrypt_header.encrypted_data_keys) == len(aws_kms_additional_cmks) + 1 - # Verify that the ciphertext and plaintext are different. + # Demonstrate that the ciphertext and plaintext are different. assert ciphertext != source_plaintext # Decrypt your encrypted data separately using the single-CMK keyrings. @@ -80,7 +80,7 @@ def run(aws_kms_generator_cmk, aws_kms_additional_cmks, source_plaintext): source=ciphertext, keyring=single_cmk_keyring_that_encrypted ) - # Verify that the decrypted plaintext is identical to the original plaintext. + # Demonstrate that the decrypted plaintext is identical to the original plaintext. assert decrypted_1 == source_plaintext assert decrypted_2 == source_plaintext diff --git a/examples/src/keyring/aws_kms/single_cmk.py b/examples/src/keyring/aws_kms/single_cmk.py index 2bf0a1e0f..ec55578ed 100644 --- a/examples/src/keyring/aws_kms/single_cmk.py +++ b/examples/src/keyring/aws_kms/single_cmk.py @@ -46,7 +46,7 @@ def run(aws_kms_cmk, source_plaintext): source=source_plaintext, encryption_context=encryption_context, keyring=keyring ) - # Verify that the ciphertext and plaintext are different. + # Demonstrate that the ciphertext and plaintext are different. assert ciphertext != source_plaintext # Decrypt your encrypted data using the same keyring you used on encrypt. @@ -55,7 +55,7 @@ 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 decrypted plaintext is identical to the original plaintext. + # Demonstrate that the decrypted plaintext is identical to the original plaintext. assert decrypted == source_plaintext # Verify that the encryption context used in the decrypt operation includes diff --git a/examples/src/keyring/multi/aws_kms_with_escrow.py b/examples/src/keyring/multi/aws_kms_with_escrow.py index 94e58a075..a05b84406 100644 --- a/examples/src/keyring/multi/aws_kms_with_escrow.py +++ b/examples/src/keyring/multi/aws_kms_with_escrow.py @@ -100,7 +100,7 @@ def run(aws_kms_cmk, source_plaintext): # It should contain one EDK for KMS and one for the escrow key. assert len(encrypt_header.encrypted_data_keys) == 2 - # Verify that the ciphertext and plaintext are different. + # Demonstrate that the ciphertext and plaintext are different. assert ciphertext != source_plaintext # Decrypt your encrypted data separately using the KMS keyring and the escrow decrypt keyring. @@ -112,7 +112,7 @@ def run(aws_kms_cmk, source_plaintext): source=ciphertext, keyring=escrow_decrypt_keyring ) - # Verify that the decrypted plaintext is identical to the original plaintext. + # Demonstrate that the decrypted plaintext is identical to the original plaintext. assert decrypted_kms == source_plaintext assert decrypted_escrow == source_plaintext diff --git a/examples/src/keyring/raw_aes/raw_aes.py b/examples/src/keyring/raw_aes/raw_aes.py index 612dc615b..66f6dcb96 100644 --- a/examples/src/keyring/raw_aes/raw_aes.py +++ b/examples/src/keyring/raw_aes/raw_aes.py @@ -58,7 +58,7 @@ def run(source_plaintext): source=source_plaintext, encryption_context=encryption_context, keyring=keyring ) - # Verify that the ciphertext and plaintext are different. + # Demonstrate that the ciphertext and plaintext are different. assert ciphertext != source_plaintext # Decrypt your encrypted data using the same keyring you used on encrypt. @@ -67,7 +67,7 @@ def run(source_plaintext): # because the header message includes the encryption context. decrypted, decrypt_header = aws_encryption_sdk.decrypt(source=ciphertext, keyring=keyring) - # Verify that the decrypted plaintext is identical to the original plaintext. + # Demonstrate that the decrypted plaintext is identical to the original plaintext. assert decrypted == source_plaintext # Verify that the encryption context used in the decrypt operation includes diff --git a/examples/src/keyring/raw_rsa/private_key_only.py b/examples/src/keyring/raw_rsa/private_key_only.py index 8cf731194..7c7f94ad4 100644 --- a/examples/src/keyring/raw_rsa/private_key_only.py +++ b/examples/src/keyring/raw_rsa/private_key_only.py @@ -65,7 +65,7 @@ def run(source_plaintext): source=source_plaintext, encryption_context=encryption_context, keyring=keyring ) - # Verify that the ciphertext and plaintext are different. + # Demonstrate that the ciphertext and plaintext are different. assert ciphertext != source_plaintext # Decrypt your encrypted data using the same keyring you used on encrypt. @@ -74,7 +74,7 @@ def run(source_plaintext): # because the header message includes the encryption context. decrypted, decrypt_header = aws_encryption_sdk.decrypt(source=ciphertext, keyring=keyring) - # Verify that the decrypted plaintext is identical to the original plaintext. + # Demonstrate that the decrypted plaintext is identical to the original plaintext. assert decrypted == source_plaintext # Verify that the encryption context used in the decrypt operation includes diff --git a/examples/src/keyring/raw_rsa/private_key_only_from_pem.py b/examples/src/keyring/raw_rsa/private_key_only_from_pem.py index aa7a0a791..4d4f33ff1 100644 --- a/examples/src/keyring/raw_rsa/private_key_only_from_pem.py +++ b/examples/src/keyring/raw_rsa/private_key_only_from_pem.py @@ -78,7 +78,7 @@ def run(source_plaintext): source=source_plaintext, encryption_context=encryption_context, keyring=keyring ) - # Verify that the ciphertext and plaintext are different. + # Demonstrate that the ciphertext and plaintext are different. assert ciphertext != source_plaintext # Decrypt your encrypted data using the same keyring you used on encrypt. @@ -87,7 +87,7 @@ def run(source_plaintext): # because the header message includes the encryption context. decrypted, decrypt_header = aws_encryption_sdk.decrypt(source=ciphertext, keyring=keyring) - # Verify that the decrypted plaintext is identical to the original plaintext. + # Demonstrate that the decrypted plaintext is identical to the original plaintext. assert decrypted == source_plaintext # Verify that the encryption context used in the decrypt operation includes diff --git a/examples/src/keyring/raw_rsa/public_private_key_separate.py b/examples/src/keyring/raw_rsa/public_private_key_separate.py index a96c8fb36..065724163 100644 --- a/examples/src/keyring/raw_rsa/public_private_key_separate.py +++ b/examples/src/keyring/raw_rsa/public_private_key_separate.py @@ -90,7 +90,7 @@ def run(source_plaintext): source=source_plaintext, encryption_context=encryption_context, keyring=public_key_keyring ) - # Verify that the ciphertext and plaintext are different. + # Demonstrate that the ciphertext and plaintext are different. assert ciphertext != source_plaintext # Try to decrypt your encrypted data using the *encrypt* keyring. @@ -111,7 +111,7 @@ def run(source_plaintext): # because the header message includes the encryption context. decrypted, decrypt_header = aws_encryption_sdk.decrypt(source=ciphertext, keyring=private_key_keyring) - # Verify that the decrypted plaintext is identical to the original plaintext. + # Demonstrate that the decrypted plaintext is identical to the original plaintext. assert decrypted == source_plaintext # Verify that the encryption context used in the decrypt operation includes diff --git a/examples/src/onestep_defaults.py b/examples/src/onestep_defaults.py index a1d6e3dbd..5f7bbbc8e 100644 --- a/examples/src/onestep_defaults.py +++ b/examples/src/onestep_defaults.py @@ -37,7 +37,7 @@ def run(aws_kms_cmk, source_plaintext): source=source_plaintext, encryption_context=encryption_context, keyring=keyring ) - # Verify that the ciphertext and plaintext are different. + # Demonstrate that the ciphertext and plaintext are different. assert ciphertext != source_plaintext # Decrypt your encrypted data using the same keyring you used on encrypt. @@ -46,7 +46,7 @@ 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 decrypted plaintext is identical to the original plaintext. + # Demonstrate that the decrypted plaintext is identical to the original plaintext. assert decrypted == source_plaintext # Verify that the encryption context used in the decrypt operation includes diff --git a/examples/src/onestep_unsigned.py b/examples/src/onestep_unsigned.py index aafb09feb..73eab5931 100644 --- a/examples/src/onestep_unsigned.py +++ b/examples/src/onestep_unsigned.py @@ -54,7 +54,7 @@ def run(aws_kms_cmk, source_plaintext): algorithm=AlgorithmSuite.AES_256_GCM_IV12_TAG16_HKDF_SHA256, ) - # Verify that the ciphertext and plaintext are different. + # Demonstrate that the ciphertext and plaintext are different. assert ciphertext != source_plaintext # Decrypt your encrypted data using the same keyring you used on encrypt. @@ -66,7 +66,7 @@ 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 decrypted plaintext is identical to the original plaintext. + # Demonstrate that the decrypted plaintext is identical to the original plaintext. assert decrypted == source_plaintext # Verify that the encryption context used in the decrypt operation includes From ad25893a36dfbe0259f4b8b1a9d59a2c7fa5b4d3 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Thu, 19 Mar 2020 11:23:08 -0700 Subject: [PATCH 27/35] docs: remove reference to Java --- examples/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/README.md b/examples/README.md index a7cf080de..647915e92 100644 --- a/examples/README.md +++ b/examples/README.md @@ -88,7 +88,7 @@ you can find these examples in [`examples/src/master_key_provider`](./src/master ## Legacy This section includes older examples, -including examples of using master keys and master key providers in Java and Python. +including examples of using master keys and master key providers. You can use them as a reference, 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). From 8e44484f9c71d4271fbe476371785f39d95e6c60 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Thu, 19 Mar 2020 12:56:12 -0700 Subject: [PATCH 28/35] docs: derive allowed discovery region from ARN rather than hard-coding it --- .../src/keyring/aws_kms/discovery_decrypt_in_region_only.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/src/keyring/aws_kms/discovery_decrypt_in_region_only.py b/examples/src/keyring/aws_kms/discovery_decrypt_in_region_only.py index 0041eddc3..2ffa2f1af 100644 --- a/examples/src/keyring/aws_kms/discovery_decrypt_in_region_only.py +++ b/examples/src/keyring/aws_kms/discovery_decrypt_in_region_only.py @@ -52,6 +52,9 @@ def run(aws_kms_cmk, source_plaintext): # Create the keyring that determines how your data keys are protected. encrypt_keyring = KmsKeyring(generator_key_id=aws_kms_cmk) + # Extract the region from the CMK ARN. + decrypt_region = aws_kms_cmk.split(":", 4)[3] + # Create the KMS discovery keyring that we will use on decrypt. # # Because we do not specify any key IDs, this keyring is created in discovery mode. @@ -59,7 +62,7 @@ def run(aws_kms_cmk, source_plaintext): # The client supplier that we specify here will only supply clients for the specified region. # The keyring only attempts to decrypt data keys if it can get a client for that region, # so this keyring will now ignore any data keys that were encrypted under a CMK in another region. - decrypt_keyring = KmsKeyring(client_supplier=AllowRegionsClientSupplier(allowed_regions=["us-west-2"])) + decrypt_keyring = KmsKeyring(client_supplier=AllowRegionsClientSupplier(allowed_regions=[decrypt_region])) # Encrypt your plaintext data. ciphertext, _encrypt_header = aws_encryption_sdk.encrypt( From 9acd33db14263817e30109f09a26bcc6a5f19bcf Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Tue, 24 Mar 2020 11:22:14 -0700 Subject: [PATCH 29/35] docs: change examples to talk about "KMS discovery keyring" rather than "KMS keyring in discovery mode" --- examples/src/keyring/aws_kms/discovery_decrypt.py | 4 ++-- .../aws_kms/discovery_decrypt_in_region_only.py | 8 ++++---- .../discovery_decrypt_with_preferred_regions.py | 13 ++++++------- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/examples/src/keyring/aws_kms/discovery_decrypt.py b/examples/src/keyring/aws_kms/discovery_decrypt.py index f5ac3cfff..92bb9db47 100644 --- a/examples/src/keyring/aws_kms/discovery_decrypt.py +++ b/examples/src/keyring/aws_kms/discovery_decrypt.py @@ -5,8 +5,8 @@ This is true both on encrypt and on decrypt. However, sometimes you need more flexibility on decrypt, especially if you might not know beforehand which CMK was used to encrypt a message. -To address this need, the KMS keyring also supports "discovery" mode. -In discovery mode, the KMS keyring will do nothing on encrypt +To address this need, you can use a KMS discovery keyring. +The KMS discovery keyring will do nothing on encrypt but will attempt to decrypt *any* data keys that were encrypted under a KMS CMK. This example shows how to configure and use a KMS keyring in discovery mode. diff --git a/examples/src/keyring/aws_kms/discovery_decrypt_in_region_only.py b/examples/src/keyring/aws_kms/discovery_decrypt_in_region_only.py index 2ffa2f1af..5ee8d7e9a 100644 --- a/examples/src/keyring/aws_kms/discovery_decrypt_in_region_only.py +++ b/examples/src/keyring/aws_kms/discovery_decrypt_in_region_only.py @@ -5,14 +5,14 @@ This is true both on encrypt and on decrypt. However, sometimes you need more flexibility on decrypt, especially if you might not know beforehand which CMK was used to encrypt a message. -To address this need, the KMS keyring also supports "discovery" mode. -In discovery mode, the KMS keyring will do nothing on encrypt +To address this need, you can use a KMS discovery keyring. +The KMS discovery keyring will do nothing on encrypt but will attempt to decrypt *any* data keys that were encrypted under a KMS CMK. However, sometimes you need to be a *bit* more restrictive than that. To address this need, you can use a client supplier to restrict what regions a KMS keyring can talk to. -This example shows how to configure and use a KMS keyring in discovery mode that is restricted to one region. +This example shows how to configure and use a KMS regional discovery keyring that is restricted to one region. https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/choose-keyring.html#use-kms-keyring @@ -23,7 +23,7 @@ see the ``keyring/aws_kms/custom_client_supplier`` and ``keyring/aws_kms/custom_kms_client_config`` examples. -For examples of how to use the KMS keyring in discovery mode on decrypt, +For examples of how to use the KMS discovery keyring on decrypt, see the ``keyring/aws_kms/discovery_decrypt`` and ``keyring/aws_kms/discovery_decrypt_with_preferred_region`` examples. """ diff --git a/examples/src/keyring/aws_kms/discovery_decrypt_with_preferred_regions.py b/examples/src/keyring/aws_kms/discovery_decrypt_with_preferred_regions.py index 52399f86f..a73046c8c 100644 --- a/examples/src/keyring/aws_kms/discovery_decrypt_with_preferred_regions.py +++ b/examples/src/keyring/aws_kms/discovery_decrypt_with_preferred_regions.py @@ -5,8 +5,8 @@ This is true both on encrypt and on decrypt. However, sometimes you need more flexibility on decrypt, especially if you might not know beforehand which CMK was used to encrypt a message. -To address this need, the KMS keyring also supports "discovery" mode. -In discovery mode, the KMS keyring will do nothing on encrypt +To address this need, you can use a KMS discovery keyring. +The KMS discovery keyring will do nothing on encrypt but will attempt to decrypt *any* data keys that were encrypted under a KMS CMK. However, sometimes you need to be a *bit* more restrictive than that. @@ -29,7 +29,7 @@ see the ``keyring/aws_kms/custom_client_supplier`` and ``keyring/aws_kms/custom_kms_client_config`` examples. -For examples of how to use the KMS keyring in discovery mode on decrypt, +For examples of how to use the KMS discovery keyring on decrypt, see the ``keyring/aws_kms/discovery_decrypt`` and ``keyring/aws_kms/discovery_decrypt_in_region_only`` examples. """ @@ -64,8 +64,7 @@ def run(aws_kms_cmk, source_plaintext): # To create our decrypt keyring, we need to know our current default AWS region. # # Create a throw-away boto3 session to discover the default region. - boto3_session = Session() - local_region = boto3_session.region_name + local_region = Session().region_name # Now, use that region name to create two KMS discovery keyrings: # @@ -78,7 +77,7 @@ def run(aws_kms_cmk, source_plaintext): # Finally, combine those two keyrings into a multi-keyring. # - # The multi-keyring steps through its member keyrings in the order that you provider them, + # The multi-keyring steps through its member keyrings in the order that you provide them, # attempting to decrypt every encrypted data key with each keyring before moving on to the next keyring. # Because of this, other_regions_decrypt_keyring will not be called # unless local_region_decrypt_keyring fails to decrypt every encrypted data key. @@ -92,7 +91,7 @@ def run(aws_kms_cmk, source_plaintext): # Demonstrate that the ciphertext and plaintext are different. assert ciphertext != source_plaintext - # Decrypt your encrypted data using the KMS discovery keyring. + # Decrypt your encrypted data using the multi-keyring. # # We do not need to specify the encryption context on decrypt # because the header message includes the encryption context. From b8fdf96400774be12b71c223a1a641940a13cbf5 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Tue, 24 Mar 2020 11:23:00 -0700 Subject: [PATCH 30/35] docs: add link to NIST docs explaining RSA key size and fix typos --- examples/src/keyring/multi/aws_kms_with_escrow.py | 3 +++ examples/src/keyring/raw_rsa/private_key_only.py | 3 +++ examples/src/keyring/raw_rsa/private_key_only_from_pem.py | 3 +++ .../src/keyring/raw_rsa/public_private_key_separate.py | 7 +++++-- 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/examples/src/keyring/multi/aws_kms_with_escrow.py b/examples/src/keyring/multi/aws_kms_with_escrow.py index a05b84406..70e5bef8f 100644 --- a/examples/src/keyring/multi/aws_kms_with_escrow.py +++ b/examples/src/keyring/multi/aws_kms_with_escrow.py @@ -49,6 +49,9 @@ def run(aws_kms_cmk, source_plaintext): # Generate an RSA private key to use with your keyring. # In practice, you should get this key from a secure key management system such as an HSM. # + # The National Institute of Standards and Technology (NIST) recommends a minimum of 2048-bit keys for RSA. + # https://www.nist.gov/publications/transitioning-use-cryptographic-algorithms-and-key-lengths + # # Why did we use this public exponent? # https://crypto.stanford.edu/~dabo/pubs/papers/RSA-survey.pdf private_key = rsa.generate_private_key(public_exponent=65537, key_size=4096, backend=default_backend()) diff --git a/examples/src/keyring/raw_rsa/private_key_only.py b/examples/src/keyring/raw_rsa/private_key_only.py index 7c7f94ad4..0a7d11d01 100644 --- a/examples/src/keyring/raw_rsa/private_key_only.py +++ b/examples/src/keyring/raw_rsa/private_key_only.py @@ -37,6 +37,9 @@ def run(source_plaintext): # Generate an RSA private key to use with your keyring. # In practice, you should get this key from a secure key management system such as an HSM. # + # The National Institute of Standards and Technology (NIST) recommends a minimum of 2048-bit keys for RSA. + # https://www.nist.gov/publications/transitioning-use-cryptographic-algorithms-and-key-lengths + # # Why did we use this public exponent? # https://crypto.stanford.edu/~dabo/pubs/papers/RSA-survey.pdf private_key = rsa.generate_private_key(public_exponent=65537, key_size=4096, backend=default_backend()) diff --git a/examples/src/keyring/raw_rsa/private_key_only_from_pem.py b/examples/src/keyring/raw_rsa/private_key_only_from_pem.py index 4d4f33ff1..4f6b03155 100644 --- a/examples/src/keyring/raw_rsa/private_key_only_from_pem.py +++ b/examples/src/keyring/raw_rsa/private_key_only_from_pem.py @@ -40,6 +40,9 @@ def run(source_plaintext): # Generate an RSA private key to use with your keyring. # In practice, you should get this key from a secure key management system such as an HSM. # + # The National Institute of Standards and Technology (NIST) recommends a minimum of 2048-bit keys for RSA. + # https://www.nist.gov/publications/transitioning-use-cryptographic-algorithms-and-key-lengths + # # Why did we use this public exponent? # https://crypto.stanford.edu/~dabo/pubs/papers/RSA-survey.pdf private_key = rsa.generate_private_key(public_exponent=65537, key_size=4096, backend=default_backend()) diff --git a/examples/src/keyring/raw_rsa/public_private_key_separate.py b/examples/src/keyring/raw_rsa/public_private_key_separate.py index 065724163..1dd28bdba 100644 --- a/examples/src/keyring/raw_rsa/public_private_key_separate.py +++ b/examples/src/keyring/raw_rsa/public_private_key_separate.py @@ -3,7 +3,7 @@ """ One of the benefits of asymmetric encryption is that you can encrypt with just the public key. -This means that you give someone the ability to encrypt +This means that you can give someone the ability to encrypt without giving them the ability to decrypt. The raw RSA keyring supports encrypt-only operations @@ -47,6 +47,9 @@ def run(source_plaintext): # Generate an RSA private key to use with your keyring. # In practice, you should get this key from a secure key management system such as an HSM. # + # The National Institute of Standards and Technology (NIST) recommends a minimum of 2048-bit keys for RSA. + # https://www.nist.gov/publications/transitioning-use-cryptographic-algorithms-and-key-lengths + # # Why did we use this public exponent? # https://crypto.stanford.edu/~dabo/pubs/papers/RSA-survey.pdf private_key = rsa.generate_private_key(public_exponent=65537, key_size=4096, backend=default_backend()) @@ -103,7 +106,7 @@ def run(source_plaintext): pass else: # Show that the public keyring could not decrypt. - raise AssertionError("This will never happen!") + raise AssertionError("The public key can never decrypt!") # Decrypt your encrypted data using the decrypt keyring. # From dd59a36be949fd4bdc331e00ebee82a8bf52ca74 Mon Sep 17 00:00:00 2001 From: Matt Bullock Date: Tue, 24 Mar 2020 21:52:35 -0700 Subject: [PATCH 31/35] docs: apply suggestions from code review Co-Authored-By: June Blender --- examples/README.md | 10 +++++----- .../keyring/aws_kms/custom_client_supplier.py | 6 +++--- .../aws_kms/custom_kms_client_config.py | 12 ++++++------ .../src/keyring/aws_kms/discovery_decrypt.py | 18 +++++++++--------- .../discovery_decrypt_in_region_only.py | 8 ++++---- 5 files changed, 27 insertions(+), 27 deletions(-) diff --git a/examples/README.md b/examples/README.md index 647915e92..592872232 100644 --- a/examples/README.md +++ b/examples/README.md @@ -21,17 +21,17 @@ in the [`examples/src/`](./src) directory. ## Configuration -To use the library APIs, +To use the encryption and decryption APIs, you need to describe how you want the library to protect your data keys. -You can do this using +You can do this by configuring [keyrings](#keyrings) or [cryptographic materials managers](#cryptographic-materials-managers), -or using [master key providers](#master-key-providers). +or by configuring [master key providers](#master-key-providers). These examples will show you how to use the configuration tools that we include for you -as well as how to create some of your own. +and how to create some of your own. We start with AWS KMS examples, then show how to use other wrapping keys. * Using AWS Key Management Service (AWS KMS) - * How to use a single AWS KMS CMK + * How to use one AWS KMS CMK * [with keyrings](./src/keyring/aws_kms/single_cmk.py) * How to use multiple AWS KMS CMKs in different regions * [with keyrings](./src/keyring/aws_kms/multiple_regions.py) diff --git a/examples/src/keyring/aws_kms/custom_client_supplier.py b/examples/src/keyring/aws_kms/custom_client_supplier.py index d662e4653..7efa901fe 100644 --- a/examples/src/keyring/aws_kms/custom_client_supplier.py +++ b/examples/src/keyring/aws_kms/custom_client_supplier.py @@ -5,8 +5,8 @@ supplies a client with the same configuration for every region. If you need different behavior, you can write your own client supplier. -One use-case where you might need this is -if you need different credentials to talk to different AWS regions. +You might use this +if you need different credentials in different AWS regions. This might be because you are crossing partitions (ex: ``aws`` and ``aws-cn``) or if you are working with regions that have separate authentication silos like ``ap-east-1`` and ``me-south-1``. @@ -38,7 +38,7 @@ try: # Python 3.5.0 and 3.5.1 have incompatible typing modules from typing import Union # noqa pylint: disable=unused-import except ImportError: # pragma: no cover - # We only actually need these imports when running the mypy checks + # We only need these imports when running the mypy checks pass diff --git a/examples/src/keyring/aws_kms/custom_kms_client_config.py b/examples/src/keyring/aws_kms/custom_kms_client_config.py index dc622ad3a..2a8e7c759 100644 --- a/examples/src/keyring/aws_kms/custom_kms_client_config.py +++ b/examples/src/keyring/aws_kms/custom_kms_client_config.py @@ -1,10 +1,10 @@ # Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0 """ -By default, the KMS keyring will use the default configurations -for all KMS clients and will use the default discoverable credentials. -If you need to change these configurations, -you can do that using the client supplier. +By default, the KMS keyring uses the default configurations +for all KMS clients and uses the default discoverable credentials. +If you need to change this configuration, +you can configure the client supplier. This example shows how to use custom-configured clients with the KMS keyring. @@ -73,8 +73,8 @@ def run(aws_kms_cmk, source_plaintext): # 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. + # You do not need to specify the encryption context on decrypt + # because the header of the encrypted message includes the encryption context. decrypted, decrypt_header = aws_encryption_sdk.decrypt(source=ciphertext, keyring=keyring) # Demonstrate that the decrypted plaintext is identical to the original plaintext. diff --git a/examples/src/keyring/aws_kms/discovery_decrypt.py b/examples/src/keyring/aws_kms/discovery_decrypt.py index 92bb9db47..dd3c94e88 100644 --- a/examples/src/keyring/aws_kms/discovery_decrypt.py +++ b/examples/src/keyring/aws_kms/discovery_decrypt.py @@ -4,12 +4,12 @@ When you give the KMS keyring specific key IDs it will use those CMKs and nothing else. This is true both on encrypt and on decrypt. However, sometimes you need more flexibility on decrypt, -especially if you might not know beforehand which CMK was used to encrypt a message. +especially when you don't know which CMKs were used to encrypt a message. To address this need, you can use a KMS discovery keyring. -The KMS discovery keyring will do nothing on encrypt -but will attempt to decrypt *any* data keys that were encrypted under a KMS CMK. +The KMS discovery keyring does nothing on encrypt, +but attempts to decrypt *any* data keys that were encrypted under a KMS CMK. -This example shows how to configure and use a KMS keyring in discovery mode. +This example shows how to configure and use a KMS discovery keyring. https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/choose-keyring.html#use-kms-keyring @@ -20,7 +20,7 @@ see the ``keyring/aws_kms/custom_client_supplier`` and ``keyring/aws_kms/custom_kms_client_config`` examples. -For examples of how to use the KMS keyring in discovery mode on decrypt, +For examples of how to use the KMS discovery keyring on decrypt, see the ``keyring/aws_kms/discovery_decrypt_in_region_only`` and ``keyring/aws_kms/discovery_decrypt_with_preferred_region`` examples. """ @@ -30,7 +30,7 @@ def run(aws_kms_cmk, source_plaintext): # type: (str, bytes) -> None - """Demonstrate configuring a KMS keyring to use discovery mode for decryption. + """Demonstrate configuring a KMS discovery keyring for decryption. :param str aws_kms_cmk: The ARN of an AWS KMS CMK that protects data keys :param bytes source_plaintext: Plaintext to encrypt @@ -48,7 +48,7 @@ def run(aws_kms_cmk, source_plaintext): # Create the keyring that determines how your data keys are protected. encrypt_keyring = KmsKeyring(generator_key_id=aws_kms_cmk) - # Create the KMS discovery keyring that we will use on decrypt. + # Create a KMS discovery keyring to use on decrypt. # # Because we do not specify any key IDs, this keyring is created in discovery mode. decrypt_keyring = KmsKeyring() @@ -63,8 +63,8 @@ def run(aws_kms_cmk, source_plaintext): # Decrypt your encrypted data using the KMS discovery keyring. # - # We do not need to specify the encryption context on decrypt - # because the header message includes the encryption context. + # You do not need to specify the encryption context on decrypt + # because the header of the encrypted message includes the encryption context. decrypted, decrypt_header = aws_encryption_sdk.decrypt(source=ciphertext, keyring=decrypt_keyring) # Demonstrate that the decrypted plaintext is identical to the original plaintext. diff --git a/examples/src/keyring/aws_kms/discovery_decrypt_in_region_only.py b/examples/src/keyring/aws_kms/discovery_decrypt_in_region_only.py index 5ee8d7e9a..0338af1cf 100644 --- a/examples/src/keyring/aws_kms/discovery_decrypt_in_region_only.py +++ b/examples/src/keyring/aws_kms/discovery_decrypt_in_region_only.py @@ -4,13 +4,13 @@ When you give the KMS keyring specific key IDs it will use those CMKs and nothing else. This is true both on encrypt and on decrypt. However, sometimes you need more flexibility on decrypt, -especially if you might not know beforehand which CMK was used to encrypt a message. +especially if you don't know which CMK was used to encrypt a message. To address this need, you can use a KMS discovery keyring. -The KMS discovery keyring will do nothing on encrypt -but will attempt to decrypt *any* data keys that were encrypted under a KMS CMK. +The KMS discovery keyring does nothing on encrypt +but attempts to decrypt *any* data keys that were encrypted under a KMS CMK. However, sometimes you need to be a *bit* more restrictive than that. -To address this need, you can use a client supplier to restrict what regions a KMS keyring can talk to. +To address this need, you can use a client supplier that restricts the regions a KMS keyring can talk to. This example shows how to configure and use a KMS regional discovery keyring that is restricted to one region. From 739ed474ee85b7ea19fa89c20a301dd983f61f10 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Tue, 24 Mar 2020 21:59:29 -0700 Subject: [PATCH 32/35] docs: carrying over copyedits --- examples/src/file_streaming_defaults.py | 4 ++-- examples/src/in_memory_streaming_defaults.py | 4 ++-- .../src/keyring/aws_kms/custom_client_supplier.py | 4 ++-- .../aws_kms/discovery_decrypt_in_region_only.py | 10 +++++----- .../discovery_decrypt_with_preferred_regions.py | 12 ++++++------ examples/src/keyring/aws_kms/multiple_regions.py | 4 ++-- examples/src/keyring/aws_kms/single_cmk.py | 4 ++-- examples/src/keyring/multi/aws_kms_with_escrow.py | 4 ++-- examples/src/keyring/raw_aes/raw_aes.py | 4 ++-- examples/src/keyring/raw_rsa/private_key_only.py | 4 ++-- .../src/keyring/raw_rsa/private_key_only_from_pem.py | 4 ++-- .../keyring/raw_rsa/public_private_key_separate.py | 4 ++-- examples/src/onestep_defaults.py | 4 ++-- examples/src/onestep_unsigned.py | 6 +++--- 14 files changed, 36 insertions(+), 36 deletions(-) diff --git a/examples/src/file_streaming_defaults.py b/examples/src/file_streaming_defaults.py index b9200406f..84bba1d31 100644 --- a/examples/src/file_streaming_defaults.py +++ b/examples/src/file_streaming_defaults.py @@ -61,8 +61,8 @@ def run(aws_kms_cmk, source_plaintext_filename): with open(ciphertext_filename, "rb") as ciphertext, open(decrypted_filename, "wb") as decrypted: # 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. + # You do not need to specify the encryption context on decrypt + # because the header of the encrypted message 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 3daa0d22b..4e0033221 100644 --- a/examples/src/in_memory_streaming_defaults.py +++ b/examples/src/in_memory_streaming_defaults.py @@ -57,8 +57,8 @@ def run(aws_kms_cmk, source_plaintext): # 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. + # You do not need to specify the encryption context on decrypt + # because the header of the encrypted message includes the encryption context. decrypted = io.BytesIO() 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/keyring/aws_kms/custom_client_supplier.py b/examples/src/keyring/aws_kms/custom_client_supplier.py index 7efa901fe..10dbb6a8d 100644 --- a/examples/src/keyring/aws_kms/custom_client_supplier.py +++ b/examples/src/keyring/aws_kms/custom_client_supplier.py @@ -100,8 +100,8 @@ def run(aws_kms_cmk, source_plaintext): # 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. + # You do not need to specify the encryption context on decrypt + # because the header of the encrypted message includes the encryption context. decrypted, decrypt_header = aws_encryption_sdk.decrypt(source=ciphertext, keyring=keyring) # Demonstrate that the decrypted plaintext is identical to the original plaintext. diff --git a/examples/src/keyring/aws_kms/discovery_decrypt_in_region_only.py b/examples/src/keyring/aws_kms/discovery_decrypt_in_region_only.py index 0338af1cf..f7293fa63 100644 --- a/examples/src/keyring/aws_kms/discovery_decrypt_in_region_only.py +++ b/examples/src/keyring/aws_kms/discovery_decrypt_in_region_only.py @@ -4,9 +4,9 @@ When you give the KMS keyring specific key IDs it will use those CMKs and nothing else. This is true both on encrypt and on decrypt. However, sometimes you need more flexibility on decrypt, -especially if you don't know which CMK was used to encrypt a message. +especially when you don't know which CMKs were used to encrypt a message. To address this need, you can use a KMS discovery keyring. -The KMS discovery keyring does nothing on encrypt +The KMS discovery keyring does nothing on encrypt, but attempts to decrypt *any* data keys that were encrypted under a KMS CMK. However, sometimes you need to be a *bit* more restrictive than that. @@ -34,7 +34,7 @@ def run(aws_kms_cmk, source_plaintext): # type: (str, bytes) -> None - """Demonstrate configuring a KMS keyring to only work within a single region. + """Demonstrate configuring a KMS discovery keyring to only work within a single region. :param str aws_kms_cmk: The ARN of an AWS KMS CMK that protects data keys :param bytes source_plaintext: Plaintext to encrypt @@ -74,8 +74,8 @@ def run(aws_kms_cmk, source_plaintext): # Decrypt your encrypted data using the KMS discovery keyring. # - # We do not need to specify the encryption context on decrypt - # because the header message includes the encryption context. + # You do not need to specify the encryption context on decrypt + # because the header of the encrypted message includes the encryption context. decrypted, decrypt_header = aws_encryption_sdk.decrypt(source=ciphertext, keyring=decrypt_keyring) # Demonstrate that the decrypted plaintext is identical to the original plaintext. diff --git a/examples/src/keyring/aws_kms/discovery_decrypt_with_preferred_regions.py b/examples/src/keyring/aws_kms/discovery_decrypt_with_preferred_regions.py index a73046c8c..8f97aa153 100644 --- a/examples/src/keyring/aws_kms/discovery_decrypt_with_preferred_regions.py +++ b/examples/src/keyring/aws_kms/discovery_decrypt_with_preferred_regions.py @@ -4,10 +4,10 @@ When you give the KMS keyring specific key IDs it will use those CMKs and nothing else. This is true both on encrypt and on decrypt. However, sometimes you need more flexibility on decrypt, -especially if you might not know beforehand which CMK was used to encrypt a message. +especially when you don't know which CMKs were used to encrypt a message. To address this need, you can use a KMS discovery keyring. -The KMS discovery keyring will do nothing on encrypt -but will attempt to decrypt *any* data keys that were encrypted under a KMS CMK. +The KMS discovery keyring does nothing on encrypt, +but attempts to decrypt *any* data keys that were encrypted under a KMS CMK. However, sometimes you need to be a *bit* more restrictive than that. To address this need, you can use a client supplier to restrict what regions a KMS keyring can talk to. @@ -43,7 +43,7 @@ def run(aws_kms_cmk, source_plaintext): # type: (str, bytes) -> None - """Demonstrate configuring a keyring prefer a particular AWS region and failover to others. + """Demonstrate configuring a KMS discovery-like keyring a particular AWS region and failover to others. :param str aws_kms_cmk: The ARN of an AWS KMS CMK that protects data keys :param bytes source_plaintext: Plaintext to encrypt @@ -93,8 +93,8 @@ def run(aws_kms_cmk, source_plaintext): # Decrypt your encrypted data using the multi-keyring. # - # We do not need to specify the encryption context on decrypt - # because the header message includes the encryption context. + # You do not need to specify the encryption context on decrypt + # because the header of the encrypted message includes the encryption context. decrypted, decrypt_header = aws_encryption_sdk.decrypt(source=ciphertext, keyring=decrypt_keyring) # Demonstrate that the decrypted plaintext is identical to the original plaintext. diff --git a/examples/src/keyring/aws_kms/multiple_regions.py b/examples/src/keyring/aws_kms/multiple_regions.py index 3aa6c812d..f466796c3 100644 --- a/examples/src/keyring/aws_kms/multiple_regions.py +++ b/examples/src/keyring/aws_kms/multiple_regions.py @@ -71,8 +71,8 @@ def run(aws_kms_generator_cmk, aws_kms_additional_cmks, source_plaintext): # Decrypt your encrypted data separately using the single-CMK keyrings. # - # We do not need to specify the encryption context on decrypt - # because the header message includes the encryption context. + # You do not need to specify the encryption context on decrypt + # because the header of the encrypted message includes the encryption context. decrypted_1, decrypt_header_1 = aws_encryption_sdk.decrypt( source=ciphertext, keyring=single_cmk_keyring_that_generated ) diff --git a/examples/src/keyring/aws_kms/single_cmk.py b/examples/src/keyring/aws_kms/single_cmk.py index ec55578ed..b186f5056 100644 --- a/examples/src/keyring/aws_kms/single_cmk.py +++ b/examples/src/keyring/aws_kms/single_cmk.py @@ -51,8 +51,8 @@ def run(aws_kms_cmk, source_plaintext): # 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. + # You do not need to specify the encryption context on decrypt + # because the header of the encrypted message includes the encryption context. decrypted, decrypt_header = aws_encryption_sdk.decrypt(source=ciphertext, keyring=keyring) # Demonstrate that the decrypted plaintext is identical to the original plaintext. diff --git a/examples/src/keyring/multi/aws_kms_with_escrow.py b/examples/src/keyring/multi/aws_kms_with_escrow.py index 70e5bef8f..dac35b9d9 100644 --- a/examples/src/keyring/multi/aws_kms_with_escrow.py +++ b/examples/src/keyring/multi/aws_kms_with_escrow.py @@ -108,8 +108,8 @@ def run(aws_kms_cmk, source_plaintext): # Decrypt your encrypted data separately using the KMS keyring and the escrow decrypt keyring. # - # We do not need to specify the encryption context on decrypt - # because the header message includes the encryption context. + # You do not need to specify the encryption context on decrypt + # because the header of the encrypted message includes the encryption context. decrypted_kms, decrypt_header_kms = aws_encryption_sdk.decrypt(source=ciphertext, keyring=kms_keyring) decrypted_escrow, decrypt_header_escrow = aws_encryption_sdk.decrypt( source=ciphertext, keyring=escrow_decrypt_keyring diff --git a/examples/src/keyring/raw_aes/raw_aes.py b/examples/src/keyring/raw_aes/raw_aes.py index 66f6dcb96..58aa697c9 100644 --- a/examples/src/keyring/raw_aes/raw_aes.py +++ b/examples/src/keyring/raw_aes/raw_aes.py @@ -63,8 +63,8 @@ def run(source_plaintext): # 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. + # You do not need to specify the encryption context on decrypt + # because the header of the encrypted message includes the encryption context. decrypted, decrypt_header = aws_encryption_sdk.decrypt(source=ciphertext, keyring=keyring) # Demonstrate that the decrypted plaintext is identical to the original plaintext. diff --git a/examples/src/keyring/raw_rsa/private_key_only.py b/examples/src/keyring/raw_rsa/private_key_only.py index 0a7d11d01..db7705735 100644 --- a/examples/src/keyring/raw_rsa/private_key_only.py +++ b/examples/src/keyring/raw_rsa/private_key_only.py @@ -73,8 +73,8 @@ def run(source_plaintext): # 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. + # You do not need to specify the encryption context on decrypt + # because the header of the encrypted message includes the encryption context. decrypted, decrypt_header = aws_encryption_sdk.decrypt(source=ciphertext, keyring=keyring) # Demonstrate that the decrypted plaintext is identical to the original plaintext. diff --git a/examples/src/keyring/raw_rsa/private_key_only_from_pem.py b/examples/src/keyring/raw_rsa/private_key_only_from_pem.py index 4f6b03155..397a79274 100644 --- a/examples/src/keyring/raw_rsa/private_key_only_from_pem.py +++ b/examples/src/keyring/raw_rsa/private_key_only_from_pem.py @@ -86,8 +86,8 @@ def run(source_plaintext): # 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. + # You do not need to specify the encryption context on decrypt + # because the header of the encrypted message includes the encryption context. decrypted, decrypt_header = aws_encryption_sdk.decrypt(source=ciphertext, keyring=keyring) # Demonstrate that the decrypted plaintext is identical to the original plaintext. diff --git a/examples/src/keyring/raw_rsa/public_private_key_separate.py b/examples/src/keyring/raw_rsa/public_private_key_separate.py index 1dd28bdba..b86b86ece 100644 --- a/examples/src/keyring/raw_rsa/public_private_key_separate.py +++ b/examples/src/keyring/raw_rsa/public_private_key_separate.py @@ -110,8 +110,8 @@ def run(source_plaintext): # Decrypt your encrypted data using the decrypt keyring. # - # We do not need to specify the encryption context on decrypt - # because the header message includes the encryption context. + # You do not need to specify the encryption context on decrypt + # because the header of the encrypted message includes the encryption context. decrypted, decrypt_header = aws_encryption_sdk.decrypt(source=ciphertext, keyring=private_key_keyring) # Demonstrate that the decrypted plaintext is identical to the original plaintext. diff --git a/examples/src/onestep_defaults.py b/examples/src/onestep_defaults.py index 5f7bbbc8e..e1d0161bf 100644 --- a/examples/src/onestep_defaults.py +++ b/examples/src/onestep_defaults.py @@ -42,8 +42,8 @@ def run(aws_kms_cmk, source_plaintext): # 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. + # You do not need to specify the encryption context on decrypt + # because the header of the encrypted message includes the encryption context. decrypted, decrypt_header = aws_encryption_sdk.decrypt(source=ciphertext, keyring=keyring) # Demonstrate that the decrypted plaintext is identical to the original plaintext. diff --git a/examples/src/onestep_unsigned.py b/examples/src/onestep_unsigned.py index 73eab5931..e411b00e4 100644 --- a/examples/src/onestep_unsigned.py +++ b/examples/src/onestep_unsigned.py @@ -59,10 +59,10 @@ def run(aws_kms_cmk, source_plaintext): # 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. + # You do not need to specify the encryption context on decrypt + # because the header of the encrypted message includes the encryption context. # - # We do not need to specify the algorithm suite on decrypt + # You 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) From 8b28bee9c305ca1a3c98c3cda28e8a7db9955350 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Wed, 25 Mar 2020 19:42:51 -0700 Subject: [PATCH 33/35] feat: update KMS discovery keyring examples to use new discovery configuration --- examples/src/keyring/aws_kms/discovery_decrypt.py | 4 +--- .../src/keyring/aws_kms/discovery_decrypt_in_region_only.py | 6 +++--- .../aws_kms/discovery_decrypt_with_preferred_regions.py | 6 ++++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/src/keyring/aws_kms/discovery_decrypt.py b/examples/src/keyring/aws_kms/discovery_decrypt.py index dd3c94e88..85f0e9c2c 100644 --- a/examples/src/keyring/aws_kms/discovery_decrypt.py +++ b/examples/src/keyring/aws_kms/discovery_decrypt.py @@ -49,9 +49,7 @@ def run(aws_kms_cmk, source_plaintext): encrypt_keyring = KmsKeyring(generator_key_id=aws_kms_cmk) # Create a KMS discovery keyring to use on decrypt. - # - # Because we do not specify any key IDs, this keyring is created in discovery mode. - decrypt_keyring = KmsKeyring() + decrypt_keyring = KmsKeyring(is_discovery=True) # Encrypt your plaintext data. ciphertext, _encrypt_header = aws_encryption_sdk.encrypt( diff --git a/examples/src/keyring/aws_kms/discovery_decrypt_in_region_only.py b/examples/src/keyring/aws_kms/discovery_decrypt_in_region_only.py index f7293fa63..326f512ec 100644 --- a/examples/src/keyring/aws_kms/discovery_decrypt_in_region_only.py +++ b/examples/src/keyring/aws_kms/discovery_decrypt_in_region_only.py @@ -57,12 +57,12 @@ def run(aws_kms_cmk, source_plaintext): # Create the KMS discovery keyring that we will use on decrypt. # - # Because we do not specify any key IDs, this keyring is created in discovery mode. - # # The client supplier that we specify here will only supply clients for the specified region. # The keyring only attempts to decrypt data keys if it can get a client for that region, # so this keyring will now ignore any data keys that were encrypted under a CMK in another region. - decrypt_keyring = KmsKeyring(client_supplier=AllowRegionsClientSupplier(allowed_regions=[decrypt_region])) + decrypt_keyring = KmsKeyring( + is_discovery=True, client_supplier=AllowRegionsClientSupplier(allowed_regions=[decrypt_region]) + ) # Encrypt your plaintext data. ciphertext, _encrypt_header = aws_encryption_sdk.encrypt( diff --git a/examples/src/keyring/aws_kms/discovery_decrypt_with_preferred_regions.py b/examples/src/keyring/aws_kms/discovery_decrypt_with_preferred_regions.py index 8f97aa153..ebe8a4953 100644 --- a/examples/src/keyring/aws_kms/discovery_decrypt_with_preferred_regions.py +++ b/examples/src/keyring/aws_kms/discovery_decrypt_with_preferred_regions.py @@ -70,10 +70,12 @@ def run(aws_kms_cmk, source_plaintext): # # One that only works in the local region local_region_decrypt_keyring = KmsKeyring( - client_supplier=AllowRegionsClientSupplier(allowed_regions=[local_region]) + is_discovery=True, client_supplier=AllowRegionsClientSupplier(allowed_regions=[local_region]) ) # and one that will work in any other region but NOT the local region. - other_regions_decrypt_keyring = KmsKeyring(client_supplier=DenyRegionsClientSupplier(denied_regions=[local_region])) + other_regions_decrypt_keyring = KmsKeyring( + is_discovery=True, client_supplier=DenyRegionsClientSupplier(denied_regions=[local_region]) + ) # Finally, combine those two keyrings into a multi-keyring. # From c4aeaf73341a1f8c88216038852237864ce696f1 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Mon, 30 Mar 2020 18:52:03 -0700 Subject: [PATCH 34/35] docs: fix typo --- examples/src/in_memory_streaming_defaults.py | 2 +- examples/src/onestep_defaults.py | 2 +- examples/src/onestep_unsigned.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/src/in_memory_streaming_defaults.py b/examples/src/in_memory_streaming_defaults.py index 4e0033221..984848bce 100644 --- a/examples/src/in_memory_streaming_defaults.py +++ b/examples/src/in_memory_streaming_defaults.py @@ -9,7 +9,7 @@ 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. +see the ``keyring`` and ``master_key_provider`` directories. """ import io diff --git a/examples/src/onestep_defaults.py b/examples/src/onestep_defaults.py index e1d0161bf..3ded2b3ae 100644 --- a/examples/src/onestep_defaults.py +++ b/examples/src/onestep_defaults.py @@ -6,7 +6,7 @@ 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. +see the ``keyring`` and ``master_key_provider`` directories. """ import aws_encryption_sdk from aws_encryption_sdk.keyrings.aws_kms import KmsKeyring diff --git a/examples/src/onestep_unsigned.py b/examples/src/onestep_unsigned.py index e411b00e4..72623087b 100644 --- a/examples/src/onestep_unsigned.py +++ b/examples/src/onestep_unsigned.py @@ -7,7 +7,7 @@ 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. +see the ``keyring`` and ``master_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 From dd6356438f9c091a2a9bd0941f79c4a5d089288a Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Mon, 30 Mar 2020 19:01:47 -0700 Subject: [PATCH 35/35] docs: remove trailing whitespace --- examples/src/keyring/aws_kms/custom_client_supplier.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/src/keyring/aws_kms/custom_client_supplier.py b/examples/src/keyring/aws_kms/custom_client_supplier.py index 10dbb6a8d..159f8a2ab 100644 --- a/examples/src/keyring/aws_kms/custom_client_supplier.py +++ b/examples/src/keyring/aws_kms/custom_client_supplier.py @@ -5,7 +5,7 @@ supplies a client with the same configuration for every region. If you need different behavior, you can write your own client supplier. -You might use this +You might use this if you need different credentials in different AWS regions. This might be because you are crossing partitions (ex: ``aws`` and ``aws-cn``) or if you are working with regions that have separate authentication silos