Skip to content

Commit 731c698

Browse files
committed
chore: fixed naming in test_i_multi_keyring_example.py, added aws_kms_multi_keyring_example.py
1 parent 41f901b commit 731c698

File tree

3 files changed

+216
-2
lines changed

3 files changed

+216
-2
lines changed
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
"""
4+
This example sets up the AWS KMS Multi Keyring made up of multiple AWS KMS Keyrings.
5+
6+
A multi-keyring is a keyring that consists of one or more individual keyrings of the
7+
same or a different type. The effect is like using several keyrings in a series.
8+
When you use a multi-keyring to encrypt data, any of the wrapping keys in any of its
9+
keyrings can decrypt that data.
10+
11+
When you create a multi-keyring to encrypt data, you designate one of the keyrings as
12+
the generator keyring. All other keyrings are known as child keyrings. The generator keyring
13+
generates and encrypts the plaintext data key. Then, all of the wrapping keys in all of the
14+
child keyrings encrypt the same plaintext data key. The multi-keyring returns the plaintext
15+
key and one encrypted data key for each wrapping key in the multi-keyring. If you create a
16+
multi-keyring with no generator keyring, you can use it to decrypt data, but not to encrypt.
17+
If the generator keyring is a KMS keyring, the generator key in the AWS KMS keyring generates
18+
and encrypts the plaintext key. Then, all additional AWS KMS keys in the AWS KMS keyring,
19+
and all wrapping keys in all child keyrings in the multi-keyring, encrypt the same plaintext key.
20+
21+
When decrypting, the AWS Encryption SDK uses the keyrings to try to decrypt one of the encrypted
22+
data keys. The keyrings are called in the order that they are specified in the multi-keyring.
23+
Processing stops as soon as any key in any keyring can decrypt an encrypted data key.
24+
25+
This example creates a Multi Keyring and then encrypts a custom input EXAMPLE_DATA
26+
with an encryption context. This example also includes some sanity checks for demonstration:
27+
1. Ciphertext and plaintext data are not the same
28+
2. Decryption of ciphertext is possible using the multi_keyring,
29+
and every one of the keyrings from the multi_keyring separately
30+
3. All decrypted plaintext value match EXAMPLE_DATA
31+
These sanity checks are for demonstration in the example only. You do not need these in your code.
32+
33+
This example creates a multi_keyring using a KMS keyring as generator keyring and
34+
another KMS keyring as a child keyring.
35+
36+
For more information on how to use Multi keyrings, see
37+
https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-multi-keyring.html
38+
"""
39+
import secrets
40+
import sys
41+
42+
import boto3
43+
from aws_cryptographic_materialproviders.mpl import AwsCryptographicMaterialProviders
44+
from aws_cryptographic_materialproviders.mpl.config import MaterialProvidersConfig
45+
from aws_cryptographic_materialproviders.mpl.models import (
46+
CreateAwsKmsKeyringInput,
47+
CreateAwsKmsMultiKeyringInput
48+
)
49+
from aws_cryptographic_materialproviders.mpl.references import IKeyring
50+
from typing import Dict
51+
52+
import aws_encryption_sdk
53+
from aws_encryption_sdk import CommitmentPolicy
54+
55+
# TODO-MPL: Remove this as part of removing PYTHONPATH hacks.
56+
MODULE_ROOT_DIR = '/'.join(__file__.split("/")[:-1])
57+
58+
sys.path.append(MODULE_ROOT_DIR)
59+
60+
EXAMPLE_DATA: bytes = b"Hello World"
61+
62+
63+
def encrypt_and_decrypt_with_keyring(
64+
default_region_kms_key_id: str,
65+
second_region_kms_key_id: str
66+
):
67+
"""Demonstrate an encrypt/decrypt cycle using an AWS KMS Multi keyring.
68+
The multi_keyring is created using a KMS keyring as generator keyring and another KMS keyring
69+
as a child keyring. For this example, `default_region_kms_key_id` is the generator key id
70+
for a KMS key located in your default region, and `second_region_kms_key_id` is the KMS key id
71+
for a KMS Key located in some second Region.
72+
73+
Usage: encrypt_and_decrypt_with_keyring(default_region_kms_key_id, second_region_kms_key_id)
74+
:param default_region_kms_key_id: KMS Key identifier for the default region KMS key you want to
75+
use as a generator keyring
76+
:type default_region_kms_key_id: string
77+
:param second_region_kms_key_id: KMS Key identifier for the second region KMS key you want to
78+
use as a child keyring
79+
:type second_region_kms_key_id: string
80+
81+
For more information on KMS Key identifiers, see
82+
https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id
83+
"""
84+
# 1. Instantiate the encryption SDK client.
85+
# This builds the client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy,
86+
# which enforces that this client only encrypts using committing algorithm suites and enforces
87+
# that this client will only decrypt encrypted messages that were created with a committing
88+
# algorithm suite.
89+
# This is the default commitment policy if you were to build the client as
90+
# `client = aws_encryption_sdk.EncryptionSDKClient()`.
91+
client = aws_encryption_sdk.EncryptionSDKClient(
92+
commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT
93+
)
94+
95+
# 2. Create encryption context.
96+
# Remember that your encryption context is NOT SECRET.
97+
# For more information, see
98+
# https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context
99+
encryption_context: Dict[str, str] = {
100+
"encryption": "context",
101+
"is not": "secret",
102+
"but adds": "useful metadata",
103+
"that can help you": "be confident that",
104+
"the data you are handling": "is what you think it is",
105+
}
106+
107+
# 3. Create an AwsKmsMultiKeyring that protects your data under two different KMS Keys.
108+
# Either KMS Key individually is capable of decrypting data encrypted under this Multi Keyring.
109+
mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders(
110+
config=MaterialProvidersConfig()
111+
)
112+
113+
kms_multi_keyring_input: CreateAwsKmsMultiKeyringInput = CreateAwsKmsMultiKeyringInput(
114+
generator=default_region_kms_key_id,
115+
kms_key_ids=[second_region_kms_key_id]
116+
)
117+
118+
kms_multi_keyring: IKeyring = mat_prov.create_aws_kms_multi_keyring(
119+
input=kms_multi_keyring_input
120+
)
121+
122+
# 4. Encrypt the data for the encryptionContext
123+
ciphertext, _ = client.encrypt(
124+
source=EXAMPLE_DATA,
125+
keyring=kms_multi_keyring,
126+
encryption_context=encryption_context
127+
)
128+
129+
# 5. Demonstrate that the ciphertext and plaintext are different.
130+
# (This is an example for demonstration; you do not need to do this in your own code.)
131+
assert ciphertext != EXAMPLE_DATA, \
132+
"Ciphertext and plaintext data are the same. Invalid encryption"
133+
134+
# 6a. Decrypt your encrypted data using the same multi_keyring you used on encrypt.
135+
plaintext_bytes_multi_keyring, _ = client.decrypt(
136+
source=ciphertext,
137+
keyring=kms_multi_keyring
138+
)
139+
140+
# 6b. Demonstrate that the decrypted plaintext is identical to the original plaintext.
141+
# (This is an example for demonstration; you do not need to do this in your own code.)
142+
assert plaintext_bytes_multi_keyring == EXAMPLE_DATA
143+
144+
# Because you used a multi_keyring on Encrypt, you can use either of the two
145+
# kms keyrings individually to decrypt the data.
146+
147+
# 7. Demonstrate that you can successfully decrypt data using a KMS keyring with just the
148+
# `default_region_kms_key_id` directly.
149+
150+
# 7a. Create a boto3 client for KMS for the default region.
151+
default_region_kms_client = boto3.client('kms', region_name="us-west-2")
152+
153+
# 7b. Create KMS keyring
154+
default_region_kms_keyring_input: CreateAwsKmsKeyringInput = CreateAwsKmsKeyringInput(
155+
kms_key_id=default_region_kms_key_id,
156+
kms_client=default_region_kms_client
157+
)
158+
159+
default_region_kms_keyring: IKeyring = mat_prov.create_aws_kms_keyring(
160+
input=default_region_kms_keyring_input
161+
)
162+
163+
# 7c. Decrypt your encrypted data using the default_region_kms_keyring.
164+
plaintext_bytes_default_region_kms_keyring, _ = client.decrypt(
165+
source=ciphertext,
166+
keyring=default_region_kms_keyring
167+
)
168+
169+
# 7d. Demonstrate that the decrypted plaintext is identical to the original plaintext.
170+
# (This is an example for demonstration; you do not need to do this in your own code.)
171+
assert plaintext_bytes_default_region_kms_keyring == EXAMPLE_DATA
172+
173+
# 8. Demonstrate that you can also successfully decrypt data using a KMS keyring with just the
174+
# `second_region_kms_key_id` directly.
175+
176+
# 8a. Create a boto3 client for KMS for the second region.
177+
second_region_kms_client = boto3.client('kms', region_name="eu-central-1")
178+
179+
# 8b. Create KMS keyring
180+
second_region_kms_keyring_input: CreateAwsKmsKeyringInput = CreateAwsKmsKeyringInput(
181+
kms_key_id=second_region_kms_key_id,
182+
kms_client=second_region_kms_client
183+
)
184+
185+
second_region_kms_keyring: IKeyring = mat_prov.create_aws_kms_keyring(
186+
input=second_region_kms_keyring_input
187+
)
188+
189+
# 8c. Decrypt your encrypted data using the second_region_kms_keyring.
190+
plaintext_bytes_second_region_kms_keyring, _ = client.decrypt(
191+
source=ciphertext,
192+
keyring=second_region_kms_keyring
193+
)
194+
195+
# 8d. Demonstrate that the decrypted plaintext is identical to the original plaintext.
196+
# (This is an example for demonstration; you do not need to do this in your own code.)
197+
assert plaintext_bytes_second_region_kms_keyring == EXAMPLE_DATA
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
"""Test suite for the AWS KMS multi keyring example."""
4+
import pytest
5+
6+
from ...src.keyrings.aws_kms_multi_keyring_example import encrypt_and_decrypt_with_keyring
7+
8+
pytestmark = [pytest.mark.examples]
9+
10+
11+
def test_encrypt_and_decrypt_with_keyring():
12+
"""Test function for encrypt and decrypt using the AWS KMS Multi Keyring example."""
13+
default_region_kms_key_id = \
14+
"arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f"
15+
second_region_kms_key_id = \
16+
"arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2"
17+
encrypt_and_decrypt_with_keyring(default_region_kms_key_id, second_region_kms_key_id)

examples/test/keyrings/test_i_multi_keyring_example.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@
1010

1111
def test_encrypt_and_decrypt_with_keyring():
1212
"""Test function for encrypt and decrypt using the Multi Keyring example."""
13-
kms_rsa_key_id = "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f"
14-
encrypt_and_decrypt_with_keyring(kms_rsa_key_id)
13+
kms_key_id = "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f"
14+
encrypt_and_decrypt_with_keyring(kms_key_id)

0 commit comments

Comments
 (0)