Skip to content

Commit daed717

Browse files
feat: immutable cryptographic materials (for keyrings) (aws#231)
* feat: change keyring materials modifiers to immutable constructors * feat: add key_name parameter and key_namespace accessor to MasterKeyInfo * feat: adopt immutable materials for KMS keyring * feat: adopt immutable materials for multi-keyring * fix: revise test helpers to use key IDs that are not used in tests * feat: add key_name parameter and key_namespace accessor to MasterKeyInfo pt2 * feat: fix raw keyrings and move to immutable materials * adopt immutable materials * add encryption context entries to keyring trace awslabs/aws-encryption-sdk-specification#32 * use the key name, not the provider info, in the keyring trace * fix the keyring trace tests to accurately fail when trace is wrong * fix: fix incorrectly placed test assertions * chore: clean up linting * docs: update src/aws_encryption_sdk/keyrings/raw.py Co-Authored-By: Wesley Rosenblum <55108558+WesleyRosenblum@users.noreply.github.com> * chore: remove unnecessary copies on encrypt and avoid overwriting pointers on decrypt * chore: finish copy cleanup Co-authored-by: Wesley Rosenblum <55108558+WesleyRosenblum@users.noreply.github.com>
1 parent 1dac283 commit daed717

File tree

13 files changed

+458
-237
lines changed

13 files changed

+458
-237
lines changed

src/aws_encryption_sdk/internal/formatting/serialize.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,6 @@ def serialize_wrapped_key(key_provider, wrapping_algorithm, wrapping_key_id, enc
316316
)
317317
key_ciphertext = encrypted_wrapped_key.ciphertext + encrypted_wrapped_key.tag
318318
return EncryptedDataKey(
319-
key_provider=MasterKeyInfo(provider_id=key_provider.provider_id, key_info=key_info),
319+
key_provider=MasterKeyInfo(provider_id=key_provider.provider_id, key_info=key_info, key_name=wrapping_key_id),
320320
encrypted_data_key=key_ciphertext,
321321
)

src/aws_encryption_sdk/keyrings/aws_kms/__init__.py

+26-25
Original file line numberDiff line numberDiff line change
@@ -180,25 +180,26 @@ class _AwsKmsSingleCmkKeyring(Keyring):
180180
def on_encrypt(self, encryption_materials):
181181
# type: (EncryptionMaterials) -> EncryptionMaterials
182182
trace_info = MasterKeyInfo(provider_id=_PROVIDER_ID, key_info=self._key_id)
183+
new_materials = encryption_materials
183184
try:
184-
if encryption_materials.data_encryption_key is None:
185+
if new_materials.data_encryption_key is None:
185186
plaintext_key, encrypted_key = _do_aws_kms_generate_data_key(
186187
client_supplier=self._client_supplier,
187188
key_name=self._key_id,
188-
encryption_context=encryption_materials.encryption_context,
189-
algorithm=encryption_materials.algorithm,
189+
encryption_context=new_materials.encryption_context,
190+
algorithm=new_materials.algorithm,
190191
grant_tokens=self._grant_tokens,
191192
)
192-
encryption_materials.add_data_encryption_key(
193+
new_materials = new_materials.with_data_encryption_key(
193194
data_encryption_key=plaintext_key,
194195
keyring_trace=KeyringTrace(wrapping_key=trace_info, flags=_GENERATE_FLAGS),
195196
)
196197
else:
197198
encrypted_key = _do_aws_kms_encrypt(
198199
client_supplier=self._client_supplier,
199200
key_name=self._key_id,
200-
plaintext_data_key=encryption_materials.data_encryption_key,
201-
encryption_context=encryption_materials.encryption_context,
201+
plaintext_data_key=new_materials.data_encryption_key,
202+
encryption_context=new_materials.encryption_context,
202203
grant_tokens=self._grant_tokens,
203204
)
204205
except Exception: # pylint: disable=broad-except
@@ -207,30 +208,30 @@ def on_encrypt(self, encryption_materials):
207208
_LOGGER.exception(message)
208209
raise EncryptKeyError(message)
209210

210-
encryption_materials.add_encrypted_data_key(
211+
return new_materials.with_encrypted_data_key(
211212
encrypted_data_key=encrypted_key, keyring_trace=KeyringTrace(wrapping_key=trace_info, flags=_ENCRYPT_FLAGS)
212213
)
213214

214-
return encryption_materials
215-
216215
def on_decrypt(self, decryption_materials, encrypted_data_keys):
217216
# type: (DecryptionMaterials, Iterable[EncryptedDataKey]) -> DecryptionMaterials
217+
new_materials = decryption_materials
218+
218219
for edk in encrypted_data_keys:
219-
if decryption_materials.data_encryption_key is not None:
220-
return decryption_materials
220+
if new_materials.data_encryption_key is not None:
221+
return new_materials
221222

222223
if (
223224
edk.key_provider.provider_id == _PROVIDER_ID
224225
and edk.key_provider.key_info.decode("utf-8") == self._key_id
225226
):
226-
decryption_materials = _try_aws_kms_decrypt(
227+
new_materials = _try_aws_kms_decrypt(
227228
client_supplier=self._client_supplier,
228-
decryption_materials=decryption_materials,
229+
decryption_materials=new_materials,
229230
grant_tokens=self._grant_tokens,
230231
encrypted_data_key=edk,
231232
)
232233

233-
return decryption_materials
234+
return new_materials
234235

235236

236237
@attr.s
@@ -258,19 +259,21 @@ def on_encrypt(self, encryption_materials):
258259

259260
def on_decrypt(self, decryption_materials, encrypted_data_keys):
260261
# type: (DecryptionMaterials, Iterable[EncryptedDataKey]) -> DecryptionMaterials
262+
new_materials = decryption_materials
263+
261264
for edk in encrypted_data_keys:
262-
if decryption_materials.data_encryption_key is not None:
263-
return decryption_materials
265+
if new_materials.data_encryption_key is not None:
266+
return new_materials
264267

265268
if edk.key_provider.provider_id == _PROVIDER_ID:
266-
decryption_materials = _try_aws_kms_decrypt(
269+
new_materials = _try_aws_kms_decrypt(
267270
client_supplier=self._client_supplier,
268-
decryption_materials=decryption_materials,
271+
decryption_materials=new_materials,
269272
grant_tokens=self._grant_tokens,
270273
encrypted_data_key=edk,
271274
)
272275

273-
return decryption_materials
276+
return new_materials
274277

275278

276279
def _try_aws_kms_decrypt(client_supplier, decryption_materials, grant_tokens, encrypted_data_key):
@@ -293,14 +296,12 @@ def _try_aws_kms_decrypt(client_supplier, decryption_materials, grant_tokens, en
293296
except Exception: # pylint: disable=broad-except
294297
# We intentionally WANT to catch all exceptions here
295298
_LOGGER.exception("Unable to decrypt encrypted data key from %s", encrypted_data_key.key_provider)
296-
else:
297-
decryption_materials.add_data_encryption_key(
298-
data_encryption_key=plaintext_key,
299-
keyring_trace=KeyringTrace(wrapping_key=encrypted_data_key.key_provider, flags=_DECRYPT_FLAGS),
300-
)
301299
return decryption_materials
302300

303-
return decryption_materials
301+
return decryption_materials.with_data_encryption_key(
302+
data_encryption_key=plaintext_key,
303+
keyring_trace=KeyringTrace(wrapping_key=encrypted_data_key.key_provider, flags=_DECRYPT_FLAGS),
304+
)
304305

305306

306307
def _do_aws_kms_decrypt(client_supplier, key_name, encrypted_data_key, encryption_context, grant_tokens):

src/aws_encryption_sdk/keyrings/multi.py

+14-10
Original file line numberDiff line numberDiff line change
@@ -67,20 +67,21 @@ def on_encrypt(self, encryption_materials):
6767
"and encryption materials do not already contain a plaintext data key."
6868
)
6969

70+
new_materials = encryption_materials
71+
7072
# Call on_encrypt on the generator keyring if it is provided
7173
if self.generator is not None:
72-
73-
encryption_materials = self.generator.on_encrypt(encryption_materials=encryption_materials)
74+
new_materials = self.generator.on_encrypt(encryption_materials=new_materials)
7475

7576
# Check if data key is generated
76-
if encryption_materials.data_encryption_key is None:
77+
if new_materials.data_encryption_key is None:
7778
raise GenerateKeyError("Unable to generate data encryption key.")
7879

7980
# Call on_encrypt on all other keyrings
8081
for keyring in self.children:
81-
encryption_materials = keyring.on_encrypt(encryption_materials=encryption_materials)
82+
new_materials = keyring.on_encrypt(encryption_materials=new_materials)
8283

83-
return encryption_materials
84+
return new_materials
8485

8586
def on_decrypt(self, decryption_materials, encrypted_data_keys):
8687
# type: (DecryptionMaterials, Iterable[EncryptedDataKey]) -> DecryptionMaterials
@@ -92,10 +93,13 @@ def on_decrypt(self, decryption_materials, encrypted_data_keys):
9293
:rtype: DecryptionMaterials
9394
"""
9495
# Call on_decrypt on all keyrings till decryption is successful
96+
new_materials = decryption_materials
9597
for keyring in self._decryption_keyrings:
96-
if decryption_materials.data_encryption_key is not None:
97-
return decryption_materials
98-
decryption_materials = keyring.on_decrypt(
99-
decryption_materials=decryption_materials, encrypted_data_keys=encrypted_data_keys
98+
if new_materials.data_encryption_key is not None:
99+
return new_materials
100+
101+
new_materials = keyring.on_decrypt(
102+
decryption_materials=new_materials, encrypted_data_keys=encrypted_data_keys
100103
)
101-
return decryption_materials
104+
105+
return new_materials

0 commit comments

Comments
 (0)