Skip to content

Commit 027b127

Browse files
mattsb42-awsWesleyRosenblumjuneb
authored
docs: add example to replicate AWS KMS MKP behavior with AWS KMS keyring (#255)
* docs: add example to replicate AWS KMS MKP behavior with AWS KMS keyring * docs: thanks, grazie; fixing grammar * docs: update examples/src/keyring/aws_kms/act_like_aws_kms_master_key_provider.py Co-Authored-By: Wesley Rosenblum <55108558+WesleyRosenblum@users.noreply.github.com> * docs: update examples/src/keyring/aws_kms/act_like_aws_kms_master_key_provider.py * docs: reword behavior separation statement * docs: linting fix * docs: apply suggestions from code review Co-Authored-By: June Blender <juneb@users.noreply.github.com> * docs: s/replicate/reproduce/ * docs: keep naming consistent * docs: fix line length issue in docstring * docs: revise description of AWS KMS discovery decrypt behavior * docs: remove duplicate typo * docs: refine wording on discovery descriptions * docs: revise AWS KMS keyring-as-mkp example * show using multiple CMKs * demonstrate how the generating CMK is defined in both * explain that it does not matter if the CMK keyring fails * docs: break up long sentence * docs: update examples/src/keyring/aws_kms/act_like_aws_kms_master_key_provider.py Co-Authored-By: Wesley Rosenblum <55108558+WesleyRosenblum@users.noreply.github.com> Co-authored-by: Wesley Rosenblum <55108558+WesleyRosenblum@users.noreply.github.com> Co-authored-by: June Blender <juneb@users.noreply.github.com>
1 parent 6ffc135 commit 027b127

File tree

5 files changed

+139
-6
lines changed

5 files changed

+139
-6
lines changed

examples/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ We start with AWS KMS examples, then show how to use other wrapping keys.
4444
* [with keyrings](./src/keyring/aws_kms/discovery_decrypt_in_region_only.py)
4545
* How to decrypt with a preferred region but failover to others
4646
* [with keyrings](./src/keyring/aws_kms/discovery_decrypt_with_preferred_regions.py)
47+
* How to reproduce the behavior of an AWS KMS master key provider
48+
* [with keyrings](./src/keyring/aws_kms/act_like_aws_kms_master_key_provider.py)
4749
* Using raw wrapping keys
4850
* How to use a raw AES wrapping key
4951
* [with keyrings](./src/keyring/raw_aes/raw_aes.py)
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
"""
4+
You might have used master key providers to protect your data keys
5+
in an earlier version of the AWS Encryption SDK.
6+
This example shows how to configure a keyring that behaves like an AWS KMS master key provider.
7+
8+
The AWS Encryption SDK provided an AWS KMS master key provider for
9+
interacting with AWS Key Management Service (AWS KMS).
10+
On encrypt, the AWS KMS master key provider behaves like the AWS KMS keyring
11+
and encrypts with all CMKs that you identify.
12+
However, on decrypt,
13+
the AWS KMS master key provider reviews each encrypted data key (EDK).
14+
If the EDK was encrypted under an AWS KMS CMK,
15+
the AWS KMS master key provider attempts to decrypt it.
16+
Whether decryption succeeds depends on permissions on the CMK.
17+
This continues until the AWS KMS master key provider either runs out of EDKs
18+
or succeeds in decrypting an EDK.
19+
We have found that separating these two behaviors
20+
makes the expected behavior clearer,
21+
so that is what we did with the AWS KMS keyring and the AWS KMS discovery keyring.
22+
However, as you migrate from master key providers to keyrings,
23+
you might want a keyring that behaves like the AWS KMS master key provider.
24+
25+
For more examples of how to use the AWS KMS keyring,
26+
see the ``keyring/aws_kms`` directory.
27+
"""
28+
import aws_encryption_sdk
29+
from aws_encryption_sdk.key_providers.kms import KMSMasterKeyProvider
30+
from aws_encryption_sdk.keyrings.aws_kms import AwsKmsKeyring
31+
from aws_encryption_sdk.keyrings.multi import MultiKeyring
32+
33+
try: # Python 3.5.0 and 3.5.1 have incompatible typing modules
34+
from typing import Sequence # noqa pylint: disable=unused-import
35+
except ImportError: # pragma: no cover
36+
# We only actually need these imports when running the mypy checks
37+
pass
38+
39+
40+
def run(aws_kms_cmk, aws_kms_additional_cmks, source_plaintext):
41+
# type: (str, Sequence[str], bytes) -> None
42+
"""Demonstrate how to create a keyring that behaves like an AWS KMS master key provider.
43+
44+
:param str aws_kms_cmk: The ARN of an AWS KMS CMK that protects data keys
45+
:param List[str] aws_kms_additional_cmks: Additional ARNs of secondary AWS KMS CMKs
46+
:param bytes source_plaintext: Plaintext to encrypt
47+
"""
48+
# Prepare your encryption context.
49+
# https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context
50+
encryption_context = {
51+
"encryption": "context",
52+
"is not": "secret",
53+
"but adds": "useful metadata",
54+
"that can help you": "be confident that",
55+
"the data you are handling": "is what you think it is",
56+
}
57+
58+
# This is the master key provider whose behavior we want to reproduce.
59+
#
60+
# When encrypting, this master key provider generates the data key using the first CMK in the list
61+
# and encrypts the data key using all specified CMKs.
62+
# However, when decrypting, this master key provider attempts to decrypt
63+
# any data keys that were encrypted under an AWS KMS CMK.
64+
master_key_provider_cmks = [aws_kms_cmk] + aws_kms_additional_cmks
65+
_master_key_provider_to_replicate = KMSMasterKeyProvider( # noqa: intentionally never used
66+
key_ids=master_key_provider_cmks,
67+
)
68+
69+
# Create a CMK keyring that encrypts and decrypts using the specified AWS KMS CMKs.
70+
#
71+
# This keyring reproduces the encryption behavior of the AWS KMS master key provider.
72+
#
73+
# The AWS KMS keyring requires that you explicitly identify the CMK
74+
# that you want the keyring to use to generate the data key.
75+
cmk_keyring = AwsKmsKeyring(generator_key_id=aws_kms_cmk, key_ids=aws_kms_additional_cmks)
76+
77+
# Create an AWS KMS discovery keyring that will attempt to decrypt
78+
# any data keys that were encrypted under an AWS KMS CMK.
79+
discovery_keyring = AwsKmsKeyring(is_discovery=True)
80+
81+
# Combine the single-CMK and discovery keyrings
82+
# to create a keyring that behaves like an AWS KMS master key provider.
83+
#
84+
# The CMK keyring reproduces the encryption behavior
85+
# and the discovery keyring reproduces the decryption behavior.
86+
# This also means that it does not matter if the CMK keyring fails to decrypt.
87+
# For example, if you configured the CMK keyring with aliases,
88+
# it works on encrypt but fails to match any encrypted data keys on decrypt
89+
# because the serialized key name is the resulting CMK ARN rather than the alias name.
90+
# However, because the discovery keyring attempts to decrypt any AWS KMS-encrypted
91+
# data keys that it finds, the message still decrypts successfully.
92+
keyring = MultiKeyring(generator=cmk_keyring, children=[discovery_keyring])
93+
94+
# Encrypt your plaintext data.
95+
ciphertext, _encrypt_header = aws_encryption_sdk.encrypt(
96+
source=source_plaintext, encryption_context=encryption_context, keyring=keyring
97+
)
98+
99+
# Demonstrate that the ciphertext and plaintext are different.
100+
assert ciphertext != source_plaintext
101+
102+
# Decrypt your encrypted data using the same keyring you used on encrypt.
103+
#
104+
# You do not need to specify the encryption context on decrypt
105+
# because the header of the encrypted message includes the encryption context.
106+
decrypted, decrypt_header = aws_encryption_sdk.decrypt(source=ciphertext, keyring=keyring)
107+
108+
# Demonstrate that the decrypted plaintext is identical to the original plaintext.
109+
assert decrypted == source_plaintext
110+
111+
# Verify that the encryption context used in the decrypt operation includes
112+
# the encryption context that you specified when encrypting.
113+
# The AWS Encryption SDK can add pairs, so don't require an exact match.
114+
#
115+
# In production, always use a meaningful encryption context.
116+
assert set(encryption_context.items()) <= set(decrypt_header.encryption_context.items())

examples/src/keyring/aws_kms/discovery_decrypt.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,13 @@
66
However, sometimes you need more flexibility on decrypt,
77
especially when you don't know which CMKs were used to encrypt a message.
88
To address this need, you can use an AWS KMS discovery keyring.
9-
The AWS KMS discovery keyring does nothing on encrypt,
10-
but attempts to decrypt *any* data keys that were encrypted under an AWS KMS CMK.
9+
The AWS KMS discovery keyring does nothing on encrypt.
10+
On decrypt it reviews each encrypted data key (EDK).
11+
If an EDK was encrypted under an AWS KMS CMK,
12+
the AWS KMS discovery keyring attempts to decrypt it.
13+
Whether decryption succeeds depends on permissions on the CMK.
14+
This continues until the AWS KMS discovery keyring either runs out of EDKs
15+
or succeeds in decrypting an EDK.
1116
1217
This example shows how to configure and use an AWS KMS discovery keyring.
1318

examples/src/keyring/aws_kms/discovery_decrypt_in_region_only.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,13 @@
66
However, sometimes you need more flexibility on decrypt,
77
especially when you don't know which CMKs were used to encrypt a message.
88
To address this need, you can use an AWS KMS discovery keyring.
9-
The AWS KMS discovery keyring does nothing on encrypt,
10-
but attempts to decrypt *any* data keys that were encrypted under an AWS KMS CMK.
9+
The AWS KMS discovery keyring does nothing on encrypt.
10+
On decrypt it reviews each encrypted data key (EDK).
11+
If an EDK was encrypted under an AWS KMS CMK,
12+
the AWS KMS discovery keyring attempts to decrypt it.
13+
Whether decryption succeeds depends on permissions on the CMK.
14+
This continues until the AWS KMS discovery keyring either runs out of EDKs
15+
or succeeds in decrypting an EDK.
1116
1217
However, sometimes you need to be a *bit* more restrictive than that.
1318
To address this need, you can use a client supplier that restricts the regions an AWS KMS keyring can talk to.

examples/src/keyring/aws_kms/discovery_decrypt_with_preferred_regions.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,13 @@
66
However, sometimes you need more flexibility on decrypt,
77
especially when you don't know which CMKs were used to encrypt a message.
88
To address this need, you can use an AWS KMS discovery keyring.
9-
The AWS KMS discovery keyring does nothing on encrypt,
10-
but attempts to decrypt *any* data keys that were encrypted under an AWS KMS CMK.
9+
The AWS KMS discovery keyring does nothing on encrypt.
10+
On decrypt it reviews each encrypted data key (EDK).
11+
If an EDK was encrypted under an AWS KMS CMK,
12+
the AWS KMS discovery keyring attempts to decrypt it.
13+
Whether decryption succeeds depends on permissions on the CMK.
14+
This continues until the AWS KMS discovery keyring either runs out of EDKs
15+
or succeeds in decrypting an EDK.
1116
1217
However, sometimes you need to be a *bit* more restrictive than that.
1318
To address this need, you can use a client supplier to restrict what regions an AWS KMS keyring can talk to.

0 commit comments

Comments
 (0)