diff --git a/src/aws_encryption_sdk/keyring/multi_keyring.py b/src/aws_encryption_sdk/keyring/multi_keyring.py new file mode 100644 index 000000000..f43820be5 --- /dev/null +++ b/src/aws_encryption_sdk/keyring/multi_keyring.py @@ -0,0 +1,105 @@ +# Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +"""Resources required for Multi Keyrings.""" +import itertools + +import attr +from attr.validators import deep_iterable, instance_of, optional + +from aws_encryption_sdk.exceptions import EncryptKeyError, GenerateKeyError +from aws_encryption_sdk.keyring.base import DecryptionMaterials, EncryptedDataKey, EncryptionMaterials, Keyring + +try: # Python 3.5.0 and 3.5.1 have incompatible typing modules + from typing import Iterable # noqa pylint: disable=unused-import +except ImportError: # pragma: no cover + # We only actually need these imports when running the mypy checks + pass + + +@attr.s +class MultiKeyring(Keyring): + """Public class for Multi Keyring. + + :param generator: Generator keyring used to generate data encryption key (optional) + :type generator: Keyring + :param list children: List of keyrings used to encrypt the data encryption key (optional) + :raises EncryptKeyError: if encryption of data key fails for any reason + """ + + children = attr.ib( + default=attr.Factory(tuple), validator=optional(deep_iterable(member_validator=instance_of(Keyring))) + ) + generator = attr.ib(default=None, validator=optional(instance_of(Keyring))) + + def __attrs_post_init__(self): + # type: () -> None + """Prepares initial values not handled by attrs.""" + neither_generator_nor_children = self.generator is None and not self.children + if neither_generator_nor_children: + raise TypeError("At least one of generator or children must be provided") + + _generator = (self.generator,) if self.generator is not None else () + self._decryption_keyrings = list(itertools.chain(_generator, self.children)) + + def on_encrypt(self, encryption_materials): + # type: (EncryptionMaterials) -> EncryptionMaterials + """Generate a data key using generator keyring + and encrypt it using any available wrapping key in any child keyring. + + :param encryption_materials: Encryption materials for keyring to modify. + :type encryption_materials: aws_encryption_sdk.materials_managers.EncryptionMaterials + :returns: Optionally modified encryption materials. + :rtype: aws_encryption_sdk.materials_managers.EncryptionMaterials + :raises EncryptKeyError: if unable to encrypt data key. + """ + # Check if generator keyring is not provided and data key is not generated + if self.generator is None and encryption_materials.data_encryption_key is None: + raise EncryptKeyError( + "Generator keyring not provided " + "and encryption materials do not already contain a plaintext data key." + ) + + # 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) + + # Check if data key is generated + if encryption_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) + + return encryption_materials + + def on_decrypt(self, decryption_materials, encrypted_data_keys): + # type: (DecryptionMaterials, Iterable[EncryptedDataKey]) -> DecryptionMaterials + """Attempt to decrypt the encrypted data keys. + + :param decryption_materials: Decryption materials for keyring to modify. + :type decryption_materials: aws_encryption_sdk.materials_managers.DecryptionMaterials + :param encrypted_data_keys: List of encrypted data keys. + :type: List of `aws_encryption_sdk.structures.EncryptedDataKey` + :returns: Optionally modified decryption materials. + :rtype: aws_encryption_sdk.materials_managers.DecryptionMaterials + """ + # Call on_decrypt on all keyrings till decryption is successful + 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 + ) + return decryption_materials diff --git a/test/functional/test_f_multi_keyring.py b/test/functional/test_f_multi_keyring.py new file mode 100644 index 000000000..54d519f0b --- /dev/null +++ b/test/functional/test_f_multi_keyring.py @@ -0,0 +1,132 @@ +# Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +"""Functional tests for Multi keyring encryption decryption path.""" + +import pytest +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives.asymmetric import rsa + +from aws_encryption_sdk.identifiers import KeyringTraceFlag, WrappingAlgorithm +from aws_encryption_sdk.internal.defaults import ALGORITHM +from aws_encryption_sdk.keyring.multi_keyring import MultiKeyring +from aws_encryption_sdk.keyring.raw_keyring import RawAESKeyring, RawRSAKeyring +from aws_encryption_sdk.materials_managers import DecryptionMaterials, EncryptionMaterials +from aws_encryption_sdk.structures import KeyringTrace, MasterKeyInfo, RawDataKey + +pytestmark = [pytest.mark.functional, pytest.mark.local] + +_ENCRYPTION_CONTEXT = {"encryption": "context", "values": "here"} +_PROVIDER_ID = "Random Raw Keys" +_KEY_ID = b"5325b043-5843-4629-869c-64794af77ada" +_WRAPPING_KEY_AES = b"\xeby-\x80A6\x15rA8\x83#,\xe4\xab\xac`\xaf\x99Z\xc1\xce\xdb\xb6\x0f\xb7\x805\xb2\x14J3" + +_ENCRYPTION_MATERIALS_WITHOUT_DATA_KEY = EncryptionMaterials( + algorithm=ALGORITHM, encryption_context=_ENCRYPTION_CONTEXT +) + +_ENCRYPTION_MATERIALS_WITH_DATA_KEY = EncryptionMaterials( + algorithm=ALGORITHM, + data_encryption_key=RawDataKey( + key_provider=MasterKeyInfo(provider_id=_PROVIDER_ID, key_info=_KEY_ID), + data_key=b'*!\xa1"^-(\xf3\x105\x05i@B\xc2\xa2\xb7\xdd\xd5\xd5\xa9\xddm\xfae\xa8\\$\xf9d\x1e(', + ), + encryption_context=_ENCRYPTION_CONTEXT, + keyring_trace=[ + KeyringTrace( + wrapping_key=MasterKeyInfo(provider_id=_PROVIDER_ID, key_info=_KEY_ID), + flags={KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY}, + ) + ], +) + +_MULTI_KEYRING_WITH_GENERATOR_AND_CHILDREN = MultiKeyring( + generator=RawAESKeyring( + key_namespace=_PROVIDER_ID, + key_name=_KEY_ID, + wrapping_algorithm=WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING, + wrapping_key=_WRAPPING_KEY_AES, + ), + children=[ + RawRSAKeyring( + key_namespace=_PROVIDER_ID, + key_name=_KEY_ID, + wrapping_algorithm=WrappingAlgorithm.RSA_OAEP_SHA256_MGF1, + private_wrapping_key=rsa.generate_private_key( + public_exponent=65537, key_size=2048, backend=default_backend() + ), + ), + RawRSAKeyring( + key_namespace=_PROVIDER_ID, + key_name=_KEY_ID, + wrapping_algorithm=WrappingAlgorithm.RSA_OAEP_SHA256_MGF1, + private_wrapping_key=rsa.generate_private_key( + public_exponent=65537, key_size=2048, backend=default_backend() + ), + ), + ], +) + +_MULTI_KEYRING_WITHOUT_CHILDREN = MultiKeyring( + generator=RawRSAKeyring( + key_namespace=_PROVIDER_ID, + key_name=_KEY_ID, + wrapping_algorithm=WrappingAlgorithm.RSA_OAEP_SHA256_MGF1, + private_wrapping_key=rsa.generate_private_key(public_exponent=65537, key_size=2048, backend=default_backend()), + ) +) + +_MULTI_KEYRING_WITHOUT_GENERATOR = MultiKeyring( + children=[ + RawRSAKeyring( + key_namespace=_PROVIDER_ID, + key_name=_KEY_ID, + wrapping_algorithm=WrappingAlgorithm.RSA_OAEP_SHA256_MGF1, + private_wrapping_key=rsa.generate_private_key( + public_exponent=65537, key_size=2048, backend=default_backend() + ), + ), + RawAESKeyring( + key_namespace=_PROVIDER_ID, + key_name=_KEY_ID, + wrapping_algorithm=WrappingAlgorithm.AES_128_GCM_IV12_TAG16_NO_PADDING, + wrapping_key=_WRAPPING_KEY_AES, + ), + ] +) + + +@pytest.mark.parametrize( + "multi_keyring, encryption_materials", + [ + (_MULTI_KEYRING_WITH_GENERATOR_AND_CHILDREN, _ENCRYPTION_MATERIALS_WITHOUT_DATA_KEY), + (_MULTI_KEYRING_WITH_GENERATOR_AND_CHILDREN, _ENCRYPTION_MATERIALS_WITH_DATA_KEY), + (_MULTI_KEYRING_WITHOUT_CHILDREN, _ENCRYPTION_MATERIALS_WITH_DATA_KEY), + (_MULTI_KEYRING_WITHOUT_GENERATOR, _ENCRYPTION_MATERIALS_WITH_DATA_KEY), + ], +) +def test_multi_keyring_encryption_decryption(multi_keyring, encryption_materials): + # Call on_encrypt function for the keyring + encryption_materials = multi_keyring.on_encrypt(encryption_materials) + + # Generate decryption materials + decryption_materials = DecryptionMaterials( + algorithm=ALGORITHM, verification_key=b"ex_verification_key", encryption_context=_ENCRYPTION_CONTEXT + ) + + # Call on_decrypt function for the keyring + decryption_materials = multi_keyring.on_decrypt( + decryption_materials=decryption_materials, encrypted_data_keys=encryption_materials.encrypted_data_keys + ) + + # Check if the data keys match + assert encryption_materials.data_encryption_key == decryption_materials.data_encryption_key diff --git a/test/unit/test_keyring_multi.py b/test/unit/test_keyring_multi.py new file mode 100644 index 000000000..6b66a490f --- /dev/null +++ b/test/unit/test_keyring_multi.py @@ -0,0 +1,247 @@ +# Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +"""Unit tests for Multi keyring.""" + +import pytest +from mock import MagicMock +from pytest_mock import mocker # noqa pylint: disable=unused-import + +from aws_encryption_sdk.exceptions import EncryptKeyError, GenerateKeyError +from aws_encryption_sdk.identifiers import WrappingAlgorithm +from aws_encryption_sdk.internal.formatting import serialize +from aws_encryption_sdk.keyring.base import Keyring +from aws_encryption_sdk.keyring.multi_keyring import MultiKeyring +from aws_encryption_sdk.keyring.raw_keyring import RawAESKeyring + +from .unit_test_utils import ( + IdentityKeyring, + OnlyGenerateKeyring, + get_decryption_materials_with_data_key, + get_decryption_materials_without_data_key, + get_encryption_materials_with_data_key, + get_encryption_materials_with_encrypted_data_key, + get_encryption_materials_without_data_key, + get_multi_keyring_with_generator_and_children, + get_multi_keyring_with_no_children, + get_multi_keyring_with_no_generator, +) + +pytestmark = [pytest.mark.unit, pytest.mark.local] + + +_ENCRYPTION_CONTEXT = {"encryption": "context", "values": "here"} +_PROVIDER_ID = "Random Raw Keys" +_KEY_ID = b"5325b043-5843-4629-869c-64794af77ada" +_WRAPPING_KEY_AES = b"\xeby-\x80A6\x15rA8\x83#,\xe4\xab\xac`\xaf\x99Z\xc1\xce\xdb\xb6\x0f\xb7\x805\xb2\x14J3" +_SIGNING_KEY = b"aws-crypto-public-key" + + +@pytest.fixture +def identity_keyring(): + return IdentityKeyring() + + +@pytest.fixture +def keyring_which_only_generates(): + return OnlyGenerateKeyring() + + +@pytest.fixture +def mock_generator(): + mock_generator_keyring = MagicMock() + mock_generator_keyring.__class__ = RawAESKeyring + return mock_generator_keyring + + +@pytest.fixture +def mock_child_1(): + mock_child_1_keyring = MagicMock() + mock_child_1_keyring.__class__ = RawAESKeyring + return mock_child_1_keyring + + +@pytest.fixture +def mock_child_2(): + mock_child_2_keyring = MagicMock() + mock_child_2_keyring.__class__ = RawAESKeyring + return mock_child_2_keyring + + +@pytest.fixture +def mock_child_3(): + mock_child_3_keyring = MagicMock() + mock_child_3_keyring.__class__ = RawAESKeyring + mock_child_3_keyring.on_decrypt.return_value = get_decryption_materials_with_data_key() + return mock_child_3_keyring + + +@pytest.fixture +def patch_encrypt(mocker): + mocker.patch.object(serialize, "serialize_raw_master_key_prefix") + return serialize.serialize_raw_master_key_prefix + + +def test_parent(): + assert issubclass(MultiKeyring, Keyring) + + +def test_keyring_with_generator_but_no_children(): + generator_keyring = RawAESKeyring( + key_namespace=_PROVIDER_ID, + key_name=_KEY_ID, + wrapping_key=_WRAPPING_KEY_AES, + wrapping_algorithm=WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING, + ) + test_multi_keyring = MultiKeyring(generator=generator_keyring) + assert test_multi_keyring.generator is generator_keyring + assert not test_multi_keyring.children + + +def test_keyring_with_children_but_no_generator(): + children_keyring = [ + RawAESKeyring( + key_namespace=_PROVIDER_ID, + key_name=_KEY_ID, + wrapping_key=_WRAPPING_KEY_AES, + wrapping_algorithm=WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING, + ) + ] + test_multi_keyring = MultiKeyring(children=children_keyring) + assert test_multi_keyring.children is children_keyring + assert test_multi_keyring.generator is None + + +def test_keyring_with_no_generator_no_children(): + with pytest.raises(TypeError) as exc_info: + MultiKeyring() + assert exc_info.match("At least one of generator or children must be provided") + + +@pytest.mark.parametrize( + "generator, children", + ( + (WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING, None), + (WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING, get_multi_keyring_with_no_generator().children), + (None, [WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING]), + (get_multi_keyring_with_no_children().generator, [WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING]), + ), +) +def test_keyring_with_invalid_parameters(generator, children): + with pytest.raises(TypeError) as exc_info: + MultiKeyring(generator=generator, children=children) + assert exc_info.match("('children'|'generator') must be .*") + + +def test_decryption_keyrings(): + test_multi_keyring = get_multi_keyring_with_generator_and_children() + assert test_multi_keyring.generator in test_multi_keyring._decryption_keyrings + for child_keyring in test_multi_keyring.children: + assert child_keyring in test_multi_keyring._decryption_keyrings + assert len(test_multi_keyring._decryption_keyrings) == len(test_multi_keyring.children) + 1 + + +def test_on_encrypt_with_no_generator_no_data_encryption_key(): + test_multi_keyring = get_multi_keyring_with_no_generator() + with pytest.raises(EncryptKeyError) as exc_info: + test_multi_keyring.on_encrypt(encryption_materials=get_encryption_materials_without_data_key()) + assert exc_info.match( + "Generator keyring not provided and encryption materials do not already contain a plaintext data key." + ) + + +def test_identity_keyring_as_generator_and_no_data_encryption_key(identity_keyring): + test_multi_keyring = MultiKeyring(generator=identity_keyring) + with pytest.raises(GenerateKeyError) as exc_info: + test_multi_keyring.on_encrypt(encryption_materials=get_encryption_materials_without_data_key()) + assert exc_info.match("Unable to generate data encryption key.") + + +def test_number_of_encrypted_data_keys_without_generator_with_children(): + test_multi_keyring = get_multi_keyring_with_no_generator() + test = test_multi_keyring.on_encrypt(encryption_materials=get_encryption_materials_with_data_key()) + assert len(test.encrypted_data_keys) == len(test_multi_keyring.children) + + +def test_number_of_encrypted_data_keys_without_children_with_generator(): + test_multi_keyring = get_multi_keyring_with_no_children() + test = test_multi_keyring.on_encrypt(encryption_materials=get_encryption_materials_with_data_key()) + assert len(test.encrypted_data_keys) == 1 + + +def test_number_of_encrypted_data_keys_with_generator_and_children(): + test_multi_keyring = get_multi_keyring_with_generator_and_children() + number_of_children = len(test_multi_keyring.children) + test = test_multi_keyring.on_encrypt(encryption_materials=get_encryption_materials_with_data_key()) + assert len(test.encrypted_data_keys) == number_of_children + 1 + + +def test_on_encrypt_when_data_encryption_key_given(mock_generator, mock_child_1, mock_child_2): + test_multi_keyring = MultiKeyring(generator=mock_generator, children=[mock_child_1, mock_child_2]) + test_multi_keyring.on_encrypt(encryption_materials=get_encryption_materials_with_data_key()) + for keyring in test_multi_keyring._decryption_keyrings: + keyring.on_encrypt.assert_called_once() + + +def test_on_encrypt_edk_length_when_keyring_generates_but_does_not_encrypt_encryption_materials_without_data_key(): + test_multi_keyring = MultiKeyring(generator=OnlyGenerateKeyring()) + len_edk_before_encrypt = len(get_encryption_materials_without_data_key().encrypted_data_keys) + test = test_multi_keyring.on_encrypt(encryption_materials=get_encryption_materials_without_data_key()) + assert test.data_encryption_key is not None + assert len(test.encrypted_data_keys) == len_edk_before_encrypt + + +def test_on_encrypt_edk_length_when_keyring_generates_but_does_not_encrypt_encryption_materials_with_data_key(): + test_multi_keyring = MultiKeyring(generator=OnlyGenerateKeyring()) + test = test_multi_keyring.on_encrypt(encryption_materials=get_encryption_materials_with_encrypted_data_key()) + assert len(test.encrypted_data_keys) == len(get_encryption_materials_with_encrypted_data_key().encrypted_data_keys) + + +def test_on_decrypt_when_data_encryption_key_given(mock_generator, mock_child_1, mock_child_2): + test_multi_keyring = MultiKeyring(generator=mock_generator, children=[mock_child_1, mock_child_2]) + test_multi_keyring.on_decrypt(decryption_materials=get_decryption_materials_with_data_key(), encrypted_data_keys=[]) + for keyring in test_multi_keyring._decryption_keyrings: + assert not keyring.on_decrypt.called + + +def test_on_decrypt_every_keyring_called_when_data_encryption_key_not_added(mock_generator, mock_child_1, mock_child_2): + mock_generator.on_decrypt.side_effect = ( + lambda decryption_materials, encrypted_data_keys: get_decryption_materials_without_data_key() + ) + mock_child_1.on_decrypt.return_value = get_decryption_materials_without_data_key() + mock_child_2.on_decrypt.return_value = get_decryption_materials_without_data_key() + + test_multi_keyring = MultiKeyring(generator=mock_generator, children=[mock_child_1, mock_child_2]) + test_multi_keyring.on_decrypt( + decryption_materials=get_decryption_materials_without_data_key(), encrypted_data_keys=[] + ) + + for keyring in test_multi_keyring._decryption_keyrings: + assert keyring.on_decrypt.called + + +def test_no_keyring_called_after_data_encryption_key_added_when_data_encryption_key_not_given( + mock_generator, mock_child_1, mock_child_2, mock_child_3 +): + + mock_generator.on_decrypt.side_effect = ( + lambda decryption_materials, encrypted_data_keys: get_decryption_materials_without_data_key() + ) + + test_multi_keyring = MultiKeyring(generator=mock_generator, children=[mock_child_3, mock_child_1, mock_child_2]) + test_multi_keyring.on_decrypt( + decryption_materials=get_decryption_materials_without_data_key(), encrypted_data_keys=[] + ) + assert mock_generator.on_decrypt.called + assert mock_child_3.on_decrypt.called + assert not mock_child_1.called + assert not mock_child_2.called diff --git a/test/unit/unit_test_utils.py b/test/unit/unit_test_utils.py index 087c8dfa5..1e5d073e8 100644 --- a/test/unit/unit_test_utils.py +++ b/test/unit/unit_test_utils.py @@ -14,14 +14,18 @@ import copy import io import itertools +import os from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives.asymmetric import rsa -from aws_encryption_sdk.identifiers import Algorithm, KeyringTraceFlag +from aws_encryption_sdk.identifiers import Algorithm, KeyringTraceFlag, WrappingAlgorithm from aws_encryption_sdk.internal.utils.streams import InsistentReaderBytesIO -from aws_encryption_sdk.keyring.base import EncryptedDataKey, Keyring +from aws_encryption_sdk.keyring.base import Keyring +from aws_encryption_sdk.keyring.multi_keyring import MultiKeyring +from aws_encryption_sdk.keyring.raw_keyring import RawAESKeyring, RawRSAKeyring from aws_encryption_sdk.materials_managers import DecryptionMaterials, EncryptionMaterials -from aws_encryption_sdk.structures import KeyringTrace, MasterKeyInfo, RawDataKey +from aws_encryption_sdk.structures import EncryptedDataKey, KeyringTrace, MasterKeyInfo, RawDataKey try: # Python 3.5.0 and 3.5.1 have incompatible typing modules from typing import Iterable # noqa pylint: disable=unused-import @@ -37,6 +41,7 @@ _DATA_KEY = ( b"\x00\xfa\x8c\xdd\x08Au\xc6\x92_4\xc5\xfb\x90\xaf\x8f\xa1D\xaf\xcc\xd25" b"\xa8\x0b\x0b\x16\x92\x91W\x01\xb7\x84" ) +_WRAPPING_KEY_AES = b"\xeby-\x80A6\x15rA8\x83#,\xe4\xab\xac`\xaf\x99Z\xc1\xce\xdb\xb6\x0f\xb7\x805\xb2\x14J3" _PUBLIC_EXPONENT = 65537 _KEY_SIZE = 2048 @@ -87,6 +92,45 @@ def on_decrypt(self, decryption_materials, encrypted_data_keys): return decryption_materials +class OnlyGenerateKeyring(Keyring): + def on_encrypt(self, encryption_materials): + # type: (EncryptionMaterials) -> EncryptionMaterials + if encryption_materials.data_encryption_key is None: + key_provider = MasterKeyInfo(provider_id=_PROVIDER_ID, key_info=_KEY_ID) + data_encryption_key = RawDataKey( + key_provider=key_provider, data_key=os.urandom(encryption_materials.algorithm.kdf_input_len) + ) + encryption_materials.add_data_encryption_key( + data_encryption_key=data_encryption_key, + keyring_trace=KeyringTrace( + wrapping_key=key_provider, flags={KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY} + ), + ) + return encryption_materials + + def on_decrypt(self, decryption_materials, encrypted_data_keys): + # type: (DecryptionMaterials, Iterable[EncryptedDataKey]) -> DecryptionMaterials + return decryption_materials + + +def get_encryption_materials_with_data_key(): + return EncryptionMaterials( + algorithm=Algorithm.AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384, + data_encryption_key=RawDataKey( + key_provider=MasterKeyInfo(provider_id=_PROVIDER_ID, key_info=_KEY_ID), + data_key=b'*!\xa1"^-(\xf3\x105\x05i@B\xc2\xa2\xb7\xdd\xd5\xd5\xa9\xddm\xfae\xa8\\$\xf9d\x1e(', + ), + encryption_context=_ENCRYPTION_CONTEXT, + signing_key=_SIGNING_KEY, + keyring_trace=[ + KeyringTrace( + wrapping_key=MasterKeyInfo(provider_id=_PROVIDER_ID, key_info=_KEY_ID), + flags={KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY}, + ) + ], + ) + + def get_encryption_materials_with_data_encryption_key(): return EncryptionMaterials( algorithm=Algorithm.AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384, @@ -105,6 +149,42 @@ def get_encryption_materials_with_data_encryption_key(): ) +def get_encryption_materials_without_data_key(): + return EncryptionMaterials( + algorithm=Algorithm.AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384, + encryption_context=_ENCRYPTION_CONTEXT, + signing_key=_SIGNING_KEY, + ) + + +def get_encryption_materials_with_encrypted_data_key(): + return EncryptionMaterials( + algorithm=Algorithm.AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384, + data_encryption_key=RawDataKey( + key_provider=MasterKeyInfo(provider_id=_PROVIDER_ID, key_info=_KEY_ID), + data_key=b'*!\xa1"^-(\xf3\x105\x05i@B\xc2\xa2\xb7\xdd\xd5\xd5\xa9\xddm\xfae\xa8\\$\xf9d\x1e(', + ), + encrypted_data_keys=[ + EncryptedDataKey( + key_provider=MasterKeyInfo(provider_id=_PROVIDER_ID, key_info=_KEY_ID), + encrypted_data_key=b"\xde^\x97\x7f\x84\xe9\x9e\x98\xd0\xe2\xf8\xd5\xcb\xe9\x7f.}\x87\x16,\x11n#\xc8p" + b"\xdb\xbf\x94\x86*Q\x06\xd2\xf5\xdah\x08\xa4p\x81\xf7\xf4G\x07FzE\xde", + ) + ], + encryption_context=_ENCRYPTION_CONTEXT, + signing_key=_SIGNING_KEY, + keyring_trace=[ + KeyringTrace( + wrapping_key=MasterKeyInfo(provider_id=_PROVIDER_ID, key_info=_KEY_ID), + flags={ + KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY, + KeyringTraceFlag.WRAPPING_KEY_ENCRYPTED_DATA_KEY, + }, + ) + ], + ) + + def get_encryption_materials_with_encrypted_data_key_aes(): return EncryptionMaterials( algorithm=Algorithm.AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384, @@ -143,6 +223,24 @@ def get_decryption_materials_without_data_encryption_key(): ) +def get_decryption_materials_with_data_key(): + return DecryptionMaterials( + algorithm=Algorithm.AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384, + data_encryption_key=RawDataKey( + key_provider=MasterKeyInfo(provider_id=_PROVIDER_ID, key_info=_KEY_ID), + data_key=b'*!\xa1"^-(\xf3\x105\x05i@B\xc2\xa2\xb7\xdd\xd5\xd5\xa9\xddm\xfae\xa8\\$\xf9d\x1e(', + ), + encryption_context=_ENCRYPTION_CONTEXT, + verification_key=b"ex_verification_key", + keyring_trace=[ + KeyringTrace( + wrapping_key=MasterKeyInfo(provider_id=_PROVIDER_ID, key_info=_KEY_ID), + flags={KeyringTraceFlag.WRAPPING_KEY_DECRYPTED_DATA_KEY}, + ) + ], + ) + + def get_decryption_materials_with_data_encryption_key(): return DecryptionMaterials( algorithm=Algorithm.AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384, @@ -161,6 +259,73 @@ def get_decryption_materials_with_data_encryption_key(): ) +def get_decryption_materials_without_data_key(): + return DecryptionMaterials(encryption_context=_ENCRYPTION_CONTEXT, verification_key=b"ex_verification_key") + + +def get_multi_keyring_with_generator_and_children(): + return MultiKeyring( + generator=RawAESKeyring( + key_namespace=_PROVIDER_ID, + key_name=_KEY_ID, + wrapping_algorithm=WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING, + wrapping_key=_WRAPPING_KEY_AES, + ), + children=[ + RawRSAKeyring( + key_namespace=_PROVIDER_ID, + key_name=_KEY_ID, + wrapping_algorithm=WrappingAlgorithm.RSA_OAEP_SHA256_MGF1, + private_wrapping_key=rsa.generate_private_key( + public_exponent=65537, key_size=2048, backend=default_backend() + ), + ), + RawRSAKeyring( + key_namespace=_PROVIDER_ID, + key_name=_KEY_ID, + wrapping_algorithm=WrappingAlgorithm.RSA_OAEP_SHA256_MGF1, + private_wrapping_key=rsa.generate_private_key( + public_exponent=65537, key_size=2048, backend=default_backend() + ), + ), + ], + ) + + +def get_multi_keyring_with_no_children(): + return MultiKeyring( + generator=RawRSAKeyring( + key_namespace=_PROVIDER_ID, + key_name=_KEY_ID, + wrapping_algorithm=WrappingAlgorithm.RSA_OAEP_SHA256_MGF1, + private_wrapping_key=rsa.generate_private_key( + public_exponent=65537, key_size=2048, backend=default_backend() + ), + ) + ) + + +def get_multi_keyring_with_no_generator(): + return MultiKeyring( + children=[ + RawRSAKeyring( + key_namespace=_PROVIDER_ID, + key_name=_KEY_ID, + wrapping_algorithm=WrappingAlgorithm.RSA_OAEP_SHA256_MGF1, + private_wrapping_key=rsa.generate_private_key( + public_exponent=65537, key_size=2048, backend=default_backend() + ), + ), + RawAESKeyring( + key_namespace=_PROVIDER_ID, + key_name=_KEY_ID, + wrapping_algorithm=WrappingAlgorithm.AES_128_GCM_IV12_TAG16_NO_PADDING, + wrapping_key=_WRAPPING_KEY_AES, + ), + ] + ) + + def all_valid_kwargs(valid_kwargs): valid = [] for cls, kwargs_sets in valid_kwargs.items():