From cbfe9b0fc9a1048b5779fdf48c677d734d65690b Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Wed, 1 Apr 2020 18:51:33 -0700 Subject: [PATCH 1/7] docs: add raw AES MKP example --- examples/src/keyring/raw_aes/raw_aes.py | 2 +- .../master_key_provider/raw_aes/__init__.py | 7 ++ .../master_key_provider/raw_aes/raw_aes.py | 81 +++++++++++++++++++ 3 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 examples/src/master_key_provider/raw_aes/__init__.py create mode 100644 examples/src/master_key_provider/raw_aes/raw_aes.py diff --git a/examples/src/keyring/raw_aes/raw_aes.py b/examples/src/keyring/raw_aes/raw_aes.py index 58aa697c9..c688141f2 100644 --- a/examples/src/keyring/raw_aes/raw_aes.py +++ b/examples/src/keyring/raw_aes/raw_aes.py @@ -42,7 +42,7 @@ def run(source_plaintext): # 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 + # and are used by the raw AES keyring # to determine whether it should attempt to decrypt # an encrypted data key. # diff --git a/examples/src/master_key_provider/raw_aes/__init__.py b/examples/src/master_key_provider/raw_aes/__init__.py new file mode 100644 index 000000000..5572015a7 --- /dev/null +++ b/examples/src/master_key_provider/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 master key provider examples. + +These examples show how to use the raw AES master key. +""" diff --git a/examples/src/master_key_provider/raw_aes/raw_aes.py b/examples/src/master_key_provider/raw_aes/raw_aes.py new file mode 100644 index 000000000..035278120 --- /dev/null +++ b/examples/src/master_key_provider/raw_aes/raw_aes.py @@ -0,0 +1,81 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +This example is provided as a reference for users migrating away from master key providers. +We recommend that all new use should use keyrings. +For examples using keyrings, see the ``examples/src/keyrings`` directory. + +This examples shows how to configure and use a raw AES master key. + +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#master-key-provider + +In this example, we use the one-step encrypt and decrypt APIs. +""" +import os + +import aws_encryption_sdk +from aws_encryption_sdk.identifiers import EncryptionKeyType, WrappingAlgorithm +from aws_encryption_sdk.key_providers.raw import RawMasterKey, WrappingKey + + +def run(source_plaintext): + # type: (bytes) -> None + """Demonstrate an encrypt/decrypt cycle using a raw AES master 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", + } + + # Choose the wrapping algorithm for your master key to use. + wrapping_algorithm = WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING + + # Generate an AES key to use with your master key. + # 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 master key that determines how your data keys are protected. + master_key = RawMasterKey( + # The provider ID and key ID are defined by you + # and are used by the raw AES master key + # to determine whether it should attempt to decrypt + # an encrypted data key. + provider_id="some managed raw keys", # provider ID corresponds to key namespace for keyrings + key_id=b"my AES wrapping key", # key ID corresponds to key name for keyrings + wrapping_key=WrappingKey( + wrapping_algorithm=wrapping_algorithm, wrapping_key_type=EncryptionKeyType.SYMMETRIC, wrapping_key=key, + ), + ) + + # Encrypt your plaintext data. + ciphertext, _encrypt_header = aws_encryption_sdk.encrypt( + source=source_plaintext, encryption_context=encryption_context, key_provider=master_key + ) + + # Demonstrate that the ciphertext and plaintext are different. + assert ciphertext != source_plaintext + + # Decrypt your encrypted data using the same master key you used on encrypt. + # + # 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, key_provider=master_key) + + # 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 + # 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 97c60baf8377486609c12ae32b7d6bd3062e626b Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Wed, 1 Apr 2020 18:51:55 -0700 Subject: [PATCH 2/7] docs: add raw RSA MKP example --- .../master_key_provider/raw_rsa/__init__.py | 7 ++ .../raw_rsa/private_key_only_from_pem.py | 104 ++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 examples/src/master_key_provider/raw_rsa/__init__.py create mode 100644 examples/src/master_key_provider/raw_rsa/private_key_only_from_pem.py diff --git a/examples/src/master_key_provider/raw_rsa/__init__.py b/examples/src/master_key_provider/raw_rsa/__init__.py new file mode 100644 index 000000000..374a606fb --- /dev/null +++ b/examples/src/master_key_provider/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 master key provider examples. + +These examples show how to use the raw RSA master key. +""" diff --git a/examples/src/master_key_provider/raw_rsa/private_key_only_from_pem.py b/examples/src/master_key_provider/raw_rsa/private_key_only_from_pem.py new file mode 100644 index 000000000..a4c6eaf3d --- /dev/null +++ b/examples/src/master_key_provider/raw_rsa/private_key_only_from_pem.py @@ -0,0 +1,104 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +This example is provided as a reference for users migrating away from master key providers. +We recommend that all new use should use keyrings. +For examples using keyrings, see the ``examples/src/keyrings`` directory. + +This example shows how to configure and use a raw RSA master key using a PEM-encoded RSA private key. + +The most commonly used encodings for RSA keys tend to be PEM and DER. +The raw RSA master key supports loading both public and private keys from PEM encoding. + +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#master-key-provider + +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 EncryptionKeyType, WrappingAlgorithm +from aws_encryption_sdk.key_providers.raw import RawMasterKey, WrappingKey + + +def run(source_plaintext): + # type: (bytes) -> None + """Demonstrate an encrypt/decrypt cycle using a raw RSA master key 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 master key. + # 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()) + + # Serialize the RSA private key to PEM encoding. + # 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, + encryption_algorithm=serialization.NoEncryption(), + ) + + # Create the master key that determines how your data keys are protected. + # + # WrappingKey can only load PEM-encoded keys. + master_key = RawMasterKey( + # The provider ID and key ID are defined by you + # and are used by the raw RSA master key + # to determine whether it should attempt to decrypt + # an encrypted data key. + provider_id="some managed raw keys", # provider ID corresponds to key namespace for keyrings + key_id=b"my RSA wrapping key", # key ID corresponds to key name for keyrings + wrapping_key=WrappingKey( + wrapping_key=private_key_pem, + wrapping_key_type=EncryptionKeyType.PRIVATE, + # The wrapping algorithm tells the raw RSA master key + # how to use your wrapping key to encrypt data keys. + # + # 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, + ), + ) + + # Encrypt your plaintext data. + ciphertext, _encrypt_header = aws_encryption_sdk.encrypt( + source=source_plaintext, encryption_context=encryption_context, key_provider=master_key + ) + + # Demonstrate that the ciphertext and plaintext are different. + assert ciphertext != source_plaintext + + # Decrypt your encrypted data using the same master key you used on encrypt. + # + # 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, key_provider=master_key) + + # 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 + # 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 d6b86c79f7a956f8a4adcb9a4558e31bba91a434 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Wed, 1 Apr 2020 18:52:15 -0700 Subject: [PATCH 3/7] docs: add KMS MK/P examples --- .../master_key_provider/aws_kms/__init__.py | 7 ++ .../aws_kms/discovery_decrypt.py | 68 ++++++++++++++ .../aws_kms/multiple_regions.py | 91 +++++++++++++++++++ .../master_key_provider/aws_kms/single_cmk.py | 64 +++++++++++++ 4 files changed, 230 insertions(+) create mode 100644 examples/src/master_key_provider/aws_kms/__init__.py create mode 100644 examples/src/master_key_provider/aws_kms/discovery_decrypt.py create mode 100644 examples/src/master_key_provider/aws_kms/multiple_regions.py create mode 100644 examples/src/master_key_provider/aws_kms/single_cmk.py diff --git a/examples/src/master_key_provider/aws_kms/__init__.py b/examples/src/master_key_provider/aws_kms/__init__.py new file mode 100644 index 000000000..5da30d818 --- /dev/null +++ b/examples/src/master_key_provider/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 master key provider examples. + +These examples show how to use the KMS master key provider. +""" diff --git a/examples/src/master_key_provider/aws_kms/discovery_decrypt.py b/examples/src/master_key_provider/aws_kms/discovery_decrypt.py new file mode 100644 index 000000000..77c1a65c6 --- /dev/null +++ b/examples/src/master_key_provider/aws_kms/discovery_decrypt.py @@ -0,0 +1,68 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +This example is provided as a reference for users migrating away from master key providers. +We recommend that all new use should use keyrings. +For examples using keyrings, see the ``examples/src/keyrings`` directory. + +The KMS master key provider uses any key IDs that you specify on encrypt, +but attempts to decrypt *any* data keys that were encrypted under a KMS CMK. +This means that you do not need to know which CMKs were used to encrypt a message. + +This example shows how to configure and use a KMS master key provider to decrypt without provider key IDs. + +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#master-key-provider + +For an example of how to use the KMS master key provider with CMKs in multiple regions, +see the ``master_key_provider/aws_kms/multiple_regions`` example. +""" +import aws_encryption_sdk +from aws_encryption_sdk.key_providers.kms import KMSMasterKey, KMSMasterKeyProvider + + +def run(aws_kms_cmk, source_plaintext): + # type: (str, bytes) -> None + """Demonstrate configuring a KMS master key provider 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 + """ + # 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 master key that determines how your data keys are protected. + encrypt_master_key = KMSMasterKey(key_id=aws_kms_cmk) + + # Create a KMS master key provider to use on decrypt. + decrypt_master_key_provider = KMSMasterKeyProvider() + + # Encrypt your plaintext data. + ciphertext, _encrypt_header = aws_encryption_sdk.encrypt( + source=source_plaintext, encryption_context=encryption_context, key_provider=encrypt_master_key + ) + + # Demonstrate that the ciphertext and plaintext are different. + assert ciphertext != source_plaintext + + # Decrypt your encrypted data using the KMS master key provider. + # + # 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, key_provider=decrypt_master_key_provider) + + # 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 + # 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/master_key_provider/aws_kms/multiple_regions.py b/examples/src/master_key_provider/aws_kms/multiple_regions.py new file mode 100644 index 000000000..476975c57 --- /dev/null +++ b/examples/src/master_key_provider/aws_kms/multiple_regions.py @@ -0,0 +1,91 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +This example is provided as a reference for users migrating away from master key providers. +We recommend that all new use should use keyrings. +For examples using keyrings, see the ``examples/src/keyrings`` directory. + +This example shows how to configure and use a KMS master key provider with with CMKs in multiple regions. + +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#master-key-provider + +For an example of how to use the KMS master key with a single CMK, +see the ``master_key_provider/aws_kms/single_cmk`` example. + +For examples of how to use the KMS master key provider in discovery mode on decrypt, +see the ``master_key_provider/aws_kms/discovery_decrypt``. +""" +import aws_encryption_sdk +from aws_encryption_sdk.key_providers.kms import KMSMasterKey, KMSMasterKeyProvider + +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 + + +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 master key provider with CMKs in multiple regions. + + :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", + } + + # Create the master key provider that will encrypt your data keys under all requested CMKs. + # + # The KMS master key provider generates the data key using the first key ID in the list. + key_ids = [aws_kms_generator_cmk] + key_ids.extend(aws_kms_additional_cmks) + master_key_provider = KMSMasterKeyProvider(key_ids=key_ids) + + # Create master keys 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. + single_cmk_master_key_that_generated = KMSMasterKey(key_id=aws_kms_generator_cmk) + single_cmk_master_key_that_encrypted = KMSMasterKey(key_id=aws_kms_additional_cmks[0]) + + # Encrypt your plaintext data using the master key provider that uses all requests CMKs. + ciphertext, encrypt_header = aws_encryption_sdk.encrypt( + source=source_plaintext, encryption_context=encryption_context, key_provider=master_key_provider + ) + + # 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 + + # Demonstrate that the ciphertext and plaintext are different. + assert ciphertext != source_plaintext + + # Decrypt your encrypted data separately using the single-CMK master keys. + # + # 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, key_provider=single_cmk_master_key_that_generated + ) + decrypted_2, decrypt_header_2 = aws_encryption_sdk.decrypt( + source=ciphertext, key_provider=single_cmk_master_key_that_encrypted + ) + + # Demonstrate 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/master_key_provider/aws_kms/single_cmk.py b/examples/src/master_key_provider/aws_kms/single_cmk.py new file mode 100644 index 000000000..4ddfd69c1 --- /dev/null +++ b/examples/src/master_key_provider/aws_kms/single_cmk.py @@ -0,0 +1,64 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +This example is provided as a reference for users migrating away from master key providers. +We recommend that all new use should use keyrings. +For examples using keyrings, see the ``examples/src/keyrings`` directory. + +This example shows how to configure and use a KMS master key with a single KMS CMK. + +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#master-key-provider + +For an example of how to use the KMS master key provider with CMKs in multiple regions, +see the ``master_key_provider/aws_kms/multiple_regions`` example. + +For examples of how to use the KMS master key provider in discovery mode on decrypt, +see the ``master_key_provider/aws_kms/discovery_decrypt``. +""" +import aws_encryption_sdk +from aws_encryption_sdk.key_providers.kms import KMSMasterKey + + +def run(aws_kms_cmk, source_plaintext): + # type: (str, bytes) -> None + """Demonstrate an encrypt/decrypt cycle using a KMS master key 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 master key that determines how your data keys are protected. + master_key = KMSMasterKey(key_id=aws_kms_cmk) + + # Encrypt your plaintext data. + ciphertext, _encrypt_header = aws_encryption_sdk.encrypt( + source=source_plaintext, encryption_context=encryption_context, key_provider=master_key + ) + + # Demonstrate that the ciphertext and plaintext are different. + assert ciphertext != source_plaintext + + # Decrypt your encrypted data using the same master key you used on encrypt. + # + # 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, key_provider=master_key) + + # 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 + # 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 1a01d325bd9e9dddeae691d2bf3b81099710a0ec Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Wed, 1 Apr 2020 18:52:30 -0700 Subject: [PATCH 4/7] docs: add MKP combination example --- examples/src/master_key_provider/__init__.py | 7 + .../src/master_key_provider/multi/__init__.py | 7 + .../multi/aws_kms_with_escrow.py | 150 ++++++++++++++++++ 3 files changed, 164 insertions(+) create mode 100644 examples/src/master_key_provider/__init__.py create mode 100644 examples/src/master_key_provider/multi/__init__.py create mode 100644 examples/src/master_key_provider/multi/aws_kms_with_escrow.py diff --git a/examples/src/master_key_provider/__init__.py b/examples/src/master_key_provider/__init__.py new file mode 100644 index 000000000..0edf699c5 --- /dev/null +++ b/examples/src/master_key_provider/__init__.py @@ -0,0 +1,7 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +Master key provider examples. + +These examples show how to use master key providers. +""" diff --git a/examples/src/master_key_provider/multi/__init__.py b/examples/src/master_key_provider/multi/__init__.py new file mode 100644 index 000000000..22f5195fc --- /dev/null +++ b/examples/src/master_key_provider/multi/__init__.py @@ -0,0 +1,7 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +Multi-master key provider examples. + +These examples show how to combine master key providers. +""" diff --git a/examples/src/master_key_provider/multi/aws_kms_with_escrow.py b/examples/src/master_key_provider/multi/aws_kms_with_escrow.py new file mode 100644 index 000000000..73fb626d8 --- /dev/null +++ b/examples/src/master_key_provider/multi/aws_kms_with_escrow.py @@ -0,0 +1,150 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +This example is provided as a reference for users migrating away from master key providers. +We recommend that all new use should use keyrings. +For examples using keyrings, see the ``examples/src/keyrings`` directory. + +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 without access to AWS KMS. +This example shows how you can achieve this +by combining a KMS master key with a raw RSA master key. + +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#master-key-provider + +For more examples of how to use the KMS master key provider, see the ``master_key_provider/aws_kms`` examples. + +For more examples of how to use the raw RSA master key, see the ``master_key_provider/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. +""" +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 EncryptionKeyType, WrappingAlgorithm +from aws_encryption_sdk.key_providers.kms import KMSMasterKeyProvider +from aws_encryption_sdk.key_providers.raw import RawMasterKey, WrappingKey + + +def run(aws_kms_cmk, source_plaintext): + # type: (str, bytes) -> None + """Demonstrate configuring a master key provider 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 + """ + # 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 master key. + # 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()) + + # Serialize the RSA private key to PEM encoding. + # 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, + encryption_algorithm=serialization.NoEncryption(), + ) + + # Collect the public key from the private key. + public_key = private_key.public_key() + + # Serialize the RSA public key to PEM encoding. + # This or DER encoding is likely to be what you get from your key management system in practice. + public_key_pem = public_key.public_bytes( + encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo, + ) + + # Create the encrypt master key that only has access to the public key. + escrow_encrypt_master_key = RawMasterKey( + # The provider ID and key ID are defined by you + # and are used by the raw RSA master key + # to determine whether it should attempt to decrypt + # an encrypted data key. + provider_id="some managed raw keys", # provider ID corresponds to key namespace for keyrings + key_id=b"my RSA wrapping key", # key ID corresponds to key name for keyrings + wrapping_key=WrappingKey( + wrapping_key=public_key_pem, + wrapping_key_type=EncryptionKeyType.PUBLIC, + # The wrapping algorithm tells the raw RSA master key + # how to use your wrapping key to encrypt data keys. + # + # 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, + ), + ) + + # Create the decrypt master key that has access to the private key. + escrow_decrypt_master_key = RawMasterKey( + # The key namespace and key name MUST match the encrypt master key. + provider_id="some managed raw keys", # provider ID corresponds to key namespace for keyrings + key_id=b"my RSA wrapping key", # key ID corresponds to key name for keyrings + wrapping_key=WrappingKey( + wrapping_key=private_key_pem, + wrapping_key_type=EncryptionKeyType.PRIVATE, + # The wrapping algorithm MUST match the encrypt master key. + wrapping_algorithm=WrappingAlgorithm.RSA_OAEP_SHA256_MGF1, + ), + ) + + # Create the KMS master key that you will use for decryption during normal operations. + kms_master_key = KMSMasterKeyProvider(key_ids=[aws_kms_cmk]) + + # Add the escrow encrypt master key to the KMS master key. + kms_master_key.add_master_key_provider(escrow_encrypt_master_key) + + # Encrypt your plaintext data using the combined master keys. + ciphertext, encrypt_header = aws_encryption_sdk.encrypt( + source=source_plaintext, encryption_context=encryption_context, key_provider=kms_master_key + ) + + # 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 + + # Demonstrate that the ciphertext and plaintext are different. + assert ciphertext != source_plaintext + + # Decrypt your encrypted data separately using the KMS master key and the escrow decrypt master key. + # + # 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, key_provider=kms_master_key) + decrypted_escrow, decrypt_header_escrow = aws_encryption_sdk.decrypt( + source=ciphertext, key_provider=escrow_decrypt_master_key + ) + + # Demonstrate 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 8fb27416414e0e006a98c48a8c414a215fde941a Mon Sep 17 00:00:00 2001 From: Matt Bullock Date: Thu, 2 Apr 2020 11:21:21 -0700 Subject: [PATCH 5/7] docs: apply suggestions from code review Co-Authored-By: Wesley Rosenblum <55108558+WesleyRosenblum@users.noreply.github.com> --- examples/src/master_key_provider/aws_kms/discovery_decrypt.py | 3 +++ examples/src/master_key_provider/aws_kms/multiple_regions.py | 4 ++-- examples/src/master_key_provider/aws_kms/single_cmk.py | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/examples/src/master_key_provider/aws_kms/discovery_decrypt.py b/examples/src/master_key_provider/aws_kms/discovery_decrypt.py index 77c1a65c6..9ffc291e7 100644 --- a/examples/src/master_key_provider/aws_kms/discovery_decrypt.py +++ b/examples/src/master_key_provider/aws_kms/discovery_decrypt.py @@ -13,6 +13,9 @@ https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#master-key-provider +For an example of how to use the KMS master key with a single CMK, +see the ``master_key_provider/aws_kms/single_cmk`` example. + For an example of how to use the KMS master key provider with CMKs in multiple regions, see the ``master_key_provider/aws_kms/multiple_regions`` example. """ diff --git a/examples/src/master_key_provider/aws_kms/multiple_regions.py b/examples/src/master_key_provider/aws_kms/multiple_regions.py index 476975c57..ae9b34b77 100644 --- a/examples/src/master_key_provider/aws_kms/multiple_regions.py +++ b/examples/src/master_key_provider/aws_kms/multiple_regions.py @@ -12,8 +12,8 @@ For an example of how to use the KMS master key with a single CMK, see the ``master_key_provider/aws_kms/single_cmk`` example. -For examples of how to use the KMS master key provider in discovery mode on decrypt, -see the ``master_key_provider/aws_kms/discovery_decrypt``. +For an example of how to use the KMS master key provider in discovery mode on decrypt, +see the ``master_key_provider/aws_kms/discovery_decrypt`` example. """ import aws_encryption_sdk from aws_encryption_sdk.key_providers.kms import KMSMasterKey, KMSMasterKeyProvider diff --git a/examples/src/master_key_provider/aws_kms/single_cmk.py b/examples/src/master_key_provider/aws_kms/single_cmk.py index 4ddfd69c1..65d4a2ae7 100644 --- a/examples/src/master_key_provider/aws_kms/single_cmk.py +++ b/examples/src/master_key_provider/aws_kms/single_cmk.py @@ -12,8 +12,8 @@ For an example of how to use the KMS master key provider with CMKs in multiple regions, see the ``master_key_provider/aws_kms/multiple_regions`` example. -For examples of how to use the KMS master key provider in discovery mode on decrypt, -see the ``master_key_provider/aws_kms/discovery_decrypt``. +For an example of how to use the KMS master key provider in discovery mode on decrypt, +see the ``master_key_provider/aws_kms/discovery_decrypt`` example. """ import aws_encryption_sdk from aws_encryption_sdk.key_providers.kms import KMSMasterKey From 0de7cdcc52088a543a1604a77e1e850dba7b4037 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Thu, 2 Apr 2020 18:38:28 -0700 Subject: [PATCH 6/7] docs: revise MKP examples intro --- examples/src/master_key_provider/aws_kms/discovery_decrypt.py | 4 ++-- examples/src/master_key_provider/aws_kms/multiple_regions.py | 4 ++-- examples/src/master_key_provider/aws_kms/single_cmk.py | 4 ++-- examples/src/master_key_provider/multi/aws_kms_with_escrow.py | 4 ++-- examples/src/master_key_provider/raw_aes/raw_aes.py | 4 ++-- .../master_key_provider/raw_rsa/private_key_only_from_pem.py | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/examples/src/master_key_provider/aws_kms/discovery_decrypt.py b/examples/src/master_key_provider/aws_kms/discovery_decrypt.py index 9ffc291e7..1b7e09452 100644 --- a/examples/src/master_key_provider/aws_kms/discovery_decrypt.py +++ b/examples/src/master_key_provider/aws_kms/discovery_decrypt.py @@ -1,8 +1,8 @@ # Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0 """ -This example is provided as a reference for users migrating away from master key providers. -We recommend that all new use should use keyrings. +This example is intended to serve as reference material for users migrating away from master key providers. +We recommend using keyrings rather than master key providers. For examples using keyrings, see the ``examples/src/keyrings`` directory. The KMS master key provider uses any key IDs that you specify on encrypt, diff --git a/examples/src/master_key_provider/aws_kms/multiple_regions.py b/examples/src/master_key_provider/aws_kms/multiple_regions.py index ae9b34b77..81fee9ec5 100644 --- a/examples/src/master_key_provider/aws_kms/multiple_regions.py +++ b/examples/src/master_key_provider/aws_kms/multiple_regions.py @@ -1,8 +1,8 @@ # Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0 """ -This example is provided as a reference for users migrating away from master key providers. -We recommend that all new use should use keyrings. +This example is intended to serve as reference material for users migrating away from master key providers. +We recommend using keyrings rather than master key providers. For examples using keyrings, see the ``examples/src/keyrings`` directory. This example shows how to configure and use a KMS master key provider with with CMKs in multiple regions. diff --git a/examples/src/master_key_provider/aws_kms/single_cmk.py b/examples/src/master_key_provider/aws_kms/single_cmk.py index 65d4a2ae7..5a23493d6 100644 --- a/examples/src/master_key_provider/aws_kms/single_cmk.py +++ b/examples/src/master_key_provider/aws_kms/single_cmk.py @@ -1,8 +1,8 @@ # Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0 """ -This example is provided as a reference for users migrating away from master key providers. -We recommend that all new use should use keyrings. +This example is intended to serve as reference material for users migrating away from master key providers. +We recommend using keyrings rather than master key providers. For examples using keyrings, see the ``examples/src/keyrings`` directory. This example shows how to configure and use a KMS master key with a single KMS CMK. diff --git a/examples/src/master_key_provider/multi/aws_kms_with_escrow.py b/examples/src/master_key_provider/multi/aws_kms_with_escrow.py index 73fb626d8..a9c4a2130 100644 --- a/examples/src/master_key_provider/multi/aws_kms_with_escrow.py +++ b/examples/src/master_key_provider/multi/aws_kms_with_escrow.py @@ -1,8 +1,8 @@ # Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0 """ -This example is provided as a reference for users migrating away from master key providers. -We recommend that all new use should use keyrings. +This example is intended to serve as reference material for users migrating away from master key providers. +We recommend using keyrings rather than master key providers. For examples using keyrings, see the ``examples/src/keyrings`` directory. One use-case that we have seen customers need is diff --git a/examples/src/master_key_provider/raw_aes/raw_aes.py b/examples/src/master_key_provider/raw_aes/raw_aes.py index 035278120..943fcb67b 100644 --- a/examples/src/master_key_provider/raw_aes/raw_aes.py +++ b/examples/src/master_key_provider/raw_aes/raw_aes.py @@ -1,8 +1,8 @@ # Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0 """ -This example is provided as a reference for users migrating away from master key providers. -We recommend that all new use should use keyrings. +This example is intended to serve as reference material for users migrating away from master key providers. +We recommend using keyrings rather than master key providers. For examples using keyrings, see the ``examples/src/keyrings`` directory. This examples shows how to configure and use a raw AES master key. diff --git a/examples/src/master_key_provider/raw_rsa/private_key_only_from_pem.py b/examples/src/master_key_provider/raw_rsa/private_key_only_from_pem.py index a4c6eaf3d..5ad78c00b 100644 --- a/examples/src/master_key_provider/raw_rsa/private_key_only_from_pem.py +++ b/examples/src/master_key_provider/raw_rsa/private_key_only_from_pem.py @@ -1,8 +1,8 @@ # Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0 """ -This example is provided as a reference for users migrating away from master key providers. -We recommend that all new use should use keyrings. +This example is intended to serve as reference material for users migrating away from master key providers. +We recommend using keyrings rather than master key providers. For examples using keyrings, see the ``examples/src/keyrings`` directory. This example shows how to configure and use a raw RSA master key using a PEM-encoded RSA private key. From c5d3a94d79979e46e4df183cb5cdc9f5c62f6886 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Thu, 2 Apr 2020 18:40:11 -0700 Subject: [PATCH 7/7] docs: add MKP examples to readme map --- examples/README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/examples/README.md b/examples/README.md index 592872232..c4c0e67ec 100644 --- a/examples/README.md +++ b/examples/README.md @@ -33,10 +33,13 @@ We start with AWS KMS examples, then show how to use other wrapping keys. * Using AWS Key Management Service (AWS KMS) * How to use one AWS KMS CMK * [with keyrings](./src/keyring/aws_kms/single_cmk.py) + * [with master key providers](./src/master_key_provider/aws_kms/single_cmk.py) * How to use multiple AWS KMS CMKs in different regions * [with keyrings](./src/keyring/aws_kms/multiple_regions.py) + * [with master key providers](./src/master_key_provider/aws_kms/multiple_regions.py) * How to decrypt when you don't know the CMK * [with keyrings](./src/keyring/aws_kms/discovery_decrypt.py) + * [with master key providers](./src/master_key_provider/aws_kms/discovery_decrypt.py) * 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 failover to others @@ -44,15 +47,18 @@ We start with AWS KMS examples, then show how to use other wrapping keys. * Using raw wrapping keys * How to use a raw AES wrapping key * [with keyrings](./src/keyring/raw_aes/raw_aes.py) + * [with master key providers](./src/master_key_provider/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) + * [with master key providers](./src/master_key_provider/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) + * [with master key providers](./src/master_key_provider/multi/aws_kms_with_escrow.py) ### Keyrings