Skip to content

Commit cd2a171

Browse files
docs: add CMM examples (#239)
* docs: add CMM examples * docs: add CMM examples to readme * docs: apply suggestions from code review Co-Authored-By: Wesley Rosenblum <[email protected]> * docs: fix and refine examples docs * chore: remove redundant example * docs: clarify "filtering CMM" references in examples comments * chore: autoformat * docs: refactor algorithm restricting example focus * docs: revise framing of encryption context example and KMS references * docs: apply suggestions from code review Co-Authored-By: Wesley Rosenblum <[email protected]> Co-authored-by: Wesley Rosenblum <[email protected]>
1 parent 5ac45bf commit cd2a171

File tree

8 files changed

+416
-4
lines changed

8 files changed

+416
-4
lines changed

examples/README.md

+6
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ We start with AWS KMS examples, then show how to use other wrapping keys.
5959
* How to combine AWS KMS with an offline escrow key
6060
* [with keyrings](./src/keyring/multi/aws_kms_with_escrow.py)
6161
* [with master key providers](./src/master_key_provider/multi/aws_kms_with_escrow.py)
62+
* How to reuse data keys across multiple messages
63+
* [with the caching cryptographic materials manager](./src/crypto_materials_manager/caching/simple_cache.py)
64+
* How to restrict algorithm suites
65+
* [with a custom cryptographic materials manager](src/crypto_materials_manager/custom/algorithm_suite_enforcement.py)
66+
* How to require encryption context fields
67+
* [with a custom cryptographic materials manager](src/crypto_materials_manager/custom/requiring_encryption_context_fields.py)
6268

6369
### Keyrings
6470

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
"""
4+
Cryptographic materials manager examples.
5+
6+
These examples show how to create and use cryptographic materials managers.
7+
"""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
"""
4+
Caching cryptographic materials manager examples.
5+
6+
These examples show how to configure and use the caching cryptographic materials manager.
7+
"""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
"""
4+
The default cryptographic materials manager (CMM)
5+
creates new encryption and decryption materials
6+
on every call.
7+
This means every encrypted message is protected by a unique data key,
8+
but it also means that you need to interact with your key management system
9+
in order to process any message.
10+
If this causes performance, operations, or cost issues for you,
11+
you might benefit from data key caching.
12+
13+
https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/data-key-caching.html
14+
15+
This example shows how to configure the caching CMM
16+
to reuse data keys across multiple encrypted messages.
17+
18+
In this example, we use an AWS KMS customer master key (CMK),
19+
but you can use other key management options with the AWS Encryption SDK.
20+
For examples that demonstrate how to use other key management configurations,
21+
see the ``keyring`` and ``master_key_provider`` directories.
22+
23+
In this example, we use the one-step encrypt and decrypt APIs.
24+
"""
25+
import aws_encryption_sdk
26+
from aws_encryption_sdk.caches.local import LocalCryptoMaterialsCache
27+
from aws_encryption_sdk.keyrings.aws_kms import KmsKeyring
28+
from aws_encryption_sdk.materials_managers.caching import CachingCryptoMaterialsManager
29+
30+
31+
def run(aws_kms_cmk, source_plaintext):
32+
# type: (str, bytes) -> None
33+
"""Demonstrate an encrypt/decrypt cycle using the caching cryptographic materials manager.
34+
35+
:param str aws_kms_cmk: The ARN of an AWS KMS CMK that protects data keys
36+
:param bytes source_plaintext: Plaintext to encrypt
37+
"""
38+
# Prepare your encryption context.
39+
# https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context
40+
encryption_context = {
41+
"encryption": "context",
42+
"is not": "secret",
43+
"but adds": "useful metadata",
44+
"that can help you": "be confident that",
45+
"the data you are handling": "is what you think it is",
46+
}
47+
48+
# Create the keyring that determines how your data keys are protected.
49+
keyring = KmsKeyring(generator_key_id=aws_kms_cmk)
50+
51+
# Create the caching cryptographic materials manager using your keyring.
52+
cmm = CachingCryptoMaterialsManager(
53+
keyring=keyring,
54+
# The cache is where the caching CMM stores the materials.
55+
#
56+
# LocalCryptoMaterialsCache gives you a local, in-memory, cache.
57+
cache=LocalCryptoMaterialsCache(capacity=100),
58+
# max_age determines how long the caching CMM will reuse materials.
59+
#
60+
# This example uses two minutes.
61+
# In production, always choose as small a value as possible
62+
# that works for your requirements.
63+
max_age=120.0,
64+
# max_messages_encrypted determines how many messages
65+
# the caching CMM will protect with the same materials.
66+
#
67+
# In production, always choose as small a value as possible
68+
# that works for your requirements.
69+
max_messages_encrypted=10,
70+
)
71+
72+
# Encrypt your plaintext data.
73+
ciphertext, _encrypt_header = aws_encryption_sdk.encrypt(
74+
source=source_plaintext, encryption_context=encryption_context, materials_manager=cmm
75+
)
76+
77+
# Demonstrate that the ciphertext and plaintext are different.
78+
assert ciphertext != source_plaintext
79+
80+
# Decrypt your encrypted data using the same cryptographic materials manager you used on encrypt.
81+
#
82+
# You do not need to specify the encryption context on decrypt
83+
# because the header of the encrypted message includes the encryption context.
84+
decrypted, decrypt_header = aws_encryption_sdk.decrypt(source=ciphertext, materials_manager=cmm)
85+
86+
# Demonstrate that the decrypted plaintext is identical to the original plaintext.
87+
assert decrypted == source_plaintext
88+
89+
# Verify that the encryption context used in the decrypt operation includes
90+
# the encryption context that you specified when encrypting.
91+
# The AWS Encryption SDK can add pairs, so don't require an exact match.
92+
#
93+
# In production, always use a meaningful encryption context.
94+
assert set(encryption_context.items()) <= set(decrypt_header.encryption_context.items())
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
"""
4+
Custom cryptographic materials manager (CMM) examples.
5+
6+
The AWS Encryption SDK includes CMMs for common use cases,
7+
but you might need to do something else.
8+
9+
These examples show how you could create your own CMM for some specific requirements.
10+
"""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
"""
4+
The AWS Encryption SDK supports several different algorithm suites
5+
that offer different security properties.
6+
7+
https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/supported-algorithms.html
8+
9+
By default, the AWS Encryption SDK will let you use any of these,
10+
but you might want to restrict that further.
11+
12+
We recommend that you use the default algorithm suite,
13+
which uses AES-GCM with 256-bit keys, HKDF, and ECDSA message signing.
14+
If your readers and writers have the same permissions,
15+
you might want to omit the message signature for faster operation.
16+
For more information about choosing a signed or unsigned algorithm suite,
17+
see the AWS Encryption SDK developer guide:
18+
19+
https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/supported-algorithms.html#other-algorithms
20+
21+
This example shows how you can make a custom cryptographic materials manager (CMM)
22+
that only allows encrypt requests that either specify one of these two algorithm suites
23+
or do not specify an algorithm suite, in which case the default CMM uses the default algorithm suite.
24+
"""
25+
import aws_encryption_sdk
26+
from aws_encryption_sdk.identifiers import AlgorithmSuite
27+
from aws_encryption_sdk.keyrings.aws_kms import KmsKeyring
28+
from aws_encryption_sdk.keyrings.base import Keyring
29+
from aws_encryption_sdk.materials_managers import (
30+
DecryptionMaterials,
31+
DecryptionMaterialsRequest,
32+
EncryptionMaterials,
33+
EncryptionMaterialsRequest,
34+
)
35+
from aws_encryption_sdk.materials_managers.base import CryptoMaterialsManager
36+
from aws_encryption_sdk.materials_managers.default import DefaultCryptoMaterialsManager
37+
38+
39+
class UnapprovedAlgorithmSuite(Exception):
40+
"""Indicate that an unsupported algorithm suite was requested."""
41+
42+
43+
class RequireApprovedAlgorithmSuitesCryptoMaterialsManager(CryptoMaterialsManager):
44+
"""Only allow encryption requests for approved algorithm suites."""
45+
46+
def __init__(self, keyring):
47+
# type: (Keyring) -> None
48+
"""Set up the inner cryptographic materials manager using the provided keyring.
49+
50+
:param Keyring keyring: Keyring to use in the inner cryptographic materials manager
51+
"""
52+
self._allowed_algorithm_suites = {
53+
None, # no algorithm suite in the request
54+
AlgorithmSuite.AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384, # the default algorithm suite
55+
AlgorithmSuite.AES_256_GCM_IV12_TAG16_HKDF_SHA256, # the recommended unsigned algorithm suite
56+
}
57+
# Wrap the provided keyring in the default cryptographic materials manager (CMM).
58+
#
59+
# This is the same thing that the encrypt and decrypt APIs, as well as the caching CMM,
60+
# do if you provide a keyring instead of a CMM.
61+
self._cmm = DefaultCryptoMaterialsManager(keyring=keyring)
62+
63+
def get_encryption_materials(self, request):
64+
# type: (EncryptionMaterialsRequest) -> EncryptionMaterials
65+
"""Block any requests that include an unapproved algorithm suite."""
66+
if request.algorithm not in self._allowed_algorithm_suites:
67+
raise UnapprovedAlgorithmSuite("Unapproved algorithm suite requested!")
68+
69+
return self._cmm.get_encryption_materials(request)
70+
71+
def decrypt_materials(self, request):
72+
# type: (DecryptionMaterialsRequest) -> DecryptionMaterials
73+
"""Be more permissive on decrypt and just pass through."""
74+
return self._cmm.decrypt_materials(request)
75+
76+
77+
def run(aws_kms_cmk, source_plaintext):
78+
# type: (str, bytes) -> None
79+
"""Demonstrate an encrypt/decrypt cycle using a custom cryptographic materials manager that filters requests.
80+
81+
:param str aws_kms_cmk: The ARN of an AWS KMS CMK that protects data keys
82+
:param bytes source_plaintext: Plaintext to encrypt
83+
"""
84+
# Prepare your encryption context.
85+
# https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context
86+
encryption_context = {
87+
"encryption": "context",
88+
"is not": "secret",
89+
"but adds": "useful metadata",
90+
"that can help you": "be confident that",
91+
"the data you are handling": "is what you think it is",
92+
}
93+
94+
# Create the keyring that determines how your data keys are protected.
95+
keyring = KmsKeyring(generator_key_id=aws_kms_cmk)
96+
97+
# Create the algorithm suite restricting cryptographic materials manager using your keyring.
98+
cmm = RequireApprovedAlgorithmSuitesCryptoMaterialsManager(keyring=keyring)
99+
100+
# Demonstrate that the algorithm suite restricting CMM will not let you use an unapproved algorithm suite.
101+
try:
102+
aws_encryption_sdk.encrypt(
103+
source=source_plaintext,
104+
encryption_context=encryption_context,
105+
materials_manager=cmm,
106+
algorithm=AlgorithmSuite.AES_256_GCM_IV12_TAG16,
107+
)
108+
except UnapprovedAlgorithmSuite:
109+
# You asked for an unapproved algorithm suite.
110+
# Reaching this point means everything is working as expected.
111+
pass
112+
else:
113+
# The algorithm suite restricting CMM keeps this from happening.
114+
raise AssertionError("The algorithm suite restricting CMM does not let this happen!")
115+
116+
# Encrypt your plaintext data.
117+
ciphertext, _encrypt_header = aws_encryption_sdk.encrypt(
118+
source=source_plaintext, encryption_context=encryption_context, materials_manager=cmm
119+
)
120+
121+
# Demonstrate that the ciphertext and plaintext are different.
122+
assert ciphertext != source_plaintext
123+
124+
# Decrypt your encrypted data using the same cryptographic materials manager you used on encrypt.
125+
#
126+
# You do not need to specify the encryption context on decrypt
127+
# because the header of the encrypted message includes the encryption context.
128+
decrypted, decrypt_header = aws_encryption_sdk.decrypt(source=ciphertext, materials_manager=cmm)
129+
130+
# Demonstrate that the decrypted plaintext is identical to the original plaintext.
131+
assert decrypted == source_plaintext
132+
133+
# Verify that the encryption context used in the decrypt operation includes
134+
# the encryption context that you specified when encrypting.
135+
# The AWS Encryption SDK can add pairs, so don't require an exact match.
136+
#
137+
# In production, always use a meaningful encryption context.
138+
assert set(encryption_context.items()) <= set(decrypt_header.encryption_context.items())

0 commit comments

Comments
 (0)