Skip to content

feat: immutable cryptographic materials (for keyrings) #231

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Mar 31, 2020
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/aws_encryption_sdk/internal/formatting/serialize.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,6 @@ def serialize_wrapped_key(key_provider, wrapping_algorithm, wrapping_key_id, enc
)
key_ciphertext = encrypted_wrapped_key.ciphertext + encrypted_wrapped_key.tag
return EncryptedDataKey(
key_provider=MasterKeyInfo(provider_id=key_provider.provider_id, key_info=key_info),
key_provider=MasterKeyInfo(provider_id=key_provider.provider_id, key_info=key_info, key_name=wrapping_key_id),
encrypted_data_key=key_ciphertext,
)
52 changes: 27 additions & 25 deletions src/aws_encryption_sdk/keyrings/aws_kms/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
.. versionadded:: 1.5.0

"""
import copy
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you still need this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope! That'll be why CI is failing. :)

import logging

import attr
Expand Down Expand Up @@ -180,25 +181,26 @@ class _AwsKmsSingleCmkKeyring(Keyring):
def on_encrypt(self, encryption_materials):
# type: (EncryptionMaterials) -> EncryptionMaterials
trace_info = MasterKeyInfo(provider_id=_PROVIDER_ID, key_info=self._key_id)
new_materials = encryption_materials
try:
if encryption_materials.data_encryption_key is None:
if new_materials.data_encryption_key is None:
plaintext_key, encrypted_key = _do_aws_kms_generate_data_key(
client_supplier=self._client_supplier,
key_name=self._key_id,
encryption_context=encryption_materials.encryption_context,
algorithm=encryption_materials.algorithm,
encryption_context=new_materials.encryption_context,
algorithm=new_materials.algorithm,
grant_tokens=self._grant_tokens,
)
encryption_materials.add_data_encryption_key(
new_materials = new_materials.with_data_encryption_key(
data_encryption_key=plaintext_key,
keyring_trace=KeyringTrace(wrapping_key=trace_info, flags=_GENERATE_FLAGS),
)
else:
encrypted_key = _do_aws_kms_encrypt(
client_supplier=self._client_supplier,
key_name=self._key_id,
plaintext_data_key=encryption_materials.data_encryption_key,
encryption_context=encryption_materials.encryption_context,
plaintext_data_key=new_materials.data_encryption_key,
encryption_context=new_materials.encryption_context,
grant_tokens=self._grant_tokens,
)
except Exception: # pylint: disable=broad-except
Expand All @@ -207,30 +209,30 @@ def on_encrypt(self, encryption_materials):
_LOGGER.exception(message)
raise EncryptKeyError(message)

encryption_materials.add_encrypted_data_key(
return new_materials.with_encrypted_data_key(
encrypted_data_key=encrypted_key, keyring_trace=KeyringTrace(wrapping_key=trace_info, flags=_ENCRYPT_FLAGS)
)

return encryption_materials

def on_decrypt(self, decryption_materials, encrypted_data_keys):
# type: (DecryptionMaterials, Iterable[EncryptedDataKey]) -> DecryptionMaterials
new_materials = decryption_materials

for edk in encrypted_data_keys:
if decryption_materials.data_encryption_key is not None:
return decryption_materials
if new_materials.data_encryption_key is not None:
return new_materials

if (
edk.key_provider.provider_id == _PROVIDER_ID
and edk.key_provider.key_info.decode("utf-8") == self._key_id
):
decryption_materials = _try_aws_kms_decrypt(
new_materials = _try_aws_kms_decrypt(
client_supplier=self._client_supplier,
decryption_materials=decryption_materials,
decryption_materials=new_materials,
grant_tokens=self._grant_tokens,
encrypted_data_key=edk,
)

return decryption_materials
return new_materials


@attr.s
Expand Down Expand Up @@ -258,19 +260,21 @@ def on_encrypt(self, encryption_materials):

def on_decrypt(self, decryption_materials, encrypted_data_keys):
# type: (DecryptionMaterials, Iterable[EncryptedDataKey]) -> DecryptionMaterials
new_materials = decryption_materials

for edk in encrypted_data_keys:
if decryption_materials.data_encryption_key is not None:
return decryption_materials
if new_materials.data_encryption_key is not None:
return new_materials

if edk.key_provider.provider_id == _PROVIDER_ID:
decryption_materials = _try_aws_kms_decrypt(
new_materials = _try_aws_kms_decrypt(
client_supplier=self._client_supplier,
decryption_materials=decryption_materials,
decryption_materials=new_materials,
grant_tokens=self._grant_tokens,
encrypted_data_key=edk,
)

return decryption_materials
return new_materials


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

return decryption_materials
return decryption_materials.with_data_encryption_key(
data_encryption_key=plaintext_key,
keyring_trace=KeyringTrace(wrapping_key=encrypted_data_key.key_provider, flags=_DECRYPT_FLAGS),
)


def _do_aws_kms_decrypt(client_supplier, key_name, encrypted_data_key, encryption_context, grant_tokens):
Expand Down
25 changes: 15 additions & 10 deletions src/aws_encryption_sdk/keyrings/multi.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
"""Resources required for Multi Keyrings."""
import copy
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needed?

import itertools

import attr
Expand Down Expand Up @@ -67,20 +68,21 @@ def on_encrypt(self, encryption_materials):
"and encryption materials do not already contain a plaintext data key."
)

new_materials = encryption_materials

# Call on_encrypt on the generator keyring if it is provided
if self.generator is not None:

encryption_materials = self.generator.on_encrypt(encryption_materials=encryption_materials)
new_materials = self.generator.on_encrypt(encryption_materials=new_materials)

# Check if data key is generated
if encryption_materials.data_encryption_key is None:
if new_materials.data_encryption_key is None:
raise GenerateKeyError("Unable to generate data encryption key.")

# Call on_encrypt on all other keyrings
for keyring in self.children:
encryption_materials = keyring.on_encrypt(encryption_materials=encryption_materials)
new_materials = keyring.on_encrypt(encryption_materials=new_materials)

return encryption_materials
return new_materials

def on_decrypt(self, decryption_materials, encrypted_data_keys):
# type: (DecryptionMaterials, Iterable[EncryptedDataKey]) -> DecryptionMaterials
Expand All @@ -92,10 +94,13 @@ def on_decrypt(self, decryption_materials, encrypted_data_keys):
:rtype: DecryptionMaterials
"""
# Call on_decrypt on all keyrings till decryption is successful
new_materials = decryption_materials
for keyring in self._decryption_keyrings:
if decryption_materials.data_encryption_key is not None:
return decryption_materials
decryption_materials = keyring.on_decrypt(
decryption_materials=decryption_materials, encrypted_data_keys=encrypted_data_keys
if new_materials.data_encryption_key is not None:
return new_materials

new_materials = keyring.on_decrypt(
decryption_materials=new_materials, encrypted_data_keys=encrypted_data_keys
)
return decryption_materials

return new_materials
Loading