Skip to content

Commit 2a35192

Browse files
authored
chore(examples): Added file_streaming, migration and set_encryption_algorithm_suite examples (#676)
1 parent e47e441 commit 2a35192

20 files changed

+602
-64
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ __pycache__
3232
# PyTest
3333
.pytest_cache
3434
# Ignore key materials generated by examples or tests
35-
test_keys/
35+
test_keyrings/
3636

3737
# PyCharm
3838
.idea/

examples/src/keyrings/aws_kms_discovery_keyring_example.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,8 @@ def encrypt_and_decrypt_with_keyring(
172172

173173
# 10. Demonstrate that the decrypted plaintext is identical to the original plaintext.
174174
# (This is an example for demonstration; you do not need to do this in your own code.)
175-
assert plaintext_bytes == EXAMPLE_DATA
175+
assert plaintext_bytes == EXAMPLE_DATA, \
176+
"Decrypted plaintext should be identical to the original plaintext. Invalid decryption"
176177

177178
# 11. Demonstrate that if a discovery keyring (Bob's) doesn't have the correct AWS Account ID's,
178179
# the decrypt will fail with an error message

examples/src/keyrings/aws_kms_discovery_multi_keyring_example.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,4 +170,5 @@ def encrypt_and_decrypt_with_keyring(
170170

171171
# 10. Demonstrate that the decrypted plaintext is identical to the original plaintext.
172172
# (This is an example for demonstration; you do not need to do this in your own code.)
173-
assert plaintext_bytes == EXAMPLE_DATA
173+
assert plaintext_bytes == EXAMPLE_DATA, \
174+
"Decrypted plaintext should be identical to the original plaintext. Invalid decryption"

examples/src/keyrings/aws_kms_keyring_example.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,4 +116,5 @@ def encrypt_and_decrypt_with_keyring(
116116

117117
# 9. Demonstrate that the decrypted plaintext is identical to the original plaintext.
118118
# (This is an example for demonstration; you do not need to do this in your own code.)
119-
assert plaintext_bytes == EXAMPLE_DATA
119+
assert plaintext_bytes == EXAMPLE_DATA, \
120+
"Decrypted plaintext should be identical to the original plaintext. Invalid decryption"

examples/src/keyrings/aws_kms_mrk_keyring_example.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,4 +151,5 @@ def encrypt_and_decrypt_with_keyring(
151151

152152
# 9. Demonstrate that the decrypted plaintext is identical to the original plaintext.
153153
# (This is an example for demonstration; you do not need to do this in your own code.)
154-
assert plaintext_bytes == EXAMPLE_DATA
154+
assert plaintext_bytes == EXAMPLE_DATA, \
155+
"Decrypted plaintext should be identical to the original plaintext. Invalid decryption"

examples/src/keyrings/aws_kms_mrk_multi_keyring_example.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,8 @@ def encrypt_and_decrypt_with_keyring(
143143

144144
# 8. Demonstrate that the decrypted plaintext is identical to the original plaintext.
145145
# (This is an example for demonstration; you do not need to do this in your own code.)
146-
assert plaintext_bytes == EXAMPLE_DATA
146+
assert plaintext_bytes == EXAMPLE_DATA, \
147+
"Decrypted plaintext should be identical to the original plaintext. Invalid decryption"
147148

148149
# Demonstrate that a single AwsKmsMrkKeyring configured with a replica of the MRK from the
149150
# multi-keyring used to encrypt the data is also capable of decrypting the data.

examples/src/keyrings/aws_kms_multi_keyring_example.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,8 @@ def encrypt_and_decrypt_with_keyring(
144144

145145
# 6b. Demonstrate that the decrypted plaintext is identical to the original plaintext.
146146
# (This is an example for demonstration; you do not need to do this in your own code.)
147-
assert plaintext_bytes_multi_keyring == EXAMPLE_DATA
147+
assert plaintext_bytes_multi_keyring == EXAMPLE_DATA, \
148+
"Decrypted plaintext should be identical to the original plaintext. Invalid decryption"
148149

149150
# Because you used a multi_keyring on Encrypt, you can use either of the two
150151
# kms keyrings individually to decrypt the data.
@@ -174,7 +175,8 @@ def encrypt_and_decrypt_with_keyring(
174175

175176
# 7d. Demonstrate that the decrypted plaintext is identical to the original plaintext.
176177
# (This is an example for demonstration; you do not need to do this in your own code.)
177-
assert plaintext_bytes_default_region_kms_keyring == EXAMPLE_DATA
178+
assert plaintext_bytes_default_region_kms_keyring == EXAMPLE_DATA, \
179+
"Decrypted plaintext should be identical to the original plaintext. Invalid decryption"
178180

179181
# 8. Demonstrate that you can also successfully decrypt data using a KMS keyring with just the
180182
# `second_region_kms_key_id` directly.
@@ -201,4 +203,5 @@ def encrypt_and_decrypt_with_keyring(
201203

202204
# 8d. Demonstrate that the decrypted plaintext is identical to the original plaintext.
203205
# (This is an example for demonstration; you do not need to do this in your own code.)
204-
assert plaintext_bytes_second_region_kms_keyring == EXAMPLE_DATA
206+
assert plaintext_bytes_second_region_kms_keyring == EXAMPLE_DATA, \
207+
"Decrypted plaintext should be identical to the original plaintext. Invalid decryption"

examples/src/keyrings/aws_kms_rsa_keyring_example.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,4 +122,5 @@ def encrypt_and_decrypt_with_keyring(
122122

123123
# 9. Demonstrate that the decrypted plaintext is identical to the original plaintext.
124124
# (This is an example for demonstration; you do not need to do this in your own code.)
125-
assert plaintext_bytes == EXAMPLE_DATA
125+
assert plaintext_bytes == EXAMPLE_DATA, \
126+
"Decrypted plaintext should be identical to the original plaintext. Invalid decryption"
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
"""
4+
This example demonstrates file streaming for encryption and decryption.
5+
6+
File streaming is useful when the plaintext or ciphertext file/data is too large to load into
7+
memory. Therefore, the AWS Encryption SDK allows users to stream the data, instead of loading it
8+
all at once in memory. In this example, we demonstrate file streaming for encryption and decryption
9+
using a Raw AES keyring. However, you can use any keyring with streaming.
10+
11+
This example creates a Raw AES Keyring and then encrypts an input stream from the file
12+
`plaintext_filename` with an encryption context to an output (encrypted) file `ciphertext_filename`.
13+
It then decrypts the ciphertext from `ciphertext_filename` to a new file `decrypted_filename`.
14+
This example also includes some sanity checks for demonstration:
15+
1. Ciphertext and plaintext data are not the same
16+
2. Encryption context is correct in the decrypted message header
17+
3. Decrypted plaintext value matches EXAMPLE_DATA
18+
These sanity checks are for demonstration in the example only. You do not need these in your code.
19+
20+
For more information on how to use Raw AES keyrings, see
21+
https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-aes-keyring.html
22+
23+
See raw_aes_keyring_example.py in the same directory for another raw AES keyring example
24+
in the AWS Encryption SDK for Python.
25+
"""
26+
import filecmp
27+
import secrets
28+
import sys
29+
30+
from aws_cryptographic_materialproviders.mpl import AwsCryptographicMaterialProviders
31+
from aws_cryptographic_materialproviders.mpl.config import MaterialProvidersConfig
32+
from aws_cryptographic_materialproviders.mpl.models import AesWrappingAlg, CreateRawAesKeyringInput
33+
from aws_cryptographic_materialproviders.mpl.references import IKeyring
34+
from typing import Dict
35+
36+
import aws_encryption_sdk
37+
from aws_encryption_sdk import CommitmentPolicy
38+
39+
# TODO-MPL: Remove this as part of removing PYTHONPATH hacks.
40+
MODULE_ROOT_DIR = '/'.join(__file__.split("/")[:-1])
41+
42+
sys.path.append(MODULE_ROOT_DIR)
43+
44+
45+
def encrypt_and_decrypt_with_keyring(
46+
plaintext_filename: str,
47+
ciphertext_filename: str,
48+
decrypted_filename: str
49+
):
50+
"""Demonstrate a streaming encrypt/decrypt cycle.
51+
52+
Usage: encrypt_and_decrypt_with_keyring(plaintext_filename
53+
ciphertext_filename
54+
decrypted_filename)
55+
:param plaintext_filename: filename of the plaintext data
56+
:type plaintext_filename: string
57+
:param ciphertext_filename: filename of the ciphertext data
58+
:type ciphertext_filename: string
59+
:param decrypted_filename: filename of the decrypted data
60+
:type decrypted_filename: string
61+
"""
62+
# 1. Instantiate the encryption SDK client.
63+
# This builds the client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy,
64+
# which enforces that this client only encrypts using committing algorithm suites and enforces
65+
# that this client will only decrypt encrypted messages that were created with a committing
66+
# algorithm suite.
67+
# This is the default commitment policy if you were to build the client as
68+
# `client = aws_encryption_sdk.EncryptionSDKClient()`.
69+
client = aws_encryption_sdk.EncryptionSDKClient(
70+
commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT
71+
)
72+
73+
# 2. The key namespace and key name are defined by you.
74+
# and are used by the Raw AES keyring to determine
75+
# whether it should attempt to decrypt an encrypted data key.
76+
# For more information, see
77+
# https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-aes-keyring.html
78+
key_name_space = "Some managed raw keys"
79+
key_name = "My 256-bit AES wrapping key"
80+
81+
# 3. Create encryption context.
82+
# Remember that your encryption context is NOT SECRET.
83+
# For more information, see
84+
# https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context
85+
encryption_context: Dict[str, str] = {
86+
"encryption": "context",
87+
"is not": "secret",
88+
"but adds": "useful metadata",
89+
"that can help you": "be confident that",
90+
"the data you are handling": "is what you think it is",
91+
}
92+
93+
# 4. Generate a 256-bit AES key to use with your keyring.
94+
# In practice, you should get this key from a secure key management system such as an HSM.
95+
96+
# Here, the input to secrets.token_bytes() = 32 bytes = 256 bits
97+
static_key = secrets.token_bytes(32)
98+
99+
# 5. Create a Raw AES keyring
100+
# We choose to use a raw AES keyring, but any keyring can be used with streaming.
101+
mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders(
102+
config=MaterialProvidersConfig()
103+
)
104+
105+
keyring_input: CreateRawAesKeyringInput = CreateRawAesKeyringInput(
106+
key_namespace=key_name_space,
107+
key_name=key_name,
108+
wrapping_key=static_key,
109+
wrapping_alg=AesWrappingAlg.ALG_AES256_GCM_IV12_TAG16
110+
)
111+
112+
raw_aes_keyring: IKeyring = mat_prov.create_raw_aes_keyring(
113+
input=keyring_input
114+
)
115+
116+
# 6. Encrypt the data stream with the encryptionContext
117+
with open(plaintext_filename, 'rb') as pt_file, open(ciphertext_filename, 'wb') as ct_file:
118+
with client.stream(
119+
mode='e',
120+
source=pt_file,
121+
keyring=raw_aes_keyring,
122+
encryption_context=encryption_context
123+
) as encryptor:
124+
for chunk in encryptor:
125+
ct_file.write(chunk)
126+
127+
# 7. Demonstrate that the ciphertext and plaintext are different.
128+
# (This is an example for demonstration; you do not need to do this in your own code.)
129+
assert not filecmp.cmp(plaintext_filename, ciphertext_filename), \
130+
"Ciphertext and plaintext data are the same. Invalid encryption"
131+
132+
# 8. Decrypt your encrypted data stream using the same keyring you used on encrypt.
133+
with open(ciphertext_filename, 'rb') as ct_file, open(decrypted_filename, 'wb') as pt_file:
134+
with client.stream(
135+
mode='d',
136+
source=ct_file,
137+
keyring=raw_aes_keyring,
138+
encryption_context=encryption_context
139+
) as decryptor:
140+
for chunk in decryptor:
141+
pt_file.write(chunk)
142+
143+
# 9. Demonstrate that the encryption context is correct in the decrypted message header
144+
# (This is an example for demonstration; you do not need to do this in your own code.)
145+
for k, v in encryption_context.items():
146+
assert v == decryptor.header.encryption_context[k], \
147+
"Encryption context does not match expected values"
148+
149+
# 10. Demonstrate that the decrypted plaintext is identical to the original plaintext.
150+
# (This is an example for demonstration; you do not need to do this in your own code.)
151+
assert filecmp.cmp(plaintext_filename, decrypted_filename), \
152+
"Decrypted plaintext should be identical to the original plaintext. Invalid decryption"

examples/src/keyrings/hierarchical_keyring_example.py

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -101,13 +101,13 @@ def encrypt_and_decrypt_with_keyring(
101101
)
102102

103103
# 4. Call CreateKey to create two new active branch keys
104-
branch_key_id_A: str = keystore.create_key(input=CreateKeyInput()).branch_key_identifier
105-
branch_key_id_B: str = keystore.create_key(input=CreateKeyInput()).branch_key_identifier
104+
branch_key_id_a: str = keystore.create_key(input=CreateKeyInput()).branch_key_identifier
105+
branch_key_id_b: str = keystore.create_key(input=CreateKeyInput()).branch_key_identifier
106106

107107
# 5. Create a branch key supplier that maps the branch key id to a more readable format
108108
branch_key_id_supplier: IBranchKeyIdSupplier = ExampleBranchKeyIdSupplier(
109-
tenant_1_id=branch_key_id_A,
110-
tenant_2_id=branch_key_id_B,
109+
tenant_1_id=branch_key_id_a,
110+
tenant_2_id=branch_key_id_b,
111111
)
112112

113113
# 6. Create the Hierarchical Keyring.
@@ -135,7 +135,7 @@ def encrypt_and_decrypt_with_keyring(
135135
# be used to encrypt data.
136136

137137
# Create encryption context for TenantA
138-
encryption_context_A: Dict[str, str] = {
138+
encryption_context_a: Dict[str, str] = {
139139
"tenant": "TenantA",
140140
"encryption": "context",
141141
"is not": "secret",
@@ -145,7 +145,7 @@ def encrypt_and_decrypt_with_keyring(
145145
}
146146

147147
# Create encryption context for TenantB
148-
encryption_context_B: Dict[str, str] = {
148+
encryption_context_b: Dict[str, str] = {
149149
"tenant": "TenantB",
150150
"encryption": "context",
151151
"is not": "secret",
@@ -155,22 +155,22 @@ def encrypt_and_decrypt_with_keyring(
155155
}
156156

157157
# 8. Encrypt the data with encryptionContextA & encryptionContextB
158-
ciphertext_A, _ = client.encrypt(
158+
ciphertext_a, _ = client.encrypt(
159159
source=EXAMPLE_DATA,
160160
keyring=hierarchical_keyring,
161-
encryption_context=encryption_context_A
161+
encryption_context=encryption_context_a
162162
)
163-
ciphertext_B, _ = client.encrypt(
163+
ciphertext_b, _ = client.encrypt(
164164
source=EXAMPLE_DATA,
165165
keyring=hierarchical_keyring,
166-
encryption_context=encryption_context_B
166+
encryption_context=encryption_context_b
167167
)
168168

169169
# 9. To attest that TenantKeyB cannot decrypt a message written by TenantKeyA,
170170
# let's construct more restrictive hierarchical keyrings.
171-
keyring_input_A: CreateAwsKmsHierarchicalKeyringInput = CreateAwsKmsHierarchicalKeyringInput(
171+
keyring_input_a: CreateAwsKmsHierarchicalKeyringInput = CreateAwsKmsHierarchicalKeyringInput(
172172
key_store=keystore,
173-
branch_key_id=branch_key_id_A,
173+
branch_key_id=branch_key_id_a,
174174
ttl_seconds=600,
175175
cache=CacheTypeDefault(
176176
value=DefaultCache(
@@ -179,13 +179,13 @@ def encrypt_and_decrypt_with_keyring(
179179
),
180180
)
181181

182-
hierarchical_keyring_A: IKeyring = mat_prov.create_aws_kms_hierarchical_keyring(
183-
input=keyring_input_A
182+
hierarchical_keyring_a: IKeyring = mat_prov.create_aws_kms_hierarchical_keyring(
183+
input=keyring_input_a
184184
)
185185

186-
keyring_input_B: CreateAwsKmsHierarchicalKeyringInput = CreateAwsKmsHierarchicalKeyringInput(
186+
keyring_input_b: CreateAwsKmsHierarchicalKeyringInput = CreateAwsKmsHierarchicalKeyringInput(
187187
key_store=keystore,
188-
branch_key_id=branch_key_id_B,
188+
branch_key_id=branch_key_id_b,
189189
ttl_seconds=600,
190190
cache=CacheTypeDefault(
191191
value=DefaultCache(
@@ -194,8 +194,8 @@ def encrypt_and_decrypt_with_keyring(
194194
),
195195
)
196196

197-
hierarchical_keyring_B: IKeyring = mat_prov.create_aws_kms_hierarchical_keyring(
198-
input=keyring_input_B
197+
hierarchical_keyring_b: IKeyring = mat_prov.create_aws_kms_hierarchical_keyring(
198+
input=keyring_input_b
199199
)
200200

201201
# 10. Demonstrate that data encrypted by one tenant's key
@@ -205,8 +205,8 @@ def encrypt_and_decrypt_with_keyring(
205205
# This will fail and raise a AWSEncryptionSDKClientError, which we swallow ONLY for demonstration purposes.
206206
try:
207207
client.decrypt(
208-
source=ciphertext_A,
209-
keyring=hierarchical_keyring_B
208+
source=ciphertext_a,
209+
keyring=hierarchical_keyring_b
210210
)
211211
except AWSEncryptionSDKClientError:
212212
pass
@@ -215,21 +215,23 @@ def encrypt_and_decrypt_with_keyring(
215215
# This will fail and raise a AWSEncryptionSDKClientError, which we swallow ONLY for demonstration purposes.
216216
try:
217217
client.decrypt(
218-
source=ciphertext_B,
219-
keyring=hierarchical_keyring_A
218+
source=ciphertext_b,
219+
keyring=hierarchical_keyring_a
220220
)
221221
except AWSEncryptionSDKClientError:
222222
pass
223223

224224
# 10. Demonstrate that data encrypted by one tenant's branch key can be decrypted by that tenant,
225225
# and that the decrypted data matches the input data.
226-
plaintext_bytes_A, _ = client.decrypt(
227-
source=ciphertext_A,
228-
keyring=hierarchical_keyring_A
226+
plaintext_bytes_a, _ = client.decrypt(
227+
source=ciphertext_a,
228+
keyring=hierarchical_keyring_a
229229
)
230-
assert plaintext_bytes_A == EXAMPLE_DATA
231-
plaintext_bytes_B, _ = client.decrypt(
232-
source=ciphertext_B,
233-
keyring=hierarchical_keyring_B
230+
assert plaintext_bytes_a == EXAMPLE_DATA, \
231+
"Decrypted plaintext should be identical to the original plaintext. Invalid decryption"
232+
plaintext_bytes_b, _ = client.decrypt(
233+
source=ciphertext_b,
234+
keyring=hierarchical_keyring_b
234235
)
235-
assert plaintext_bytes_B == EXAMPLE_DATA
236+
assert plaintext_bytes_b == EXAMPLE_DATA, \
237+
"Decrypted plaintext should be identical to the original plaintext. Invalid decryption"

0 commit comments

Comments
 (0)