Skip to content

Commit 52ad465

Browse files
authored
chore(examples): Added mrk and mrk_multi keyring examples (#673)
1 parent 428fe95 commit 52ad465

File tree

4 files changed

+382
-0
lines changed

4 files changed

+382
-0
lines changed
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
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 MRK (multi-region key) Keyring
5+
6+
The AWS Key Management Service (AWS KMS) MRK keyring interacts with AWS KMS to
7+
create, encrypt, and decrypt data keys with multi-region AWS KMS keys (MRKs).
8+
This example creates a KMS MRK Keyring and then encrypts a custom input EXAMPLE_DATA
9+
with an encryption context. This example also includes some sanity checks for demonstration:
10+
1. Ciphertext and plaintext data are not the same
11+
2. Encryption context is correct in the decrypted message header
12+
3. Decrypted plaintext value matches EXAMPLE_DATA
13+
These sanity checks are for demonstration in the example only. You do not need these in your code.
14+
15+
AWS KMS MRK keyrings can be used independently or in a multi-keyring with other keyrings
16+
of the same or a different type.
17+
18+
For more information on how to use KMS keyrings, see
19+
https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html
20+
21+
For more info on KMS MRK (multi-region keys), see the KMS documentation:
22+
https://docs.aws.amazon.com/kms/latest/developerguide/multi-region-keys-overview.html
23+
"""
24+
import sys
25+
26+
import boto3
27+
from aws_cryptographic_materialproviders.mpl import AwsCryptographicMaterialProviders
28+
from aws_cryptographic_materialproviders.mpl.config import MaterialProvidersConfig
29+
from aws_cryptographic_materialproviders.mpl.models import CreateAwsKmsMrkKeyringInput
30+
from aws_cryptographic_materialproviders.mpl.references import IKeyring
31+
from typing import Dict
32+
33+
import aws_encryption_sdk
34+
from aws_encryption_sdk import CommitmentPolicy
35+
36+
# TODO-MPL: Remove this as part of removing PYTHONPATH hacks.
37+
MODULE_ROOT_DIR = '/'.join(__file__.split("/")[:-1])
38+
39+
sys.path.append(MODULE_ROOT_DIR)
40+
41+
EXAMPLE_DATA: bytes = b"Hello World"
42+
43+
44+
def encrypt_and_decrypt_with_keyring(
45+
mrk_key_id_encrypt: str,
46+
mrk_replica_key_id_decrypt: str,
47+
mrk_encrypt_region: str,
48+
mrk_replica_decrypt_region: str
49+
):
50+
"""Demonstrate an encrypt/decrypt cycle using an AWS KMS MRK keyring.
51+
52+
Usage: encrypt_and_decrypt_with_keyring(mrk_key_id_encrypt,
53+
mrk_replica_key_id_decrypt,
54+
mrk_encrypt_region,
55+
mrk_replica_decrypt_region)
56+
:param mrk_key_id_encrypt: KMS Key identifier for the KMS key located in your
57+
default region, which you want to use for encryption of your data keys
58+
:type mrk_key_id_encrypt: string
59+
:param mrk_replica_key_id_decrypt: KMS Key identifier for the KMS key
60+
that is a replica of the `mrk_key_id_encrypt` in a second region, which you
61+
want to use for decryption of your data keys
62+
:type mrk_replica_key_id_decrypt: string
63+
:param mrk_encrypt_region: AWS Region for encryption of your data keys. This should
64+
be the region of the mrk_key_id_encrypt.
65+
:type mrk_encrypt_region: string
66+
:param mrk_replica_decrypt_region: AWS Region for decryption of your data keys. This should
67+
be the region of the mrk_replica_key_id_decrypt.
68+
:type mrk_replica_decrypt_region: string
69+
70+
For more information on KMS Key identifiers for multi-region keys, see
71+
https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id
72+
"""
73+
# 1. Instantiate the encryption SDK client.
74+
# This builds the client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy,
75+
# which enforces that this client only encrypts using committing algorithm suites and enforces
76+
# that this client will only decrypt encrypted messages that were created with a committing
77+
# algorithm suite.
78+
# This is the default commitment policy if you were to build the client as
79+
# `client = aws_encryption_sdk.EncryptionSDKClient()`.
80+
client = aws_encryption_sdk.EncryptionSDKClient(
81+
commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT
82+
)
83+
84+
# 2. Create encryption context.
85+
# Remember that your encryption context is NOT SECRET.
86+
# For more information, see
87+
# https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context
88+
encryption_context: Dict[str, str] = {
89+
"encryption": "context",
90+
"is not": "secret",
91+
"but adds": "useful metadata",
92+
"that can help you": "be confident that",
93+
"the data you are handling": "is what you think it is",
94+
}
95+
96+
# 3. Create a keyring that will encrypt your data, using a KMS MRK in the first region.
97+
mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders(
98+
config=MaterialProvidersConfig()
99+
)
100+
101+
# Create a boto3 client for KMS in the first region.
102+
encrypt_kms_client = boto3.client('kms', region_name=mrk_encrypt_region)
103+
104+
encrypt_keyring_input: CreateAwsKmsMrkKeyringInput = CreateAwsKmsMrkKeyringInput(
105+
kms_key_id=mrk_key_id_encrypt,
106+
kms_client=encrypt_kms_client
107+
)
108+
109+
encrypt_keyring: IKeyring = mat_prov.create_aws_kms_mrk_keyring(
110+
input=encrypt_keyring_input
111+
)
112+
113+
# 4. Encrypt the data with the encryptionContext using the encrypt_keyring.
114+
ciphertext, _ = client.encrypt(
115+
source=EXAMPLE_DATA,
116+
keyring=encrypt_keyring,
117+
encryption_context=encryption_context
118+
)
119+
120+
# 5. Demonstrate that the ciphertext and plaintext are different.
121+
# (This is an example for demonstration; you do not need to do this in your own code.)
122+
assert ciphertext != EXAMPLE_DATA, \
123+
"Ciphertext and plaintext data are the same. Invalid encryption"
124+
125+
# 6. Create a keyring that will decrypt your data, using the same KMS MRK replicated
126+
# to the second region. This example assumes you have already replicated your key
127+
128+
# Create a boto3 client for KMS in the second region.
129+
decrypt_kms_client = boto3.client('kms', region_name=mrk_replica_decrypt_region)
130+
131+
decrypt_keyring_input: CreateAwsKmsMrkKeyringInput = CreateAwsKmsMrkKeyringInput(
132+
kms_key_id=mrk_replica_key_id_decrypt,
133+
kms_client=decrypt_kms_client
134+
)
135+
136+
decrypt_keyring: IKeyring = mat_prov.create_aws_kms_mrk_keyring(
137+
input=decrypt_keyring_input
138+
)
139+
140+
# 7. Decrypt your encrypted data using the same keyring you used on encrypt.
141+
plaintext_bytes, dec_header = client.decrypt(
142+
source=ciphertext,
143+
keyring=decrypt_keyring
144+
)
145+
146+
# 8. Demonstrate that the encryption context is correct in the decrypted message header
147+
# (This is an example for demonstration; you do not need to do this in your own code.)
148+
for k, v in encryption_context.items():
149+
assert v == dec_header.encryption_context[k], \
150+
"Encryption context does not match expected values"
151+
152+
# 9. Demonstrate that the decrypted plaintext is identical to the original plaintext.
153+
# (This is an example for demonstration; you do not need to do this in your own code.)
154+
assert plaintext_bytes == EXAMPLE_DATA
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
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 MRK Multi Keyring
5+
6+
The AWS Key Management Service (AWS KMS) MRK keyring interacts with AWS KMS to
7+
create, encrypt, and decrypt data keys with AWS KMS MRK keys.
8+
The KMS MRK multi-keyring consists of one or more individual keyrings of the
9+
same or different type. The keys can either be regular KMS keys or MRKs.
10+
The effect is like using several keyrings in a series.
11+
12+
This example creates a AwsKmsMrkMultiKeyring using an mrk_key_id (generator) and a kms_key_id
13+
as a child key, and then encrypts a custom input EXAMPLE_DATA with an encryption context.
14+
Either KMS Key individually is capable of decrypting data encrypted under this keyring.
15+
This example also includes some sanity checks for demonstration:
16+
1. Ciphertext and plaintext data are not the same
17+
2. Encryption context is correct in the decrypted message header
18+
3. Decrypted plaintext value matches EXAMPLE_DATA
19+
4. Ciphertext can be decrypted using an AwsKmsMrkKeyring containing a replica of the
20+
MRK (from the multi-keyring used for encryption) copied from the first region into
21+
the second region
22+
These sanity checks are for demonstration in the example only. You do not need these in your code.
23+
24+
For more information on how to use KMS keyrings, see
25+
https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html
26+
27+
For more info on KMS MRK (multi-region keys), see the KMS documentation:
28+
https://docs.aws.amazon.com/kms/latest/developerguide/multi-region-keys-overview.html
29+
"""
30+
import sys
31+
32+
import boto3
33+
from aws_cryptographic_materialproviders.mpl import AwsCryptographicMaterialProviders
34+
from aws_cryptographic_materialproviders.mpl.config import MaterialProvidersConfig
35+
from aws_cryptographic_materialproviders.mpl.models import CreateAwsKmsMrkKeyringInput, CreateAwsKmsMrkMultiKeyringInput
36+
from aws_cryptographic_materialproviders.mpl.references import IKeyring
37+
from typing import Dict
38+
39+
import aws_encryption_sdk
40+
from aws_encryption_sdk import CommitmentPolicy
41+
42+
# TODO-MPL: Remove this as part of removing PYTHONPATH hacks.
43+
MODULE_ROOT_DIR = '/'.join(__file__.split("/")[:-1])
44+
45+
sys.path.append(MODULE_ROOT_DIR)
46+
47+
EXAMPLE_DATA: bytes = b"Hello World"
48+
49+
50+
def encrypt_and_decrypt_with_keyring(
51+
mrk_key_id: str,
52+
kms_key_id: str,
53+
mrk_replica_key_id: str,
54+
mrk_replica_decrypt_region: str
55+
):
56+
"""Demonstrate an encrypt/decrypt cycle using a Multi-Keyring made
57+
up of multiple AWS KMS MRK Keyrings
58+
59+
Usage: encrypt_and_decrypt_with_keyring(mrk_key_id,
60+
kms_key_id,
61+
mrk_replica_key_id,
62+
mrk_replica_decrypt_region)
63+
:param mrk_key_id: KMS Key identifier for an AWS KMS multi-region key (MRK) located in your
64+
default region
65+
:type mrk_key_id: string
66+
:param kms_key_id: KMS Key identifier for a KMS key, possibly located in a different region
67+
than the MRK
68+
:type kms_key_id: string
69+
:param mrk_replica_key_id: KMS Key identifier for an MRK that is a replica of the
70+
`mrk_key_id` in a second region.
71+
:type mrk_replica_key_id: string
72+
:param mrk_replica_decrypt_region: The second region where the MRK replica is located
73+
:type mrk_replica_decrypt_region: string
74+
75+
For more information on KMS Key identifiers for multi-region keys, see
76+
https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id
77+
"""
78+
# 1. Instantiate the encryption SDK client.
79+
# This builds the client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy,
80+
# which enforces that this client only encrypts using committing algorithm suites and enforces
81+
# that this client will only decrypt encrypted messages that were created with a committing
82+
# algorithm suite.
83+
# This is the default commitment policy if you were to build the client as
84+
# `client = aws_encryption_sdk.EncryptionSDKClient()`.
85+
client = aws_encryption_sdk.EncryptionSDKClient(
86+
commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT
87+
)
88+
89+
# 2. Create encryption context.
90+
# Remember that your encryption context is NOT SECRET.
91+
# For more information, see
92+
# https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context
93+
encryption_context: Dict[str, str] = {
94+
"encryption": "context",
95+
"is not": "secret",
96+
"but adds": "useful metadata",
97+
"that can help you": "be confident that",
98+
"the data you are handling": "is what you think it is",
99+
}
100+
101+
# 3. Create an AwsKmsMrkMultiKeyring that protects your data under two different KMS Keys.
102+
# The Keys can either be regular KMS keys or MRKs.
103+
# Either KMS Key individually is capable of decrypting data encrypted under this keyring.
104+
mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders(
105+
config=MaterialProvidersConfig()
106+
)
107+
108+
kms_mrk_multi_keyring_input: CreateAwsKmsMrkMultiKeyringInput =\
109+
CreateAwsKmsMrkMultiKeyringInput(
110+
generator=mrk_key_id,
111+
kms_key_ids=[kms_key_id]
112+
)
113+
114+
kms_mrk_multi_keyring: IKeyring = mat_prov.create_aws_kms_mrk_multi_keyring(
115+
input=kms_mrk_multi_keyring_input
116+
)
117+
118+
# 4. Encrypt the data with the encryptionContext using the kms_mrk_multi_keyring.
119+
ciphertext, _ = client.encrypt(
120+
source=EXAMPLE_DATA,
121+
keyring=kms_mrk_multi_keyring,
122+
encryption_context=encryption_context
123+
)
124+
125+
# 5. Demonstrate that the ciphertext and plaintext are different.
126+
# (This is an example for demonstration; you do not need to do this in your own code.)
127+
assert ciphertext != EXAMPLE_DATA, \
128+
"Ciphertext and plaintext data are the same. Invalid encryption"
129+
130+
# 6. Decrypt your encrypted data using the same AwsKmsMrkMultiKeyring you used on encrypt.
131+
# It will decrypt the data using the generator key (in this case, the MRK), since that is
132+
# the first available KMS key on the keyring that is capable of decrypting the data.
133+
plaintext_bytes, dec_header = client.decrypt(
134+
source=ciphertext,
135+
keyring=kms_mrk_multi_keyring
136+
)
137+
138+
# 7. Demonstrate that the encryption context is correct in the decrypted message header
139+
# (This is an example for demonstration; you do not need to do this in your own code.)
140+
for k, v in encryption_context.items():
141+
assert v == dec_header.encryption_context[k], \
142+
"Encryption context does not match expected values"
143+
144+
# 8. Demonstrate that the decrypted plaintext is identical to the original plaintext.
145+
# (This is an example for demonstration; you do not need to do this in your own code.)
146+
assert plaintext_bytes == EXAMPLE_DATA
147+
148+
# Demonstrate that a single AwsKmsMrkKeyring configured with a replica of the MRK from the
149+
# multi-keyring used to encrypt the data is also capable of decrypting the data.
150+
# (This is an example for demonstration; you do not need to do this in your own code.)
151+
152+
# 9. Create a single AwsKmsMrkKeyring with the replica KMS MRK from the second region.
153+
154+
# Create a boto3 client for KMS in the second region which is the region for mrk_replica_key_id.
155+
second_region_kms_client = boto3.client('kms', region_name=mrk_replica_decrypt_region)
156+
157+
second_region_mrk_keyring_input: CreateAwsKmsMrkKeyringInput = CreateAwsKmsMrkKeyringInput(
158+
kms_key_id=mrk_replica_key_id,
159+
kms_client=second_region_kms_client
160+
)
161+
162+
second_region_mrk_keyring: IKeyring = mat_prov.create_aws_kms_mrk_keyring(
163+
input=second_region_mrk_keyring_input
164+
)
165+
166+
# 10. Decrypt your encrypted data using the second region AwsKmsMrkKeyring
167+
plaintext_bytes_second_region, dec_header_second_region = client.decrypt(
168+
source=ciphertext,
169+
keyring=second_region_mrk_keyring
170+
)
171+
172+
# 11. Demonstrate that the encryption context is correct in the decrypted message header
173+
# (This is an example for demonstration; you do not need to do this in your own code.)
174+
for k, v in encryption_context.items():
175+
assert v == dec_header_second_region.encryption_context[k], \
176+
"Encryption context does not match expected values"
177+
178+
# 12. Demonstrate that the decrypted plaintext is identical to the original plaintext.
179+
# (This is an example for demonstration; you do not need to do this in your own code.)
180+
assert plaintext_bytes_second_region == EXAMPLE_DATA
181+
182+
# Not shown in this example: A KMS Keyring created with `kms_key_id` could also
183+
# decrypt this message.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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 MRK keyring example."""
4+
import pytest
5+
6+
from ...src.keyrings.aws_kms_mrk_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 MRK Keyring example."""
13+
mrk_key_id_encrypt = \
14+
"arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7"
15+
mrk_replica_key_id_decrypt = \
16+
"arn:aws:kms:eu-west-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7"
17+
mrk_encrypt_region = "us-east-1"
18+
mrk_replica_decrypt_region = "eu-west-1"
19+
encrypt_and_decrypt_with_keyring(mrk_key_id_encrypt,
20+
mrk_replica_key_id_decrypt,
21+
mrk_encrypt_region,
22+
mrk_replica_decrypt_region)
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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 MRK Multi keyring example."""
4+
import pytest
5+
6+
from ...src.keyrings.aws_kms_mrk_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 MRK Multi Keyring example."""
13+
mrk_key_id = \
14+
"arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7"
15+
kms_key_id = \
16+
"arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f"
17+
mrk_replica_key_id = \
18+
"arn:aws:kms:eu-west-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7"
19+
mrk_replica_decrypt_region = "eu-west-1"
20+
encrypt_and_decrypt_with_keyring(mrk_key_id,
21+
kms_key_id,
22+
mrk_replica_key_id,
23+
mrk_replica_decrypt_region)

0 commit comments

Comments
 (0)