diff --git a/examples/src/migration/README.rst b/examples/src/migration/README.rst new file mode 100644 index 000000000..41d3f5515 --- /dev/null +++ b/examples/src/migration/README.rst @@ -0,0 +1,19 @@ +################## +Migration Examples +################## + +The `Encryption SDK for Python`_ now uses the `AWS Cryptographic Material Providers Library`_. The MPL abstracts lower +level cryptographic materials management of encryption and decryption materials. + +This directory contains migration examples for: + +#. Moving to Keyrings from Master Key Providers: + * Migration example to AWS KMS keyring from AWS KMS Master Key Provider. + * Migration example to Raw AES keyring from Raw AES Master Key Provider. + * Migration example to Raw RSA keyring from Raw RSA Master Key Provider. + +#. Migration to newer versions of the ESDK (4.x+) from 1.x versions: + * Setting a 'CommitmentPolicy' during migration - If you have messages encrypted with 1.x versions of the ESDK (i.e. not using key commitment) and want to migrate to encrypt with key commitment using the keyring providers introduced in ESDK 4.x, this example will guide you on how to decrypt those messages using the new version of the ESDK. + +.. _AWS Cryptographic Material Providers Library: https://github.com/aws/aws-cryptographic-material-providers-library +.. _Encryption SDK for Python: https://github.com/aws/aws-encryption-sdk-python/tree/9c34aad60fc918c1a9186ec5215a451e8bfd0f65 \ No newline at end of file diff --git a/examples/src/migration/migration_aws_kms_key_example.py b/examples/src/migration/migration_aws_kms_key_example.py new file mode 100644 index 000000000..28b8193e3 --- /dev/null +++ b/examples/src/migration/migration_aws_kms_key_example.py @@ -0,0 +1,166 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +This is a migration example for moving to the AWS KMS Keyring from AWS KMS master key provider (MKP) + +The AWS KMS keyring uses symmetric encryption KMS keys to generate, encrypt and +decrypt data keys. This example creates a KMS Keyring and KMS MKP and +then encrypts a custom input EXAMPLE_DATA with the same encryption context using both +the keyring and MKP. The example then decrypts the ciphertexts using both keyring and MKPs. +This example also includes some sanity checks for demonstration: +1. Decryption of these ciphertexts encrypted using keyring and MKP + is possible using both KMS keyring and KMS MKP +2. Both decrypted plaintexts are same and match EXAMPLE_DATA +These sanity checks are for demonstration in the example only. You do not need these in your code. + +Note: The ciphertexts obtained by encrypting EXAMPLE_DATA using keyring and MKP are not +the same because the ESDK generates different data keys each time for encryption of the data. +But both ciphertexts when decrypted using keyring and MKP should give the same plaintext result. + +For more information on how to use KMS keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html +""" +import boto3 +from aws_cryptographic_materialproviders.mpl import AwsCryptographicMaterialProviders +from aws_cryptographic_materialproviders.mpl.config import MaterialProvidersConfig +from aws_cryptographic_materialproviders.mpl.models import CreateAwsKmsKeyringInput +from aws_cryptographic_materialproviders.mpl.references import IKeyring +from typing import Dict # noqa pylint: disable=wrong-import-order + +import aws_encryption_sdk + +EXAMPLE_DATA: bytes = b"Hello World" + +DEFAULT_ENCRYPTION_CONTEXT : Dict[str, str] = { + "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", +} + + +def create_keyring( + kms_key_id: str, + aws_region="us-west-2" +): + """Demonstrate how to create an AWS KMS keyring. + + Usage: create_keyring(kms_key_id) + :param kms_key_id: KMS Key identifier for the KMS key you want to use. + :type kms_key_id: string + + For more information on KMS Key identifiers, see + https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id + """ + # Create a boto3 client for KMS. + kms_client = boto3.client('kms', region_name=aws_region) + + # Create a KMS keyring + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + keyring_input: CreateAwsKmsKeyringInput = CreateAwsKmsKeyringInput( + kms_key_id=kms_key_id, + kms_client=kms_client + ) + + keyring: IKeyring = mat_prov.create_aws_kms_keyring( + input=keyring_input + ) + + return keyring + + +def create_key_provider( + kms_key_id: str +): + """Demonstrate how to create an AWS KMS master key provider. + + Usage: create_key_provider(kms_key_id) + :param kms_key_id: KMS Key identifier for the KMS key you want to use. + :type kms_key_id: string + + For more information on KMS Key identifiers, see + https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id + """ + # Create a KMS master key provider. + key_provider = aws_encryption_sdk.StrictAwsKmsMasterKeyProvider(key_ids=[ + kms_key_id, + ]) + + return key_provider + + +def migration_aws_kms_key( + kms_key_id: str +): + """Demonstrate a migration example for moving to an AWS KMS keyring from AWS KMS MKP. + + Usage: migration_aws_kms_key(kms_key_id) + :param kms_key_id: KMS Key identifier for the KMS key you want to use for encryption and + decryption of your data keys. + :type kms_key_id: string + + For more information on KMS Key identifiers, see + https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id + """ + client = aws_encryption_sdk.EncryptionSDKClient() + + # 1a. Create a AWS KMS Keyring + aws_kms_keyring = create_keyring(kms_key_id=kms_key_id) + + # 1b. Create a AWS KMS Master Key Provider + aws_kms_master_key_provider = create_key_provider(kms_key_id=kms_key_id) + + # 2a. Encrypt EXAMPLE_DATA using AWS KMS Keyring + ciphertext_keyring, _ = client.encrypt( + source=EXAMPLE_DATA, + keyring=aws_kms_keyring, + encryption_context=DEFAULT_ENCRYPTION_CONTEXT + ) + + # 2b. Encrypt EXAMPLE_DATA using AWS KMS Master Key Provider + ciphertext_mkp, _ = client.encrypt( + source=EXAMPLE_DATA, + key_provider=aws_kms_master_key_provider, + encryption_context=DEFAULT_ENCRYPTION_CONTEXT + ) + + # Note: The ciphertexts obtained by encrypting EXAMPLE_DATA using keyring and MKP + # (that is ciphertext_keyring and ciphertext_mkp) are not the same because the ESDK + # generates different data keys each time for encryption of the data. But both + # ciphertexts when decrypted using keyring and MKP should give the same plaintext result. + + # 3. Decrypt the ciphertext_keyring using both the keyring and MKP and ensure the + # resulting plaintext is the same and also equal to EXAMPLE_DATA + decrypted_ciphertext_keyring_using_keyring, _ = client.decrypt( + source=ciphertext_keyring, + keyring=aws_kms_keyring + ) + + decrypted_ciphertext_keyring_using_mkp, _ = client.decrypt( + source=ciphertext_keyring, + key_provider=aws_kms_master_key_provider + ) + + assert decrypted_ciphertext_keyring_using_keyring == decrypted_ciphertext_keyring_using_mkp \ + and decrypted_ciphertext_keyring_using_keyring == EXAMPLE_DATA, \ + "Decrypted outputs using keyring and master key provider are not the same" + + # 4. Decrypt the ciphertext_mkp using both the keyring and MKP and ensure the + # resulting plaintext is the same and also equal to EXAMPLE_DATA + decrypted_ciphertext_mkp_using_keyring, _ = client.decrypt( + source=ciphertext_mkp, + keyring=aws_kms_keyring + ) + + decrypted_ciphertext_mkp_using_mkp, _ = client.decrypt( + source=ciphertext_mkp, + key_provider=aws_kms_master_key_provider + ) + + assert decrypted_ciphertext_mkp_using_keyring == decrypted_ciphertext_mkp_using_mkp \ + and decrypted_ciphertext_mkp_using_keyring == EXAMPLE_DATA, \ + "Decrypted outputs using keyring and master key provider are not the same" diff --git a/examples/src/migration/migration_raw_aes_key_example.py b/examples/src/migration/migration_raw_aes_key_example.py new file mode 100644 index 000000000..772f83cf5 --- /dev/null +++ b/examples/src/migration/migration_raw_aes_key_example.py @@ -0,0 +1,207 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +This is a migration example for moving to the Raw AES Keyring from Raw AES master key provider (MKP) + +The Raw AES keyring lets you use an AES symmetric key that you provide as a wrapping key that +protects your data key. You need to generate, store, and protect the key material, +preferably in a hardware security module (HSM) or key management system. Use a Raw AES keyring +when you need to provide the wrapping key and encrypt the data keys locally or offline. + +This example creates a Raw AES Keyring and Raw AES MKP and +then encrypts a custom input EXAMPLE_DATA with the same encryption context using both +the keyring and MKP. The example then decrypts the ciphertexts using both keyring and MKPs. +This example also includes some sanity checks for demonstration: +1. Decryption of these ciphertexts encrypted using keyring and MKP + is possible using both Raw AES keyring and Raw AES MKP +2. Both decrypted plaintexts are same and match EXAMPLE_DATA +These sanity checks are for demonstration in the example only. You do not need these in your code. + +Note: The ciphertexts obtained by encrypting EXAMPLE_DATA using keyring and MKP are not +the same because the ESDK generates different data keys each time for encryption of the data. +But both ciphertexts when decrypted using keyring and MKP will give the same plaintext result. + +For more information on how to use Raw AES keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-aes-keyring.html +""" +import secrets + +from aws_cryptographic_materialproviders.mpl import AwsCryptographicMaterialProviders +from aws_cryptographic_materialproviders.mpl.config import MaterialProvidersConfig +from aws_cryptographic_materialproviders.mpl.models import AesWrappingAlg, CreateRawAesKeyringInput +from aws_cryptographic_materialproviders.mpl.references import IKeyring +from typing import Dict # noqa pylint: disable=wrong-import-order + +import aws_encryption_sdk +from aws_encryption_sdk.identifiers import EncryptionKeyType, WrappingAlgorithm +from aws_encryption_sdk.internal.crypto.wrapping_keys import WrappingKey +from aws_encryption_sdk.key_providers.raw import RawMasterKeyProvider + +EXAMPLE_DATA: bytes = b"Hello World" + +DEFAULT_ENCRYPTION_CONTEXT : Dict[str, str] = { + "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", +} + +DEFAULT_AES_256_STATIC_KEY = secrets.token_bytes(32) + +# The key namespace in the Raw keyrings is equivalent to Provider ID (or Provider) field +# in the Raw Master Key Providers +DEFAULT_KEY_NAME_SPACE = "Some managed raw keys" + +# The key name in the Raw keyrings is equivalent to the Key ID field +# in the Raw Master Key Providers +DEFAULT_KEY_NAME = "My 256-bit AES wrapping key" + + +def create_keyring(): + """Demonstrate how to create a Raw AES keyring. + + Usage: create_keyring() + """ + # We fix the static key in order to make the test deterministic + static_key = DEFAULT_AES_256_STATIC_KEY + + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + # The key namespace in the Raw keyrings is equivalent to Provider ID (or Provider) field + # in the Raw Master Key Providers + # The key name in the Raw keyrings is equivalent to the Key ID field + # in the Raw Master Key Providers + keyring_input: CreateRawAesKeyringInput = CreateRawAesKeyringInput( + key_namespace=DEFAULT_KEY_NAME_SPACE, + key_name=DEFAULT_KEY_NAME, + wrapping_key=static_key, + wrapping_alg=AesWrappingAlg.ALG_AES256_GCM_IV12_TAG16 + ) + + keyring: IKeyring = mat_prov.create_raw_aes_keyring( + input=keyring_input + ) + + return keyring + + +# This is a helper class necessary for the Raw AES master key provider +# In the StaticMasterKeyProvider, we fix the static key to +# DEFAULT_AES_256_STATIC_KEY in order to make the test deterministic. +# Thus, both the Raw AES keyring and Raw AES MKP have the same key +# and we are able to encrypt data using keyrings and decrypt using MKP and vice versa +# In practice, users should generate a new random key for each key id. +class StaticMasterKeyProvider(RawMasterKeyProvider): + """Generates 256-bit keys for each unique key ID.""" + + # The key namespace in the Raw keyrings is equivalent to Provider ID (or Provider) field + # in the Raw Master Key Providers + provider_id = DEFAULT_KEY_NAME_SPACE + + def __init__(self, **kwargs): # pylint: disable=unused-argument + """Initialize empty map of keys.""" + self._static_keys = {} + + def _get_raw_key(self, key_id): + """Returns a static, symmetric key for the specified key ID. + + :param str key_id: Key ID + :returns: Wrapping key that contains the specified static key + :rtype: :class:`aws_encryption_sdk.internal.crypto.WrappingKey` + """ + try: + static_key = self._static_keys[key_id] + except KeyError: + # We fix the static key in order to make the test deterministic + # In practice, you should get this key from a secure key management system such as an HSM. + static_key = DEFAULT_AES_256_STATIC_KEY + self._static_keys[key_id] = static_key + return WrappingKey( + wrapping_algorithm=WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING, + wrapping_key=static_key, + wrapping_key_type=EncryptionKeyType.SYMMETRIC, + ) + + +def create_key_provider(): + """Demonstrate how to create a Raw AES master key provider. + + Usage: create_key_provider() + """ + # Create a Raw AES master key provider. + + # The key name in the Raw keyrings is equivalent to the Key ID field + # in the Raw Master Key Providers + key_id = DEFAULT_KEY_NAME + key_provider = StaticMasterKeyProvider() + key_provider.add_master_key(key_id) + + return key_provider + + +def migration_raw_aes_key(): + """Demonstrate a migration example for moving to a Raw AES keyring from Raw AES MKP. + + Usage: migration_raw_aes_key() + """ + client = aws_encryption_sdk.EncryptionSDKClient() + + # 1a. Create a Raw AES Keyring + raw_aes_keyring = create_keyring() + + # 1b. Create a Raw AES Master Key Provider + raw_aes_master_key_provider = create_key_provider() + + # 2a. Encrypt EXAMPLE_DATA using Raw AES Keyring + ciphertext_keyring, _ = client.encrypt( + source=EXAMPLE_DATA, + keyring=raw_aes_keyring, + encryption_context=DEFAULT_ENCRYPTION_CONTEXT + ) + + # 2b. Encrypt EXAMPLE_DATA using Raw AES Master Key Provider + ciphertext_mkp, _ = client.encrypt( + source=EXAMPLE_DATA, + key_provider=raw_aes_master_key_provider, + encryption_context=DEFAULT_ENCRYPTION_CONTEXT + ) + + # Note: The ciphertexts obtained by encrypting EXAMPLE_DATA using keyring and MKP + # (that is ciphertext_keyring and ciphertext_mkp) are not the same because the ESDK + # generates different data keys each time for encryption of the data. But both + # ciphertexts when decrypted using keyring and MKP will give the same plaintext result. + + # 3. Decrypt the ciphertext_keyring using both the keyring and MKP and ensure the + # resulting plaintext is the same and also equal to EXAMPLE_DATA + decrypted_ciphertext_keyring_using_keyring, _ = client.decrypt( + source=ciphertext_keyring, + keyring=raw_aes_keyring + ) + + decrypted_ciphertext_keyring_using_mkp, _ = client.decrypt( + source=ciphertext_keyring, + key_provider=raw_aes_master_key_provider + ) + + assert decrypted_ciphertext_keyring_using_keyring == decrypted_ciphertext_keyring_using_mkp \ + and decrypted_ciphertext_keyring_using_keyring == EXAMPLE_DATA, \ + "Decrypted outputs using keyring and master key provider are not the same" + + # 4. Decrypt the ciphertext_mkp using both the keyring and MKP and ensure the + # resulting plaintext is the same and also equal to EXAMPLE_DATA + decrypted_ciphertext_mkp_using_keyring, _ = client.decrypt( + source=ciphertext_mkp, + keyring=raw_aes_keyring + ) + + decrypted_ciphertext_mkp_using_mkp, _ = client.decrypt( + source=ciphertext_mkp, + key_provider=raw_aes_master_key_provider + ) + + assert decrypted_ciphertext_mkp_using_keyring == decrypted_ciphertext_mkp_using_mkp \ + and decrypted_ciphertext_mkp_using_keyring == EXAMPLE_DATA, \ + "Decrypted outputs using keyring and master key provider are not the same" diff --git a/examples/src/migration/migration_raw_rsa_key_example.py b/examples/src/migration/migration_raw_rsa_key_example.py new file mode 100644 index 000000000..7c6020a53 --- /dev/null +++ b/examples/src/migration/migration_raw_rsa_key_example.py @@ -0,0 +1,259 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +This is a migration example for moving to the Raw RSA Keyring from Raw RSA master key provider (MKP) + +The Raw RSA keyring performs asymmetric encryption and decryption of data keys in local memory +with RSA public and private keys that you provide. In this example, we define the RSA keys to +encrypt and decrypt the data keys. + +You need to generate, store, and protect the private key, preferably in a +hardware security module (HSM) or key management system. +The encryption function encrypts the data key under the RSA public key. The decryption function +decrypts the data key using the private key. + +This example creates a Raw RSA Keyring and Raw RSA MKP and +then encrypts a custom input EXAMPLE_DATA with the same encryption context using both +the keyring and MKP. The example then decrypts the ciphertexts using both keyring and MKPs. +This example also includes some sanity checks for demonstration: +1. Decryption of these ciphertexts encrypted using keyring and MKP + is possible using both Raw RSA keyring and Raw RSA MKP +2. Both decrypted plaintexts are same and match EXAMPLE_DATA +These sanity checks are for demonstration in the example only. You do not need these in your code. + +Note: The ciphertexts obtained by encrypting EXAMPLE_DATA using keyring and MKP are not +the same because the ESDK generates different data keys each time for encryption of the data. +But both ciphertexts when decrypted using keyring and MKP will give the same plaintext result. + +For more information on how to use Raw RSA keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-rsa-keyring.html +""" +from aws_cryptographic_materialproviders.mpl import AwsCryptographicMaterialProviders +from aws_cryptographic_materialproviders.mpl.config import MaterialProvidersConfig +from aws_cryptographic_materialproviders.mpl.models import CreateRawRsaKeyringInput, PaddingScheme +from aws_cryptographic_materialproviders.mpl.references import IKeyring +from cryptography.hazmat.backends import default_backend as crypto_default_backend +from cryptography.hazmat.primitives import serialization as crypto_serialization +from cryptography.hazmat.primitives.asymmetric import rsa +from typing import Dict # noqa pylint: disable=wrong-import-order + +import aws_encryption_sdk +from aws_encryption_sdk.identifiers import EncryptionKeyType, WrappingAlgorithm +from aws_encryption_sdk.internal.crypto.wrapping_keys import WrappingKey +from aws_encryption_sdk.key_providers.raw import RawMasterKeyProvider + +EXAMPLE_DATA: bytes = b"Hello World" + +DEFAULT_ENCRYPTION_CONTEXT : Dict[str, str] = { + "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", +} + +# The key namespace in the Raw keyrings is equivalent to Provider ID (or Provider) field +# in the Raw Master Key Providers +DEFAULT_KEY_NAME_SPACE = "Some managed raw keys" + +# The key name in the Raw keyrings is equivalent to the Key ID field +# in the Raw Master Key Providers +DEFAULT_KEY_NAME = "My 4096-bit RSA wrapping key" + + +def generate_rsa_keys_helper(): + """Generates a 4096-bit RSA public and private key pair + + Usage: generate_rsa_keys_helper() + """ + ssh_rsa_exponent = 65537 + bit_strength = 4096 + key = rsa.generate_private_key( + backend=crypto_default_backend(), + public_exponent=ssh_rsa_exponent, + key_size=bit_strength + ) + + # This example choses a particular type of encoding, format and encryption_algorithm + # Users can choose the PublicFormat, PrivateFormat and encryption_algorithm that align most + # with their use-cases + public_key = key.public_key().public_bytes( + encoding=crypto_serialization.Encoding.PEM, + format=crypto_serialization.PublicFormat.SubjectPublicKeyInfo + ) + private_key = key.private_bytes( + encoding=crypto_serialization.Encoding.PEM, + format=crypto_serialization.PrivateFormat.TraditionalOpenSSL, + encryption_algorithm=crypto_serialization.NoEncryption() + ) + + return public_key, private_key + + +DEFAULT_RSA_PUBLIC_KEY, DEFAULT_RSA_PRIVATE_KEY = generate_rsa_keys_helper() + + +def create_keyring(public_key, private_key): + """Demonstrate how to create a Raw RSA keyring using the key pair. + + Usage: create_keyring(public_key, private_key) + """ + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + # The key namespace in the Raw keyrings is equivalent to Provider ID (or Provider) field + # in the Raw Master Key Providers + # The key name in the Raw keyrings is equivalent to the Key ID field + # in the Raw Master Key Providers + keyring_input: CreateRawRsaKeyringInput = CreateRawRsaKeyringInput( + key_namespace=DEFAULT_KEY_NAME_SPACE, + key_name=DEFAULT_KEY_NAME, + padding_scheme=PaddingScheme.OAEP_SHA256_MGF1, + public_key=public_key, + private_key=private_key + ) + + keyring: IKeyring = mat_prov.create_raw_rsa_keyring( + input=keyring_input + ) + + return keyring + + +# This is a helper class necessary for the Raw RSA master key provider. +# In the StaticMasterKeyProvider, we fix the static key to +# DEFAULT_RSA_PRIVATE_KEY in order to make the test deterministic. +# Thus, both the Raw RSA keyring and Raw RSA MKP have the same private_key +# and we are able to encrypt data using keyrings and decrypt using MKP and vice versa +# In practice, users should generate a new random key pair for each key id. +class StaticMasterKeyProvider(RawMasterKeyProvider): + """Provides 4096-bit RSA keys consistently per unique key id.""" + + # The key namespace in the Raw keyrings is equivalent to Provider ID (or Provider) field + # in the Raw Master Key Providers + provider_id = DEFAULT_KEY_NAME_SPACE + + def __init__(self, **kwargs): # pylint: disable=unused-argument + """Initialize empty map of keys.""" + self._static_keys = {} + + def _get_raw_key(self, key_id): + """Retrieves a static, RSA key for the specified key id. + + :param str key_id: User-defined ID for the static key + :returns: Wrapping key that contains the specified static key + :rtype: :class:`aws_encryption_sdk.internal.crypto.WrappingKey` + """ + try: + static_key = self._static_keys[key_id] + except KeyError: + # We fix the static key in order to make the test deterministic + # In practice, you should get this key from a secure key management system such as an HSM. + # Also, in practice, users should generate a new key pair for each key id in + # the StaticMasterKeyProvider. + static_key = DEFAULT_RSA_PRIVATE_KEY + self._static_keys[key_id] = static_key + return WrappingKey( + wrapping_algorithm=WrappingAlgorithm.RSA_OAEP_SHA256_MGF1, + wrapping_key=static_key, + wrapping_key_type=EncryptionKeyType.PRIVATE, + ) + + +def create_key_provider(): + """Demonstrate how to create a Raw RSA master key provider. + + Usage: create_key_provider() + """ + # Create a Raw RSA master key provider. + + # The key name in the Raw keyrings is equivalent to the Key ID field + # in the Raw Master Key Providers + key_id = DEFAULT_KEY_NAME + + # In this example, we fix the static key to DEFAULT_RSA_PRIVATE_KEY in both the keyring + # and MKP (for MKP, we fix the static key in StaticMasterKeyProvider) in order to make + # the test deterministic. Thus, both the Raw RSA keyring and Raw RSA MKP have the same + # private_key and we are able to encrypt data using keyrings and decrypt using MKP + # and vice versa. In practice, users should generate a new key pair for each key id in + # the StaticMasterKeyProvider. + key_provider = StaticMasterKeyProvider() + key_provider.add_master_key(key_id) + + return key_provider + + +def migration_raw_rsa_key( + public_key=DEFAULT_RSA_PUBLIC_KEY, + private_key=DEFAULT_RSA_PRIVATE_KEY +): + """Demonstrate a migration example for moving to a Raw RSA keyring from Raw RSA MKP. + + Usage: migration_raw_rsa_key(public_key, private_key) + """ + client = aws_encryption_sdk.EncryptionSDKClient() + + # 1a. Create a Raw RSA Keyring + raw_rsa_keyring = create_keyring(public_key=public_key, private_key=private_key) + + # 1b. Create a Raw RSA Master Key Provider + + # In this example, we fix the static key to DEFAULT_RSA_PRIVATE_KEY in both the keyring + # and MKP (for MKP, we fix the static key in StaticMasterKeyProvider) in order to make + # the test deterministic. Thus, both the Raw RSA keyring and Raw RSA MKP have the same + # private_key and we are able to encrypt data using keyrings and decrypt using MKP + # and vice versa. In practice, users should generate a new key pair for each key id in + # the StaticMasterKeyProvider. + raw_rsa_master_key_provider = create_key_provider() + + # 2a. Encrypt EXAMPLE_DATA using Raw RSA Keyring + ciphertext_keyring, _ = client.encrypt( + source=EXAMPLE_DATA, + keyring=raw_rsa_keyring, + encryption_context=DEFAULT_ENCRYPTION_CONTEXT + ) + + # 2b. Encrypt EXAMPLE_DATA using Raw RSA Master Key Provider + ciphertext_mkp, _ = client.encrypt( + source=EXAMPLE_DATA, + key_provider=raw_rsa_master_key_provider, + encryption_context=DEFAULT_ENCRYPTION_CONTEXT + ) + + # Note: The ciphertexts obtained by encrypting EXAMPLE_DATA using keyring and MKP + # (that is ciphertext_keyring and ciphertext_mkp) are not the same because the ESDK + # generates different data keys each time for encryption of the data. But both + # ciphertexts when decrypted using keyring and MKP will give the same plaintext result. + + # 3. Decrypt the ciphertext_keyring using both the keyring and MKP and ensure the + # resulting plaintext is the same and also equal to EXAMPLE_DATA + decrypted_ciphertext_keyring_using_keyring, _ = client.decrypt( + source=ciphertext_keyring, + keyring=raw_rsa_keyring + ) + + decrypted_ciphertext_keyring_using_mkp, _ = client.decrypt( + source=ciphertext_keyring, + key_provider=raw_rsa_master_key_provider + ) + + assert decrypted_ciphertext_keyring_using_keyring == decrypted_ciphertext_keyring_using_mkp \ + and decrypted_ciphertext_keyring_using_keyring == EXAMPLE_DATA, \ + "Decrypted outputs using keyring and master key provider are not the same" + + # 4. Decrypt the ciphertext_mkp using both the keyring and MKP and ensure the + # resulting plaintext is the same and also equal to EXAMPLE_DATA + decrypted_ciphertext_mkp_using_keyring, _ = client.decrypt( + source=ciphertext_mkp, + keyring=raw_rsa_keyring + ) + + decrypted_ciphertext_mkp_using_mkp, _ = client.decrypt( + source=ciphertext_mkp, + key_provider=raw_rsa_master_key_provider + ) + + assert decrypted_ciphertext_mkp_using_keyring == decrypted_ciphertext_mkp_using_mkp \ + and decrypted_ciphertext_mkp_using_keyring == EXAMPLE_DATA, \ + "Decrypted outputs using keyring and master key provider are not the same" diff --git a/examples/src/migration_set_commitment_policy_example.py b/examples/src/migration/migration_set_commitment_policy_example.py similarity index 100% rename from examples/src/migration_set_commitment_policy_example.py rename to examples/src/migration/migration_set_commitment_policy_example.py diff --git a/examples/test/migration/__init__.py b/examples/test/migration/__init__.py new file mode 100644 index 000000000..120179eda --- /dev/null +++ b/examples/test/migration/__init__.py @@ -0,0 +1,3 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Stub module indicator to make linter configuration simpler.""" diff --git a/examples/test/migration/test_i_migration_aws_kms_key_example.py b/examples/test/migration/test_i_migration_aws_kms_key_example.py new file mode 100644 index 000000000..d4cf49ce9 --- /dev/null +++ b/examples/test/migration/test_i_migration_aws_kms_key_example.py @@ -0,0 +1,16 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Test suite for the migration_aws_kms_key_example.""" +import pytest + +from ...src.migration.migration_aws_kms_key_example import ( + migration_aws_kms_key, +) + +pytestmark = [pytest.mark.examples] + + +def test_migration_aws_kms_key(): + """Test function for migration of AWS KMS Keys.""" + kms_key_id = "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f" + migration_aws_kms_key(kms_key_id) diff --git a/examples/test/migration/test_i_migration_raw_aes_key_example.py b/examples/test/migration/test_i_migration_raw_aes_key_example.py new file mode 100644 index 000000000..7601e7dc0 --- /dev/null +++ b/examples/test/migration/test_i_migration_raw_aes_key_example.py @@ -0,0 +1,15 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Test suite for the migration_raw_aes_key_example.""" +import pytest + +from ...src.migration.migration_raw_aes_key_example import ( + migration_raw_aes_key, +) + +pytestmark = [pytest.mark.examples] + + +def test_migration_raw_aes_key(): + """Test function for migration of Raw AES keys.""" + migration_raw_aes_key() diff --git a/examples/test/migration/test_i_migration_raw_rsa_key_example.py b/examples/test/migration/test_i_migration_raw_rsa_key_example.py new file mode 100644 index 000000000..9e111d25a --- /dev/null +++ b/examples/test/migration/test_i_migration_raw_rsa_key_example.py @@ -0,0 +1,15 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Test suite for the migration_raw_rsa_key_example.""" +import pytest + +from ...src.migration.migration_raw_rsa_key_example import ( + migration_raw_rsa_key, +) + +pytestmark = [pytest.mark.examples] + + +def test_migration_raw_rsa_key(): + """Test function for migration of Raw RSA keys.""" + migration_raw_rsa_key() diff --git a/examples/test/test_i_migration_set_commitment_policy_example.py b/examples/test/migration/test_i_migration_set_commitment_policy_example.py similarity index 83% rename from examples/test/test_i_migration_set_commitment_policy_example.py rename to examples/test/migration/test_i_migration_set_commitment_policy_example.py index afbc758bc..4620d64df 100644 --- a/examples/test/test_i_migration_set_commitment_policy_example.py +++ b/examples/test/migration/test_i_migration_set_commitment_policy_example.py @@ -3,7 +3,7 @@ """Test suite for the migration_set_commitment_policy_example.""" import pytest -from ..src.migration_set_commitment_policy_example import encrypt_and_decrypt_with_keyring +from ...src.migration.migration_set_commitment_policy_example import encrypt_and_decrypt_with_keyring pytestmark = [pytest.mark.examples]