Skip to content

Commit 385edf2

Browse files
committed
added file streaming example; updated assertion error message in all keyrings; fixed nomenclature in hierarchical and requiredec keyring examples
1 parent 048a799 commit 385edf2

22 files changed

+382
-72
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@ __pycache__
3232
# PyTest
3333
.pytest_cache
3434
# Ignore key materials generated by examples or tests
35-
test_keys/
35+
test_keyrings/user_public_key_file_name.pem
36+
test_keyrings/user_private_key_file_name.pem
37+
test_keyrings/my-encrypted-data.ct
38+
test_keyrings/my-decrypted-data.dat
3639

3740
# PyCharm
3841
.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: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
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 using a Raw AES keyring
5+
6+
The Raw AES keyring lets you use an AES symmetric key that you provide as a wrapping key that
7+
protects your data key. You need to generate, store, and protect the key material,
8+
preferably in a hardware security module (HSM) or key management system. Use a Raw AES keyring
9+
when you need to provide the wrapping key and encrypt the data keys locally or offline.
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 `new_plaintext_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+
The Raw AES keyring encrypts data by using the AES-GCM algorithm and a wrapping key that
21+
you specify as a byte array. You can specify only one wrapping key in each Raw AES keyring,
22+
but you can include multiple Raw AES keyrings, alone or with other keyrings, in a multi-keyring.
23+
24+
For more information on how to use Raw AES keyrings, see
25+
https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-aes-keyring.html
26+
"""
27+
import secrets
28+
import sys
29+
import filecmp
30+
31+
from aws_cryptographic_materialproviders.mpl import AwsCryptographicMaterialProviders
32+
from aws_cryptographic_materialproviders.mpl.config import MaterialProvidersConfig
33+
from aws_cryptographic_materialproviders.mpl.models import AesWrappingAlg, CreateRawAesKeyringInput
34+
from aws_cryptographic_materialproviders.mpl.references import IKeyring
35+
from typing import Dict
36+
37+
import aws_encryption_sdk
38+
from aws_encryption_sdk import CommitmentPolicy
39+
40+
# TODO-MPL: Remove this as part of removing PYTHONPATH hacks.
41+
MODULE_ROOT_DIR = '/'.join(__file__.split("/")[:-1])
42+
43+
sys.path.append(MODULE_ROOT_DIR)
44+
45+
46+
def encrypt_and_decrypt_with_keyring(
47+
plaintext_filename: str,
48+
ciphertext_filename: str,
49+
new_plaintext_filename: str
50+
):
51+
"""Demonstrate a streaming encrypt/decrypt cycle using a Raw AES keyring.
52+
53+
Usage: encrypt_and_decrypt_with_keyring()
54+
"""
55+
# 1. Instantiate the encryption SDK client.
56+
# This builds the client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy,
57+
# which enforces that this client only encrypts using committing algorithm suites and enforces
58+
# that this client will only decrypt encrypted messages that were created with a committing
59+
# algorithm suite.
60+
# This is the default commitment policy if you were to build the client as
61+
# `client = aws_encryption_sdk.EncryptionSDKClient()`.
62+
client = aws_encryption_sdk.EncryptionSDKClient(
63+
commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT
64+
)
65+
66+
# 2. The key namespace and key name are defined by you.
67+
# and are used by the Raw AES keyring to determine
68+
# whether it should attempt to decrypt an encrypted data key.
69+
# For more information, see
70+
# https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-aes-keyring.html
71+
key_name_space = "Some managed raw keys"
72+
key_name = "My 256-bit AES wrapping key"
73+
74+
# 3. Create encryption context.
75+
# Remember that your encryption context is NOT SECRET.
76+
# For more information, see
77+
# https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context
78+
encryption_context: Dict[str, str] = {
79+
"encryption": "context",
80+
"is not": "secret",
81+
"but adds": "useful metadata",
82+
"that can help you": "be confident that",
83+
"the data you are handling": "is what you think it is",
84+
}
85+
86+
# 4. Generate a 256-bit AES key to use with your keyring.
87+
# In practice, you should get this key from a secure key management system such as an HSM.
88+
89+
# Here, the input to secrets.token_bytes() = 32 bytes = 256 bits
90+
static_key = secrets.token_bytes(32)
91+
92+
# 5. Create a Raw AES keyring
93+
mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders(
94+
config=MaterialProvidersConfig()
95+
)
96+
97+
keyring_input: CreateRawAesKeyringInput = CreateRawAesKeyringInput(
98+
key_namespace=key_name_space,
99+
key_name=key_name,
100+
wrapping_key=static_key,
101+
wrapping_alg=AesWrappingAlg.ALG_AES256_GCM_IV12_TAG16
102+
)
103+
104+
raw_aes_keyring: IKeyring = mat_prov.create_raw_aes_keyring(
105+
input=keyring_input
106+
)
107+
108+
# 6. Encrypt the data stream with the encryptionContext
109+
with open(plaintext_filename, 'rb') as pt_file, open(ciphertext_filename, 'wb') as ct_file:
110+
with client.stream(
111+
mode='e',
112+
source=pt_file,
113+
keyring=raw_aes_keyring,
114+
encryption_context=encryption_context
115+
) as encryptor:
116+
for chunk in encryptor:
117+
ct_file.write(chunk)
118+
119+
# 7. Demonstrate that the ciphertext and plaintext are different.
120+
# (This is an example for demonstration; you do not need to do this in your own code.)
121+
assert not filecmp.cmp(plaintext_filename, ciphertext_filename), \
122+
"Ciphertext and plaintext data are the same. Invalid encryption"
123+
124+
# 8. Decrypt your encrypted data using the same keyring you used on encrypt.
125+
with open(ciphertext_filename, 'rb') as ct_file, open(new_plaintext_filename, 'wb') as pt_file:
126+
with client.stream(
127+
mode='d',
128+
source=ct_file,
129+
keyring=raw_aes_keyring,
130+
encryption_context=encryption_context
131+
) as decryptor:
132+
for chunk in decryptor:
133+
pt_file.write(chunk)
134+
135+
# 9. Demonstrate that the encryption context is correct in the decrypted message header
136+
# (This is an example for demonstration; you do not need to do this in your own code.)
137+
for k, v in encryption_context.items():
138+
assert v == decryptor.header.encryption_context[k], \
139+
"Encryption context does not match expected values"
140+
141+
# 10. Demonstrate that the decrypted plaintext is identical to the original plaintext.
142+
# (This is an example for demonstration; you do not need to do this in your own code.)
143+
assert filecmp.cmp(plaintext_filename, new_plaintext_filename), \
144+
"Decrypted plaintext should be identical to the original plaintext. Invalid decryption"

examples/src/keyrings/hierarchical_keyring.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"

examples/src/keyrings/multi_keyring_example.py

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

176176
# 10b. Demonstrate that the decrypted plaintext is identical to the original plaintext.
177177
# (This is an example for demonstration; you do not need to do this in your own code.)
178-
assert plaintext_bytes_multi_keyring == EXAMPLE_DATA
178+
assert plaintext_bytes_multi_keyring == EXAMPLE_DATA, \
179+
"Decrypted plaintext should be identical to the original plaintext. Invalid decryption"
179180

180181
# Because you used a multi_keyring on Encrypt, you can use either the
181182
# `kms_keyring` or `raw_aes_keyring` individually to decrypt the data.
@@ -192,7 +193,8 @@ def encrypt_and_decrypt_with_keyring(
192193

193194
# 11b. Demonstrate that the decrypted plaintext is identical to the original plaintext.
194195
# (This is an example for demonstration; you do not need to do this in your own code.)
195-
assert plaintext_bytes_kms_keyring == EXAMPLE_DATA
196+
assert plaintext_bytes_kms_keyring == EXAMPLE_DATA, \
197+
"Decrypted plaintext should be identical to the original plaintext. Invalid decryption"
196198

197199
# 12. Demonstrate that you can also successfully decrypt data using the `raw_aes_keyring`
198200
# directly.
@@ -206,4 +208,5 @@ def encrypt_and_decrypt_with_keyring(
206208

207209
# 12b. Demonstrate that the decrypted plaintext is identical to the original plaintext.
208210
# (This is an example for demonstration; you do not need to do this in your own code.)
209-
assert plaintext_bytes_raw_aes_keyring == EXAMPLE_DATA
211+
assert plaintext_bytes_raw_aes_keyring == EXAMPLE_DATA, \
212+
"Decrypted plaintext should be identical to the original plaintext. Invalid decryption"

0 commit comments

Comments
 (0)