Skip to content

Commit 3476816

Browse files
authored
chore(migration examples): added KMS, raw AES and raw RSA keyring/MKP… (#687)
1 parent 39d4498 commit 3476816

10 files changed

+701
-1
lines changed

examples/src/migration/README.rst

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
##################
2+
Migration Examples
3+
##################
4+
5+
The `Encryption SDK for Python`_ now uses the `AWS Cryptographic Material Providers Library`_. The MPL abstracts lower
6+
level cryptographic materials management of encryption and decryption materials.
7+
8+
This directory contains migration examples for:
9+
10+
#. Moving to Keyrings from Master Key Providers:
11+
* Migration example to AWS KMS keyring from AWS KMS Master Key Provider.
12+
* Migration example to Raw AES keyring from Raw AES Master Key Provider.
13+
* Migration example to Raw RSA keyring from Raw RSA Master Key Provider.
14+
15+
#. Migration to newer versions of the ESDK (4.x+) from 1.x versions:
16+
* 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.
17+
18+
.. _AWS Cryptographic Material Providers Library: https://github.com/aws/aws-cryptographic-material-providers-library
19+
.. _Encryption SDK for Python: https://github.com/aws/aws-encryption-sdk-python/tree/9c34aad60fc918c1a9186ec5215a451e8bfd0f65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
"""
4+
This is a migration example for moving to the AWS KMS Keyring from AWS KMS master key provider (MKP)
5+
6+
The AWS KMS keyring uses symmetric encryption KMS keys to generate, encrypt and
7+
decrypt data keys. This example creates a KMS Keyring and KMS MKP and
8+
then encrypts a custom input EXAMPLE_DATA with the same encryption context using both
9+
the keyring and MKP. The example then decrypts the ciphertexts using both keyring and MKPs.
10+
This example also includes some sanity checks for demonstration:
11+
1. Decryption of these ciphertexts encrypted using keyring and MKP
12+
is possible using both KMS keyring and KMS MKP
13+
2. Both decrypted plaintexts are same and match EXAMPLE_DATA
14+
These sanity checks are for demonstration in the example only. You do not need these in your code.
15+
16+
Note: The ciphertexts obtained by encrypting EXAMPLE_DATA using keyring and MKP are not
17+
the same because the ESDK generates different data keys each time for encryption of the data.
18+
But both ciphertexts when decrypted using keyring and MKP should give the same plaintext result.
19+
20+
For more information on how to use KMS keyrings, see
21+
https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html
22+
"""
23+
import boto3
24+
from aws_cryptographic_materialproviders.mpl import AwsCryptographicMaterialProviders
25+
from aws_cryptographic_materialproviders.mpl.config import MaterialProvidersConfig
26+
from aws_cryptographic_materialproviders.mpl.models import CreateAwsKmsKeyringInput
27+
from aws_cryptographic_materialproviders.mpl.references import IKeyring
28+
from typing import Dict # noqa pylint: disable=wrong-import-order
29+
30+
import aws_encryption_sdk
31+
32+
EXAMPLE_DATA: bytes = b"Hello World"
33+
34+
DEFAULT_ENCRYPTION_CONTEXT : Dict[str, str] = {
35+
"encryption": "context",
36+
"is not": "secret",
37+
"but adds": "useful metadata",
38+
"that can help you": "be confident that",
39+
"the data you are handling": "is what you think it is",
40+
}
41+
42+
43+
def create_keyring(
44+
kms_key_id: str,
45+
aws_region="us-west-2"
46+
):
47+
"""Demonstrate how to create an AWS KMS keyring.
48+
49+
Usage: create_keyring(kms_key_id)
50+
:param kms_key_id: KMS Key identifier for the KMS key you want to use.
51+
:type kms_key_id: string
52+
53+
For more information on KMS Key identifiers, see
54+
https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id
55+
"""
56+
# Create a boto3 client for KMS.
57+
kms_client = boto3.client('kms', region_name=aws_region)
58+
59+
# Create a KMS keyring
60+
mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders(
61+
config=MaterialProvidersConfig()
62+
)
63+
64+
keyring_input: CreateAwsKmsKeyringInput = CreateAwsKmsKeyringInput(
65+
kms_key_id=kms_key_id,
66+
kms_client=kms_client
67+
)
68+
69+
keyring: IKeyring = mat_prov.create_aws_kms_keyring(
70+
input=keyring_input
71+
)
72+
73+
return keyring
74+
75+
76+
def create_key_provider(
77+
kms_key_id: str
78+
):
79+
"""Demonstrate how to create an AWS KMS master key provider.
80+
81+
Usage: create_key_provider(kms_key_id)
82+
:param kms_key_id: KMS Key identifier for the KMS key you want to use.
83+
:type kms_key_id: string
84+
85+
For more information on KMS Key identifiers, see
86+
https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id
87+
"""
88+
# Create a KMS master key provider.
89+
key_provider = aws_encryption_sdk.StrictAwsKmsMasterKeyProvider(key_ids=[
90+
kms_key_id,
91+
])
92+
93+
return key_provider
94+
95+
96+
def migration_aws_kms_key(
97+
kms_key_id: str
98+
):
99+
"""Demonstrate a migration example for moving to an AWS KMS keyring from AWS KMS MKP.
100+
101+
Usage: migration_aws_kms_key(kms_key_id)
102+
:param kms_key_id: KMS Key identifier for the KMS key you want to use for encryption and
103+
decryption of your data keys.
104+
:type kms_key_id: string
105+
106+
For more information on KMS Key identifiers, see
107+
https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id
108+
"""
109+
client = aws_encryption_sdk.EncryptionSDKClient()
110+
111+
# 1a. Create a AWS KMS Keyring
112+
aws_kms_keyring = create_keyring(kms_key_id=kms_key_id)
113+
114+
# 1b. Create a AWS KMS Master Key Provider
115+
aws_kms_master_key_provider = create_key_provider(kms_key_id=kms_key_id)
116+
117+
# 2a. Encrypt EXAMPLE_DATA using AWS KMS Keyring
118+
ciphertext_keyring, _ = client.encrypt(
119+
source=EXAMPLE_DATA,
120+
keyring=aws_kms_keyring,
121+
encryption_context=DEFAULT_ENCRYPTION_CONTEXT
122+
)
123+
124+
# 2b. Encrypt EXAMPLE_DATA using AWS KMS Master Key Provider
125+
ciphertext_mkp, _ = client.encrypt(
126+
source=EXAMPLE_DATA,
127+
key_provider=aws_kms_master_key_provider,
128+
encryption_context=DEFAULT_ENCRYPTION_CONTEXT
129+
)
130+
131+
# Note: The ciphertexts obtained by encrypting EXAMPLE_DATA using keyring and MKP
132+
# (that is ciphertext_keyring and ciphertext_mkp) are not the same because the ESDK
133+
# generates different data keys each time for encryption of the data. But both
134+
# ciphertexts when decrypted using keyring and MKP should give the same plaintext result.
135+
136+
# 3. Decrypt the ciphertext_keyring using both the keyring and MKP and ensure the
137+
# resulting plaintext is the same and also equal to EXAMPLE_DATA
138+
decrypted_ciphertext_keyring_using_keyring, _ = client.decrypt(
139+
source=ciphertext_keyring,
140+
keyring=aws_kms_keyring
141+
)
142+
143+
decrypted_ciphertext_keyring_using_mkp, _ = client.decrypt(
144+
source=ciphertext_keyring,
145+
key_provider=aws_kms_master_key_provider
146+
)
147+
148+
assert decrypted_ciphertext_keyring_using_keyring == decrypted_ciphertext_keyring_using_mkp \
149+
and decrypted_ciphertext_keyring_using_keyring == EXAMPLE_DATA, \
150+
"Decrypted outputs using keyring and master key provider are not the same"
151+
152+
# 4. Decrypt the ciphertext_mkp using both the keyring and MKP and ensure the
153+
# resulting plaintext is the same and also equal to EXAMPLE_DATA
154+
decrypted_ciphertext_mkp_using_keyring, _ = client.decrypt(
155+
source=ciphertext_mkp,
156+
keyring=aws_kms_keyring
157+
)
158+
159+
decrypted_ciphertext_mkp_using_mkp, _ = client.decrypt(
160+
source=ciphertext_mkp,
161+
key_provider=aws_kms_master_key_provider
162+
)
163+
164+
assert decrypted_ciphertext_mkp_using_keyring == decrypted_ciphertext_mkp_using_mkp \
165+
and decrypted_ciphertext_mkp_using_keyring == EXAMPLE_DATA, \
166+
"Decrypted outputs using keyring and master key provider are not the same"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
"""
4+
This is a migration example for moving to the Raw AES Keyring from Raw AES master key provider (MKP)
5+
6+
The Raw AES keyring lets you use an AES symmetric key that you provide as a wrapping key that
7+
protects your data key. You need to generate, store, and protect the key material,
8+
preferably in a hardware security module (HSM) or key management system. Use a Raw AES keyring
9+
when you need to provide the wrapping key and encrypt the data keys locally or offline.
10+
11+
This example creates a Raw AES Keyring and Raw AES MKP and
12+
then encrypts a custom input EXAMPLE_DATA with the same encryption context using both
13+
the keyring and MKP. The example then decrypts the ciphertexts using both keyring and MKPs.
14+
This example also includes some sanity checks for demonstration:
15+
1. Decryption of these ciphertexts encrypted using keyring and MKP
16+
is possible using both Raw AES keyring and Raw AES MKP
17+
2. Both decrypted plaintexts are same and match EXAMPLE_DATA
18+
These sanity checks are for demonstration in the example only. You do not need these in your code.
19+
20+
Note: The ciphertexts obtained by encrypting EXAMPLE_DATA using keyring and MKP are not
21+
the same because the ESDK generates different data keys each time for encryption of the data.
22+
But both ciphertexts when decrypted using keyring and MKP will give the same plaintext result.
23+
24+
For more information on how to use Raw AES keyrings, see
25+
https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-aes-keyring.html
26+
"""
27+
import secrets
28+
29+
from aws_cryptographic_materialproviders.mpl import AwsCryptographicMaterialProviders
30+
from aws_cryptographic_materialproviders.mpl.config import MaterialProvidersConfig
31+
from aws_cryptographic_materialproviders.mpl.models import AesWrappingAlg, CreateRawAesKeyringInput
32+
from aws_cryptographic_materialproviders.mpl.references import IKeyring
33+
from typing import Dict # noqa pylint: disable=wrong-import-order
34+
35+
import aws_encryption_sdk
36+
from aws_encryption_sdk.identifiers import EncryptionKeyType, WrappingAlgorithm
37+
from aws_encryption_sdk.internal.crypto.wrapping_keys import WrappingKey
38+
from aws_encryption_sdk.key_providers.raw import RawMasterKeyProvider
39+
40+
EXAMPLE_DATA: bytes = b"Hello World"
41+
42+
DEFAULT_ENCRYPTION_CONTEXT : Dict[str, str] = {
43+
"encryption": "context",
44+
"is not": "secret",
45+
"but adds": "useful metadata",
46+
"that can help you": "be confident that",
47+
"the data you are handling": "is what you think it is",
48+
}
49+
50+
DEFAULT_AES_256_STATIC_KEY = secrets.token_bytes(32)
51+
52+
# The key namespace in the Raw keyrings is equivalent to Provider ID (or Provider) field
53+
# in the Raw Master Key Providers
54+
DEFAULT_KEY_NAME_SPACE = "Some managed raw keys"
55+
56+
# The key name in the Raw keyrings is equivalent to the Key ID field
57+
# in the Raw Master Key Providers
58+
DEFAULT_KEY_NAME = "My 256-bit AES wrapping key"
59+
60+
61+
def create_keyring():
62+
"""Demonstrate how to create a Raw AES keyring.
63+
64+
Usage: create_keyring()
65+
"""
66+
# We fix the static key in order to make the test deterministic
67+
static_key = DEFAULT_AES_256_STATIC_KEY
68+
69+
mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders(
70+
config=MaterialProvidersConfig()
71+
)
72+
73+
# The key namespace in the Raw keyrings is equivalent to Provider ID (or Provider) field
74+
# in the Raw Master Key Providers
75+
# The key name in the Raw keyrings is equivalent to the Key ID field
76+
# in the Raw Master Key Providers
77+
keyring_input: CreateRawAesKeyringInput = CreateRawAesKeyringInput(
78+
key_namespace=DEFAULT_KEY_NAME_SPACE,
79+
key_name=DEFAULT_KEY_NAME,
80+
wrapping_key=static_key,
81+
wrapping_alg=AesWrappingAlg.ALG_AES256_GCM_IV12_TAG16
82+
)
83+
84+
keyring: IKeyring = mat_prov.create_raw_aes_keyring(
85+
input=keyring_input
86+
)
87+
88+
return keyring
89+
90+
91+
# This is a helper class necessary for the Raw AES master key provider
92+
# In the StaticMasterKeyProvider, we fix the static key to
93+
# DEFAULT_AES_256_STATIC_KEY in order to make the test deterministic.
94+
# Thus, both the Raw AES keyring and Raw AES MKP have the same key
95+
# and we are able to encrypt data using keyrings and decrypt using MKP and vice versa
96+
# In practice, users should generate a new random key for each key id.
97+
class StaticMasterKeyProvider(RawMasterKeyProvider):
98+
"""Generates 256-bit keys for each unique key ID."""
99+
100+
# The key namespace in the Raw keyrings is equivalent to Provider ID (or Provider) field
101+
# in the Raw Master Key Providers
102+
provider_id = DEFAULT_KEY_NAME_SPACE
103+
104+
def __init__(self, **kwargs): # pylint: disable=unused-argument
105+
"""Initialize empty map of keys."""
106+
self._static_keys = {}
107+
108+
def _get_raw_key(self, key_id):
109+
"""Returns a static, symmetric key for the specified key ID.
110+
111+
:param str key_id: Key ID
112+
:returns: Wrapping key that contains the specified static key
113+
:rtype: :class:`aws_encryption_sdk.internal.crypto.WrappingKey`
114+
"""
115+
try:
116+
static_key = self._static_keys[key_id]
117+
except KeyError:
118+
# We fix the static key in order to make the test deterministic
119+
# In practice, you should get this key from a secure key management system such as an HSM.
120+
static_key = DEFAULT_AES_256_STATIC_KEY
121+
self._static_keys[key_id] = static_key
122+
return WrappingKey(
123+
wrapping_algorithm=WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING,
124+
wrapping_key=static_key,
125+
wrapping_key_type=EncryptionKeyType.SYMMETRIC,
126+
)
127+
128+
129+
def create_key_provider():
130+
"""Demonstrate how to create a Raw AES master key provider.
131+
132+
Usage: create_key_provider()
133+
"""
134+
# Create a Raw AES master key provider.
135+
136+
# The key name in the Raw keyrings is equivalent to the Key ID field
137+
# in the Raw Master Key Providers
138+
key_id = DEFAULT_KEY_NAME
139+
key_provider = StaticMasterKeyProvider()
140+
key_provider.add_master_key(key_id)
141+
142+
return key_provider
143+
144+
145+
def migration_raw_aes_key():
146+
"""Demonstrate a migration example for moving to a Raw AES keyring from Raw AES MKP.
147+
148+
Usage: migration_raw_aes_key()
149+
"""
150+
client = aws_encryption_sdk.EncryptionSDKClient()
151+
152+
# 1a. Create a Raw AES Keyring
153+
raw_aes_keyring = create_keyring()
154+
155+
# 1b. Create a Raw AES Master Key Provider
156+
raw_aes_master_key_provider = create_key_provider()
157+
158+
# 2a. Encrypt EXAMPLE_DATA using Raw AES Keyring
159+
ciphertext_keyring, _ = client.encrypt(
160+
source=EXAMPLE_DATA,
161+
keyring=raw_aes_keyring,
162+
encryption_context=DEFAULT_ENCRYPTION_CONTEXT
163+
)
164+
165+
# 2b. Encrypt EXAMPLE_DATA using Raw AES Master Key Provider
166+
ciphertext_mkp, _ = client.encrypt(
167+
source=EXAMPLE_DATA,
168+
key_provider=raw_aes_master_key_provider,
169+
encryption_context=DEFAULT_ENCRYPTION_CONTEXT
170+
)
171+
172+
# Note: The ciphertexts obtained by encrypting EXAMPLE_DATA using keyring and MKP
173+
# (that is ciphertext_keyring and ciphertext_mkp) are not the same because the ESDK
174+
# generates different data keys each time for encryption of the data. But both
175+
# ciphertexts when decrypted using keyring and MKP will give the same plaintext result.
176+
177+
# 3. Decrypt the ciphertext_keyring using both the keyring and MKP and ensure the
178+
# resulting plaintext is the same and also equal to EXAMPLE_DATA
179+
decrypted_ciphertext_keyring_using_keyring, _ = client.decrypt(
180+
source=ciphertext_keyring,
181+
keyring=raw_aes_keyring
182+
)
183+
184+
decrypted_ciphertext_keyring_using_mkp, _ = client.decrypt(
185+
source=ciphertext_keyring,
186+
key_provider=raw_aes_master_key_provider
187+
)
188+
189+
assert decrypted_ciphertext_keyring_using_keyring == decrypted_ciphertext_keyring_using_mkp \
190+
and decrypted_ciphertext_keyring_using_keyring == EXAMPLE_DATA, \
191+
"Decrypted outputs using keyring and master key provider are not the same"
192+
193+
# 4. Decrypt the ciphertext_mkp using both the keyring and MKP and ensure the
194+
# resulting plaintext is the same and also equal to EXAMPLE_DATA
195+
decrypted_ciphertext_mkp_using_keyring, _ = client.decrypt(
196+
source=ciphertext_mkp,
197+
keyring=raw_aes_keyring
198+
)
199+
200+
decrypted_ciphertext_mkp_using_mkp, _ = client.decrypt(
201+
source=ciphertext_mkp,
202+
key_provider=raw_aes_master_key_provider
203+
)
204+
205+
assert decrypted_ciphertext_mkp_using_keyring == decrypted_ciphertext_mkp_using_mkp \
206+
and decrypted_ciphertext_mkp_using_keyring == EXAMPLE_DATA, \
207+
"Decrypted outputs using keyring and master key provider are not the same"

0 commit comments

Comments
 (0)