From dbccf9f414b93c99fd8251132614abead234ad94 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Thu, 20 Feb 2020 13:44:38 -0800 Subject: [PATCH 01/25] chore: add __all__ values for keyring modules --- src/aws_encryption_sdk/keyrings/base.py | 2 ++ src/aws_encryption_sdk/keyrings/multi.py | 2 ++ src/aws_encryption_sdk/keyrings/raw.py | 1 + 3 files changed, 5 insertions(+) diff --git a/src/aws_encryption_sdk/keyrings/base.py b/src/aws_encryption_sdk/keyrings/base.py index 32f810e47..e5ad9a2ea 100644 --- a/src/aws_encryption_sdk/keyrings/base.py +++ b/src/aws_encryption_sdk/keyrings/base.py @@ -23,6 +23,8 @@ # We only actually need these imports when running the mypy checks pass +__all__ = ("Keyring",) + class Keyring(object): """Parent interface for Keyring classes. diff --git a/src/aws_encryption_sdk/keyrings/multi.py b/src/aws_encryption_sdk/keyrings/multi.py index 4274c1c4d..87b14490e 100644 --- a/src/aws_encryption_sdk/keyrings/multi.py +++ b/src/aws_encryption_sdk/keyrings/multi.py @@ -31,6 +31,8 @@ # We only actually need these imports when running the mypy checks pass +__all__ = ("MultiKeyring",) + @attr.s class MultiKeyring(Keyring): diff --git a/src/aws_encryption_sdk/keyrings/raw.py b/src/aws_encryption_sdk/keyrings/raw.py index dea924eae..ef367ebad 100644 --- a/src/aws_encryption_sdk/keyrings/raw.py +++ b/src/aws_encryption_sdk/keyrings/raw.py @@ -46,6 +46,7 @@ # We only actually need these imports when running the mypy checks pass +__all__ = ("RawAESKeyring", "RawRSAKeyring") _LOGGER = logging.getLogger(__name__) From bd1eea9af0045ab81c27a28803d61da31a6e6d41 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Thu, 20 Feb 2020 14:04:28 -0800 Subject: [PATCH 02/25] chore: add versionadded tags for keyrings --- src/aws_encryption_sdk/keyrings/multi.py | 2 ++ src/aws_encryption_sdk/keyrings/raw.py | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/src/aws_encryption_sdk/keyrings/multi.py b/src/aws_encryption_sdk/keyrings/multi.py index 87b14490e..5792732ce 100644 --- a/src/aws_encryption_sdk/keyrings/multi.py +++ b/src/aws_encryption_sdk/keyrings/multi.py @@ -38,6 +38,8 @@ class MultiKeyring(Keyring): """Public class for Multi Keyring. + .. versionadded:: 1.5.0 + :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) diff --git a/src/aws_encryption_sdk/keyrings/raw.py b/src/aws_encryption_sdk/keyrings/raw.py index ef367ebad..60e7462ac 100644 --- a/src/aws_encryption_sdk/keyrings/raw.py +++ b/src/aws_encryption_sdk/keyrings/raw.py @@ -92,6 +92,8 @@ class RawAESKeyring(Keyring): """Generate an instance of Raw AES Keyring which encrypts using AES-GCM algorithm using wrapping key provided as a byte array + .. versionadded:: 1.5.0 + :param str key_namespace: String defining the keyring. :param bytes key_name: Key ID :param bytes wrapping_key: Encryption key with which to wrap plaintext data key. @@ -241,6 +243,8 @@ class RawRSAKeyring(Keyring): """Generate an instance of Raw RSA Keyring which performs asymmetric encryption and decryption using public and private keys provided + .. versionadded:: 1.5.0 + :param str key_namespace: String defining the keyring ID :param bytes key_name: Key ID :param private_wrapping_key: Private encryption key with which to wrap plaintext data key (optional) From 17ec28ea398eca03b544fa54089308b77d6ff027 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Fri, 21 Feb 2020 14:39:14 -0800 Subject: [PATCH 03/25] feat: plump keyrings into stream handlers and default CMM --- src/aws_encryption_sdk/exceptions.py | 7 + .../materials_managers/default.py | 153 ++++++++++++++++-- src/aws_encryption_sdk/streaming_client.py | 97 ++++++----- 3 files changed, 204 insertions(+), 53 deletions(-) diff --git a/src/aws_encryption_sdk/exceptions.py b/src/aws_encryption_sdk/exceptions.py index cd60ab6bd..fb699e25b 100644 --- a/src/aws_encryption_sdk/exceptions.py +++ b/src/aws_encryption_sdk/exceptions.py @@ -87,6 +87,13 @@ class SignatureKeyError(AWSEncryptionSDKClientError): """ +class InvalidCryptographicMaterialsError(AWSEncryptionSDKClientError): + """Exception class for errors encountered when attempting to validate cryptographic materials. + + .. versionadded:: 1.5.0 + """ + + class ActionNotAllowedError(AWSEncryptionSDKClientError): """Exception class for errors encountered when attempting to perform unallowed actions.""" diff --git a/src/aws_encryption_sdk/materials_managers/default.py b/src/aws_encryption_sdk/materials_managers/default.py index 6d10465a9..1db23f65b 100644 --- a/src/aws_encryption_sdk/materials_managers/default.py +++ b/src/aws_encryption_sdk/materials_managers/default.py @@ -14,16 +14,18 @@ import logging import attr +from attr.validators import instance_of, optional -from ..exceptions import MasterKeyProviderError, SerializationError -from ..internal.crypto.authentication import Signer, Verifier -from ..internal.crypto.elliptic_curve import generate_ecc_signing_key -from ..internal.defaults import ALGORITHM, ENCODED_SIGNER_KEY -from ..internal.str_ops import to_str -from ..internal.utils import prepare_data_keys -from ..key_providers.base import MasterKeyProvider -from . import DecryptionMaterials, EncryptionMaterials -from .base import CryptoMaterialsManager +from aws_encryption_sdk.exceptions import InvalidCryptographicMaterialsError, MasterKeyProviderError, SerializationError +from aws_encryption_sdk.internal.crypto.authentication import Signer, Verifier +from aws_encryption_sdk.internal.crypto.elliptic_curve import generate_ecc_signing_key +from aws_encryption_sdk.internal.defaults import ALGORITHM, ENCODED_SIGNER_KEY +from aws_encryption_sdk.internal.str_ops import to_str +from aws_encryption_sdk.internal.utils import prepare_data_keys +from aws_encryption_sdk.key_providers.base import MasterKeyProvider +from aws_encryption_sdk.keyrings.base import Keyring +from aws_encryption_sdk.materials_managers import DecryptionMaterials, EncryptionMaterials +from aws_encryption_sdk.materials_managers.base import CryptoMaterialsManager _LOGGER = logging.getLogger(__name__) @@ -34,12 +36,25 @@ class DefaultCryptoMaterialsManager(CryptoMaterialsManager): .. versionadded:: 1.3.0 - :param master_key_provider: Master key provider to use - :type master_key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider + .. versionadded:: 1.5.0 + The *keyring* parameter. + + :param MasterKeyProvider master_key_provider: Master key provider to use + (either `keyring` or `master_key_provider` is required) + :param Keyring keyring: Keyring to use + (either `keyring` or `master_key_provider` is required) """ algorithm = ALGORITHM - master_key_provider = attr.ib(validator=attr.validators.instance_of(MasterKeyProvider)) + master_key_provider = attr.ib(default=None, validator=optional(instance_of(MasterKeyProvider))) + keyring = attr.ib(default=None, validator=optional(instance_of(Keyring))) + + def __attrs_post_init__(self): + both = self.master_key_provider is not None and self.keyring is not None + neither = self.master_key_provider is None and self.keyring is None + + if both or neither: + raise TypeError("Exactly one of 'keyring' or 'master_key_provider' must be supplied.") def _generate_signing_key_and_update_encryption_context(self, algorithm, encryption_context): """Generates a signing key based on the provided algorithm. @@ -58,7 +73,7 @@ def _generate_signing_key_and_update_encryption_context(self, algorithm, encrypt encryption_context[ENCODED_SIGNER_KEY] = to_str(signer.encoded_public_key()) return signer.key_bytes() - def get_encryption_materials(self, request): + def _get_encryption_materials_using_master_key_provider(self, request): """Creates encryption materials using underlying master key provider. :param request: encryption materials request @@ -101,6 +116,64 @@ def get_encryption_materials(self, request): signing_key=signing_key, ) + def _get_encryption_materials_using_keyring(self, request): + """Creates encryption materials using underlying keyring. + + :param request: encryption materials request + :type request: aws_encryption_sdk.materials_managers.EncryptionMaterialsRequest + :returns: encryption materials + :rtype: aws_encryption_sdk.materials_managers.EncryptionMaterials + :raises InvalidCryptographicMaterialsError: if keyring cannot complete encryption materials + :raises InvalidCryptographicMaterialsError: + if encryption materials received from keyring do not match request + """ + algorithm = request.algorithm if request.algorithm is not None else self.algorithm + encryption_context = request.encryption_context.copy() + + signing_key = self._generate_signing_key_and_update_encryption_context(algorithm, encryption_context) + + expected_encryption_context = encryption_context.copy() + + encryption_materials = EncryptionMaterials( + algorithm=algorithm, encryption_context=encryption_context, signing_key=signing_key, + ) + + final_materials = self.keyring.on_encrypt(encryption_materials=encryption_materials) + + if not final_materials.is_complete: + raise InvalidCryptographicMaterialsError("Encryption materials are incomplete!") + + materials_are_valid = ( + final_materials.algorithm is algorithm, + final_materials.encryption_context == expected_encryption_context, + final_materials.signing_key is signing_key, + ) + if not all(materials_are_valid): + raise InvalidCryptographicMaterialsError("Encryption materials do not match request!") + + _LOGGER.debug("Post-encrypt encryption context: %s", encryption_context) + + return final_materials + + def get_encryption_materials(self, request): + """Creates encryption materials using underlying master key provider. + + :param request: encryption materials request + :type request: aws_encryption_sdk.materials_managers.EncryptionMaterialsRequest + :returns: encryption materials + :rtype: aws_encryption_sdk.materials_managers.EncryptionMaterials + :raises InvalidCryptographicMaterialsError: if keyring cannot complete encryption materials + :raises InvalidCryptographicMaterialsError: + if encryption materials received from keyring do not match request + :raises MasterKeyProviderError: if no master keys are available from the underlying master key provider + :raises MasterKeyProviderError: if the primary master key provided by the underlying master key provider + is not included in the full set of master keys provided by that provider + """ + if self.master_key_provider is not None: + return self._get_encryption_materials_using_master_key_provider(request) + + return self._get_encryption_materials_using_keyring(request) + def _load_verification_key_from_encryption_context(self, algorithm, encryption_context): """Loads the verification key from the encryption context if used by algorithm suite. @@ -124,7 +197,7 @@ def _load_verification_key_from_encryption_context(self, algorithm, encryption_c verifier = Verifier.from_encoded_point(algorithm=algorithm, encoded_point=encoded_verification_key) return verifier.key_bytes() - def decrypt_materials(self, request): + def _decrypt_materials_using_master_key_provider(self, request): """Obtains a plaintext data key from one or more encrypted data keys using underlying master key provider. @@ -143,3 +216,55 @@ def decrypt_materials(self, request): ) return DecryptionMaterials(data_key=data_key, verification_key=verification_key) + + def _decrypt_materials_using_keyring(self, request): + """Obtains a plaintext data key from one or more encrypted data keys + using underlying keyring. + + :param request: decrypt materials request + :type request: aws_encryption_sdk.materials_managers.DecryptionMaterialsRequest + :returns: decryption materials + :rtype: aws_encryption_sdk.materials_managers.DecryptionMaterials + :raises InvalidCryptographicMaterialsError: if keyring cannot complete decryption materials + :raises InvalidCryptographicMaterialsError: + if decryption materials received from keyring do not match request + """ + verification_key = self._load_verification_key_from_encryption_context( + algorithm=request.algorithm, encryption_context=request.encryption_context + ) + decryption_materials = DecryptionMaterials( + algorithm=request.algorithm, + encryption_context=request.encryption_context.copy(), + verification_key=verification_key, + ) + + final_materials = self.keyring.on_decrypt( + decryption_materials=decryption_materials, encrypted_data_keys=request.encrypted_data_keys + ) + + if not final_materials.is_complete: + raise InvalidCryptographicMaterialsError("Decryption materials are incomplete!") + + materials_are_valid = ( + final_materials.algorithm is request.algorithm, + final_materials.encryption_context == request.encryption_context, + final_materials.verification_key is verification_key, + ) + if not all(materials_are_valid): + raise InvalidCryptographicMaterialsError("Decryption materials do not match reqest!") + + return final_materials + + def decrypt_materials(self, request): + """Obtains a plaintext data key from one or more encrypted data keys + using underlying master key provider. + + :param request: decrypt materials request + :type request: aws_encryption_sdk.materials_managers.DecryptionMaterialsRequest + :returns: decryption materials + :rtype: aws_encryption_sdk.materials_managers.DecryptionMaterials + """ + if self.master_key_provider is not None: + return self._decrypt_materials_using_master_key_provider(request) + + return self._decrypt_materials_using_keyring(request) diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 504f68977..72c060532 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -19,6 +19,7 @@ import math import attr +from attr.validators import instance_of, optional import six import aws_encryption_sdk.internal.utils @@ -58,6 +59,7 @@ from aws_encryption_sdk.materials_managers.base import CryptoMaterialsManager from aws_encryption_sdk.materials_managers.default import DefaultCryptoMaterialsManager from aws_encryption_sdk.structures import MessageHeader +from aws_encryption_sdk.keyrings.base import Keyring _LOGGER = logging.getLogger(__name__) @@ -69,12 +71,14 @@ class _ClientConfig(object): :param source: Source data to encrypt or decrypt :type source: str, bytes, io.IOBase, or file - :param materials_manager: `CryptoMaterialsManager` from which to obtain cryptographic materials - (either `materials_manager` or `key_provider` required) - :type materials_manager: aws_encryption_sdk.materials_manager.base.CryptoMaterialsManager - :param key_provider: `MasterKeyProvider` from which to obtain data keys for encryption - (either `materials_manager` or `key_provider` required) - :type key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider + :param CryptoMaterialsManager materials_manager: + Cryptographic materials manager to use for encryption + (either ``materials_manager``, ``keyring``, ``key_provider`` required) + :param Keyring keyring: Keyring to use for encryption + (either ``materials_manager``, ``keyring``, ``key_provider`` required) + :param MasterKeyProvider key_provider: + Master key provider to use for encryption + (either ``materials_manager``, ``keyring``, ``key_provider`` required) :param int source_length: Length of source data (optional) .. note:: @@ -84,27 +88,34 @@ class _ClientConfig(object): source = attr.ib(hash=True, converter=aws_encryption_sdk.internal.utils.prep_stream_data) materials_manager = attr.ib( - hash=True, default=None, validator=attr.validators.optional(attr.validators.instance_of(CryptoMaterialsManager)) + hash=True, default=None, validator=optional(instance_of(CryptoMaterialsManager)) + ) + keyring = attr.ib( + default=None, validator=optional(instance_of(Keyring)) ) key_provider = attr.ib( - hash=True, default=None, validator=attr.validators.optional(attr.validators.instance_of(MasterKeyProvider)) + hash=True, default=None, validator=optional(instance_of(MasterKeyProvider)) ) source_length = attr.ib( - hash=True, default=None, validator=attr.validators.optional(attr.validators.instance_of(six.integer_types)) + hash=True, default=None, validator=optional(instance_of(six.integer_types)) ) line_length = attr.ib( - hash=True, default=LINE_LENGTH, validator=attr.validators.instance_of(six.integer_types) + hash=True, default=LINE_LENGTH, validator=instance_of(six.integer_types) ) # DEPRECATED: Value is no longer configurable here. Parameter left here to avoid breaking consumers. def __attrs_post_init__(self): """Normalize inputs to crypto material manager.""" - both_cmm_and_mkp_defined = self.materials_manager is not None and self.key_provider is not None - neither_cmm_nor_mkp_defined = self.materials_manager is None and self.key_provider is None + options_provided = [option is not None for option in (self.materials_manager, self.keyring, self.key_provider)] + provided_count = len([is_set for is_set in options_provided if is_set]) + + if provided_count != 1: + raise TypeError("Exactly one of 'materials_manager', 'keyring', or 'key_provider' must be provided") - if both_cmm_and_mkp_defined or neither_cmm_nor_mkp_defined: - raise TypeError("Exactly one of materials_manager or key_provider must be provided") if self.materials_manager is None: - self.materials_manager = DefaultCryptoMaterialsManager(master_key_provider=self.key_provider) + if self.key_provider is not None: + self.materials_manager = DefaultCryptoMaterialsManager(master_key_provider=self.key_provider) + else: + self.materials_manager = DefaultCryptoMaterialsManager(keyring=self.keyring) class _EncryptionStream(io.IOBase): @@ -313,12 +324,14 @@ class EncryptorConfig(_ClientConfig): :param source: Source data to encrypt or decrypt :type source: str, bytes, io.IOBase, or file - :param materials_manager: `CryptoMaterialsManager` from which to obtain cryptographic materials - (either `materials_manager` or `key_provider` required) - :type materials_manager: aws_encryption_sdk.materials_manager.base.CryptoMaterialsManager - :param key_provider: `MasterKeyProvider` from which to obtain data keys for encryption - (either `materials_manager` or `key_provider` required) - :type key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider + :param CryptoMaterialsManager materials_manager: + Cryptographic materials manager to use for encryption + (either ``materials_manager``, ``keyring``, ``key_provider`` required) + :param Keyring keyring: Keyring to use for encryption + (either ``materials_manager``, ``keyring``, ``key_provider`` required) + :param MasterKeyProvider key_provider: + Master key provider to use for encryption + (either ``materials_manager``, ``keyring``, ``key_provider`` required) :param int source_length: Length of source data (optional) .. note:: @@ -364,12 +377,14 @@ class StreamEncryptor(_EncryptionStream): # pylint: disable=too-many-instance-a :type config: aws_encryption_sdk.streaming_client.EncryptorConfig :param source: Source data to encrypt or decrypt :type source: str, bytes, io.IOBase, or file - :param materials_manager: `CryptoMaterialsManager` from which to obtain cryptographic materials - (either `materials_manager` or `key_provider` required) - :type materials_manager: aws_encryption_sdk.materials_manager.base.CryptoMaterialsManager - :param key_provider: `MasterKeyProvider` from which to obtain data keys for encryption - (either `materials_manager` or `key_provider` required) - :type key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider + :param CryptoMaterialsManager materials_manager: + Cryptographic materials manager to use for encryption + (either ``materials_manager``, ``keyring``, ``key_provider`` required) + :param Keyring keyring: Keyring to use for encryption + (either ``materials_manager``, ``keyring``, ``key_provider`` required) + :param MasterKeyProvider key_provider: + Master key provider to use for encryption + (either ``materials_manager``, ``keyring``, ``key_provider`` required) :param int source_length: Length of source data (optional) .. note:: @@ -669,12 +684,14 @@ class DecryptorConfig(_ClientConfig): :param source: Source data to encrypt or decrypt :type source: str, bytes, io.IOBase, or file - :param materials_manager: `CryptoMaterialsManager` from which to obtain cryptographic materials - (either `materials_manager` or `key_provider` required) - :type materials_manager: aws_encryption_sdk.materials_managers.base.CryptoMaterialsManager - :param key_provider: `MasterKeyProvider` from which to obtain data keys for decryption - (either `materials_manager` or `key_provider` required) - :type key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider + :param CryptoMaterialsManager materials_manager: + Cryptographic materials manager to use for encryption + (either ``materials_manager``, ``keyring``, ``key_provider`` required) + :param Keyring keyring: Keyring to use for encryption + (either ``materials_manager``, ``keyring``, ``key_provider`` required) + :param MasterKeyProvider key_provider: + Master key provider to use for encryption + (either ``materials_manager``, ``keyring``, ``key_provider`` required) :param int source_length: Length of source data (optional) .. note:: @@ -705,12 +722,14 @@ class StreamDecryptor(_EncryptionStream): # pylint: disable=too-many-instance-a :type config: aws_encryption_sdk.streaming_client.DecryptorConfig :param source: Source data to encrypt or decrypt :type source: str, bytes, io.IOBase, or file - :param materials_manager: `CryptoMaterialsManager` from which to obtain cryptographic materials - (either `materials_manager` or `key_provider` required) - :type materials_manager: aws_encryption_sdk.materials_managers.base.CryptoMaterialsManager - :param key_provider: `MasterKeyProvider` from which to obtain data keys for decryption - (either `materials_manager` or `key_provider` required) - :type key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider + :param CryptoMaterialsManager materials_manager: + Cryptographic materials manager to use for encryption + (either ``materials_manager``, ``keyring``, ``key_provider`` required) + :param Keyring keyring: Keyring to use for encryption + (either ``materials_manager``, ``keyring``, ``key_provider`` required) + :param MasterKeyProvider key_provider: + Master key provider to use for encryption + (either ``materials_manager``, ``keyring``, ``key_provider`` required) :param int source_length: Length of source data (optional) .. note:: From 263750de2b16f4eab211405249a6eb33b908a04a Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Fri, 21 Feb 2020 14:39:47 -0800 Subject: [PATCH 04/25] chore: incorporate keyrings into tests --- test/functional/test_client.py | 135 ++++++++++++++----- test/unit/materials_managers/test_default.py | 26 +++- test/unit/unit_test_utils.py | 116 +++++++++++++++- 3 files changed, 238 insertions(+), 39 deletions(-) diff --git a/test/functional/test_client.py b/test/functional/test_client.py index fb19e868a..bb598bd38 100644 --- a/test/functional/test_client.py +++ b/test/functional/test_client.py @@ -15,6 +15,7 @@ import io import logging +import itertools import attr import botocore.client @@ -34,9 +35,12 @@ from aws_encryption_sdk.internal.crypto.wrapping_keys import WrappingKey from aws_encryption_sdk.internal.defaults import LINE_LENGTH from aws_encryption_sdk.internal.formatting.encryption_context import serialize_encryption_context -from aws_encryption_sdk.key_providers.base import MasterKeyProviderConfig +from aws_encryption_sdk.key_providers.base import MasterKeyProviderConfig, MasterKeyProvider from aws_encryption_sdk.key_providers.raw import RawMasterKeyProvider from aws_encryption_sdk.materials_managers import DecryptionMaterialsRequest, EncryptionMaterialsRequest +from aws_encryption_sdk.keyrings.raw import RawRSAKeyring + +from ..unit.unit_test_utils import ephemeral_raw_aes_keyring, ephemeral_raw_rsa_keyring pytestmark = [pytest.mark.functional, pytest.mark.local] @@ -315,58 +319,121 @@ def test_encrypt_ciphertext_message(frame_length, algorithm, encryption_context) assert len(ciphertext) == results_length -@pytest.mark.parametrize( - "wrapping_algorithm, encryption_key_type, decryption_key_type", - ( - (WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING, EncryptionKeyType.SYMMETRIC, EncryptionKeyType.SYMMETRIC), +def _raw_aes(): + for symmetric_algorithm in (WrappingAlgorithm.AES_128_GCM_IV12_TAG16_NO_PADDING, WrappingAlgorithm.AES_192_GCM_IV12_TAG16_NO_PADDING, WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING): + yield pytest.param( + "key_provider", + build_fake_raw_key_provider(symmetric_algorithm, EncryptionKeyType.SYMMETRIC), + build_fake_raw_key_provider(symmetric_algorithm, EncryptionKeyType.SYMMETRIC), + id="raw AES master key provider -- {}".format(symmetric_algorithm.name) + ) + keyring = ephemeral_raw_aes_keyring(symmetric_algorithm) + yield pytest.param( + "keyring", + keyring, + keyring, + id="raw AES keyring -- {}".format(symmetric_algorithm.name) + ) + + for wrapping_algorithm, encryption_key_type, decryption_key_type in ( (WrappingAlgorithm.RSA_PKCS1, EncryptionKeyType.PRIVATE, EncryptionKeyType.PRIVATE), (WrappingAlgorithm.RSA_PKCS1, EncryptionKeyType.PUBLIC, EncryptionKeyType.PRIVATE), (WrappingAlgorithm.RSA_OAEP_SHA1_MGF1, EncryptionKeyType.PRIVATE, EncryptionKeyType.PRIVATE), (WrappingAlgorithm.RSA_OAEP_SHA1_MGF1, EncryptionKeyType.PUBLIC, EncryptionKeyType.PRIVATE), - ), -) -def test_encryption_cycle_raw_mkp(caplog, wrapping_algorithm, encryption_key_type, decryption_key_type): - caplog.set_level(logging.DEBUG) + ): + yield pytest.param( + "key_provider", + build_fake_raw_key_provider(wrapping_algorithm, encryption_key_type), + build_fake_raw_key_provider(wrapping_algorithm, decryption_key_type) + ) + + +def _raw_rsa(include_pre_sha2=True, include_sha2=True): + wrapping_algorithms = [] + if include_pre_sha2: + wrapping_algorithms.extend([WrappingAlgorithm.RSA_PKCS1, WrappingAlgorithm.RSA_OAEP_SHA1_MGF1]) + if include_sha2: + wrapping_algorithms.extend([ + WrappingAlgorithm.RSA_OAEP_SHA256_MGF1, + WrappingAlgorithm.RSA_OAEP_SHA384_MGF1, + WrappingAlgorithm.RSA_OAEP_SHA512_MGF1 + ]) + for wrapping_algorithm in wrapping_algorithms: + yield pytest.param( + "key_provider", + build_fake_raw_key_provider(wrapping_algorithm, EncryptionKeyType.PRIVATE), + build_fake_raw_key_provider(wrapping_algorithm, EncryptionKeyType.PRIVATE), + id="raw AES master key provider -- private encrypt, private decrypt -- {}".format(wrapping_algorithm.name) + ) + yield pytest.param( + "key_provider", + build_fake_raw_key_provider(wrapping_algorithm, EncryptionKeyType.PUBLIC), + build_fake_raw_key_provider(wrapping_algorithm, EncryptionKeyType.PRIVATE), + id="raw AES master key provider -- public encrypt, private decrypt -- {}".format(wrapping_algorithm.name) + ) + + private_keyring = ephemeral_raw_rsa_keyring(wrapping_algorithm=wrapping_algorithm) + public_keyring = RawRSAKeyring( + key_namespace=private_keyring.key_namespace, + key_name=private_keyring.key_name, + wrapping_algorithm=wrapping_algorithm, + public_wrapping_key=private_keyring._private_wrapping_key.public_key() + ) + yield pytest.param( + "keyring", + private_keyring, + private_keyring, + id="raw AES keyring -- private encrypt, private decrypt -- {}".format(wrapping_algorithm.name) + ) + yield pytest.param( + "keyring", + public_keyring, + private_keyring, + id="raw AES keyring -- public encrypt, private decrypt -- {}".format(wrapping_algorithm.name) + ) + + +def assert_key_not_logged(provider, log_capture): + if isinstance(provider, MasterKeyProvider): + for member in provider._members: + assert repr(member.config.wrapping_key._wrapping_key)[2:-1] not in log_capture + + +def run_raw_provider_check(log_capturer, param_name, encrypting_provider, decrypting_provider): + log_capturer.set_level(logging.DEBUG) + + encrypt_kwargs = {param_name: encrypting_provider} + decrypt_kwargs = {param_name: decrypting_provider} - encrypting_key_provider = build_fake_raw_key_provider(wrapping_algorithm, encryption_key_type) - decrypting_key_provider = build_fake_raw_key_provider(wrapping_algorithm, decryption_key_type) ciphertext, _ = aws_encryption_sdk.encrypt( source=VALUES["plaintext_128"], - key_provider=encrypting_key_provider, encryption_context=VALUES["encryption_context"], frame_length=0, + **encrypt_kwargs ) - plaintext, _ = aws_encryption_sdk.decrypt(source=ciphertext, key_provider=decrypting_key_provider) + plaintext, _ = aws_encryption_sdk.decrypt(source=ciphertext, **decrypt_kwargs) assert plaintext == VALUES["plaintext_128"] - for member in encrypting_key_provider._members: - assert repr(member.config.wrapping_key._wrapping_key)[2:-1] not in caplog.text + assert_key_not_logged(encrypting_provider, log_capturer.text) + + +@pytest.mark.parametrize( + "param_name, encrypting_provider, decrypting_provider", + itertools.chain.from_iterable((_raw_aes(), _raw_rsa(include_sha2=False))) +) +def test_encryption_cycle_raw_mkp(caplog, param_name, encrypting_provider, decrypting_provider): + run_raw_provider_check(caplog, param_name, encrypting_provider, decrypting_provider) @pytest.mark.skipif( not _mgf1_sha256_supported(), reason="MGF1-SHA2 not supported by this backend: OpenSSL required v1.0.2+" ) @pytest.mark.parametrize( - "wrapping_algorithm", - ( - WrappingAlgorithm.RSA_OAEP_SHA256_MGF1, - WrappingAlgorithm.RSA_OAEP_SHA384_MGF1, - WrappingAlgorithm.RSA_OAEP_SHA512_MGF1, - ), + "param_name, encrypting_provider, decrypting_provider", + _raw_rsa(include_pre_sha2=False) ) -@pytest.mark.parametrize("encryption_key_type", (EncryptionKeyType.PUBLIC, EncryptionKeyType.PRIVATE)) -def test_encryption_cycle_raw_mkp_openssl_102_plus(wrapping_algorithm, encryption_key_type): - decryption_key_type = EncryptionKeyType.PRIVATE - encrypting_key_provider = build_fake_raw_key_provider(wrapping_algorithm, encryption_key_type) - decrypting_key_provider = build_fake_raw_key_provider(wrapping_algorithm, decryption_key_type) - ciphertext, _ = aws_encryption_sdk.encrypt( - source=VALUES["plaintext_128"], - key_provider=encrypting_key_provider, - encryption_context=VALUES["encryption_context"], - frame_length=0, - ) - plaintext, _ = aws_encryption_sdk.decrypt(source=ciphertext, key_provider=decrypting_key_provider) - assert plaintext == VALUES["plaintext_128"] +def test_encryption_cycle_raw_mkp_openssl_102_plus(caplog, param_name, encrypting_provider, decrypting_provider): + run_raw_provider_check(caplog, param_name, encrypting_provider, decrypting_provider) @pytest.mark.parametrize( diff --git a/test/unit/materials_managers/test_default.py b/test/unit/materials_managers/test_default.py index 32fdc953a..394c49277 100644 --- a/test/unit/materials_managers/test_default.py +++ b/test/unit/materials_managers/test_default.py @@ -13,17 +13,24 @@ """Test suite for aws_encryption_sdk.materials_managers.default""" import pytest from mock import MagicMock, sentinel -from pytest_mock import mocker # noqa pylint: disable=unused-import import aws_encryption_sdk.materials_managers.default from aws_encryption_sdk.exceptions import MasterKeyProviderError, SerializationError from aws_encryption_sdk.identifiers import Algorithm from aws_encryption_sdk.internal.defaults import ALGORITHM, ENCODED_SIGNER_KEY from aws_encryption_sdk.key_providers.base import MasterKeyProvider +from aws_encryption_sdk.keyrings.base import Keyring from aws_encryption_sdk.materials_managers import EncryptionMaterials from aws_encryption_sdk.materials_managers.default import DefaultCryptoMaterialsManager from aws_encryption_sdk.structures import DataKey, EncryptedDataKey, MasterKeyInfo, RawDataKey +from ..unit_test_utils import ( + ephemeral_raw_aes_keyring, + ephemeral_raw_aes_master_key, + ephemeral_raw_rsa_keyring, + ephemeral_raw_rsa_master_key, +) + pytestmark = [pytest.mark.unit, pytest.mark.local] _DATA_KEY = DataKey( @@ -60,14 +67,25 @@ def build_cmm(): mock_mkp.decrypt_data_key_from_list.return_value = _DATA_KEY mock_mkp.master_keys_for_encryption.return_value = ( sentinel.primary_mk, - set([sentinel.primary_mk, sentinel.mk_a, sentinel.mk_b]), + {sentinel.primary_mk, sentinel.mk_a, sentinel.mk_b}, ) return DefaultCryptoMaterialsManager(master_key_provider=mock_mkp) -def test_attributes_fail(): +@pytest.mark.parametrize( + "kwargs", + ( + pytest.param(dict(), id="no parameters"), + pytest.param(dict(master_key_provider=None, keyring=None), id="explicit None for both"), + pytest.param( + dict(master_key_provider=ephemeral_raw_aes_master_key(), keyring=ephemeral_raw_aes_keyring()), + id="both provided", + ), + ), +) +def test_attributes_fail(kwargs): with pytest.raises(TypeError): - DefaultCryptoMaterialsManager(master_key_provider=None) + DefaultCryptoMaterialsManager(**kwargs) def test_attributes_default(): diff --git a/test/unit/unit_test_utils.py b/test/unit/unit_test_utils.py index 2bf1bc838..b78557f3e 100644 --- a/test/unit/unit_test_utils.py +++ b/test/unit/unit_test_utils.py @@ -17,10 +17,15 @@ import os from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import rsa -from aws_encryption_sdk.identifiers import Algorithm, KeyringTraceFlag, WrappingAlgorithm +from aws_encryption_sdk.exceptions import DecryptKeyError +from aws_encryption_sdk.identifiers import Algorithm, EncryptionKeyType, KeyringTraceFlag, WrappingAlgorithm +from aws_encryption_sdk.internal.crypto.wrapping_keys import WrappingKey from aws_encryption_sdk.internal.utils.streams import InsistentReaderBytesIO +from aws_encryption_sdk.key_providers.base import MasterKeyProvider, MasterKeyProviderConfig +from aws_encryption_sdk.key_providers.raw import RawMasterKey, RawMasterKeyProvider from aws_encryption_sdk.keyrings.base import Keyring from aws_encryption_sdk.keyrings.multi import MultiKeyring from aws_encryption_sdk.keyrings.raw import RawAESKeyring, RawRSAKeyring @@ -401,3 +406,112 @@ def assert_prepped_stream_identity(prepped_stream, wrapped_type): assert isinstance(prepped_stream, wrapped_type) # Check the wrapping streams assert isinstance(prepped_stream, InsistentReaderBytesIO) + + +def _generate_rsa_key_bytes(size): + # type: (int) -> bytes + private_key = rsa.generate_private_key(public_exponent=65537, key_size=size, backend=default_backend()) + return private_key.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.PKCS8, + encryption_algorithm=serialization.NoEncryption(), + ) + + +def ephemeral_raw_rsa_master_key(size=4096): + # type: (int) -> RawMasterKey + key_bytes = _generate_rsa_key_bytes(size) + return RawMasterKey( + provider_id="fake", + key_id="rsa-{}".format(size).encode("utf-8"), + wrapping_key=WrappingKey( + wrapping_algorithm=WrappingAlgorithm.RSA_OAEP_SHA256_MGF1, + wrapping_key=key_bytes, + wrapping_key_type=EncryptionKeyType.PRIVATE, + ), + ) + + +def ephemeral_raw_rsa_keyring(size=4096, wrapping_algorithm=WrappingAlgorithm.RSA_OAEP_SHA256_MGF1): + # type: (int, WrappingAlgorithm) -> RawRSAKeyring + key_bytes = _generate_rsa_key_bytes(size) + return RawRSAKeyring.from_pem_encoding( + key_namespace="fake", + key_name="rsa-{}".format(size).encode("utf-8"), + wrapping_algorithm=wrapping_algorithm, + private_encoded_key=key_bytes, + ) + + +def ephemeral_raw_aes_master_key(wrapping_algorithm=WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING): + # type: (WrappingAlgorithm) -> RawMasterKey + key_length = wrapping_algorithm.algorithm.data_key_len + key = os.urandom(key_length) + return RawMasterKey( + provider_id="fake", + key_id="aes-{}".format(key_length * 8).encode("utf-8"), + wrapping_key=WrappingKey( + wrapping_algorithm=wrapping_algorithm, + wrapping_key=key, + wrapping_key_type=EncryptionKeyType.SYMMETRIC, + ), + ) + + +def ephemeral_raw_aes_keyring(wrapping_algorithm=WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING): + # type: (WrappingAlgorithm) -> RawAESKeyring + key_length = wrapping_algorithm.algorithm.data_key_len + key = os.urandom(key_length) + return RawAESKeyring( + key_namespace="fake", + key_name="aes-{}".format(key_length * 8).encode("utf-8"), + wrapping_algorithm=wrapping_algorithm, + wrapping_key=key, + ) + + +class EphemeralRawMasterKeyProvider(RawMasterKeyProvider): + """Master key provider with raw master keys that are generated on each initialization.""" + + provider_id = "fake" + + def __init__(self): + self.__keys = {b"aes-256": ephemeral_raw_aes_master_key(256), b"rsa-4096": ephemeral_raw_rsa_master_key(4096)} + + def _get_raw_key(self, key_id): + return self.__keys[key_id].config.wrapping_key + + +class EmptyMasterKeyProvider(MasterKeyProvider): + """Master key provider that provides no master keys.""" + + provider_id = "empty" + _config_class = MasterKeyProviderConfig + vend_masterkey_on_decrypt = False + + def _new_master_key(self, key_id): + raise Exception("How did this happen??") + + def master_keys_for_encryption(self, encryption_context, plaintext_rostream, plaintext_length=None): + return ephemeral_raw_aes_master_key(), [] + + +class DisjointMasterKeyProvider(MasterKeyProvider): + """Master key provider that does not provide the primary master key in the additional master keys.""" + + provider_id = "disjoint" + _config_class = MasterKeyProviderConfig + vend_masterkey_on_decrypt = False + + def _new_master_key(self, key_id): + raise Exception("How did this happen??") + + def master_keys_for_encryption(self, encryption_context, plaintext_rostream, plaintext_length=None): + return ephemeral_raw_aes_master_key(), [ephemeral_raw_rsa_master_key()] + + +class FailingDecryptMasterKeyProvider(EphemeralRawMasterKeyProvider): + """EphemeralRawMasterKeyProvider that cannot decrypt.""" + + def decrypt_data_key(self, encrypted_data_key, algorithm, encryption_context): + raise DecryptKeyError("FailingDecryptMasterKeyProvider cannot decrypt!") From fa1a6ee69110092edfbe58ff18bf085192bed4de Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Fri, 21 Feb 2020 16:53:35 -0800 Subject: [PATCH 05/25] add raw AES keyring-MKP compatibility tests --- src/aws_encryption_sdk/streaming_client.py | 20 ++--- test/functional/test_client.py | 98 +++++++++++++--------- test/unit/unit_test_utils.py | 22 ++--- 3 files changed, 77 insertions(+), 63 deletions(-) diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 72c060532..f67e9f384 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -19,8 +19,8 @@ import math import attr -from attr.validators import instance_of, optional import six +from attr.validators import instance_of, optional import aws_encryption_sdk.internal.utils from aws_encryption_sdk.exceptions import ( @@ -55,11 +55,11 @@ serialize_non_framed_open, ) from aws_encryption_sdk.key_providers.base import MasterKeyProvider +from aws_encryption_sdk.keyrings.base import Keyring from aws_encryption_sdk.materials_managers import DecryptionMaterialsRequest, EncryptionMaterialsRequest from aws_encryption_sdk.materials_managers.base import CryptoMaterialsManager from aws_encryption_sdk.materials_managers.default import DefaultCryptoMaterialsManager from aws_encryption_sdk.structures import MessageHeader -from aws_encryption_sdk.keyrings.base import Keyring _LOGGER = logging.getLogger(__name__) @@ -87,18 +87,10 @@ class _ClientConfig(object): """ source = attr.ib(hash=True, converter=aws_encryption_sdk.internal.utils.prep_stream_data) - materials_manager = attr.ib( - hash=True, default=None, validator=optional(instance_of(CryptoMaterialsManager)) - ) - keyring = attr.ib( - default=None, validator=optional(instance_of(Keyring)) - ) - key_provider = attr.ib( - hash=True, default=None, validator=optional(instance_of(MasterKeyProvider)) - ) - source_length = attr.ib( - hash=True, default=None, validator=optional(instance_of(six.integer_types)) - ) + materials_manager = attr.ib(hash=True, default=None, validator=optional(instance_of(CryptoMaterialsManager))) + keyring = attr.ib(default=None, validator=optional(instance_of(Keyring))) + key_provider = attr.ib(hash=True, default=None, validator=optional(instance_of(MasterKeyProvider))) + source_length = attr.ib(hash=True, default=None, validator=optional(instance_of(six.integer_types))) line_length = attr.ib( hash=True, default=LINE_LENGTH, validator=instance_of(six.integer_types) ) # DEPRECATED: Value is no longer configurable here. Parameter left here to avoid breaking consumers. diff --git a/test/functional/test_client.py b/test/functional/test_client.py index bb598bd38..9fdda6795 100644 --- a/test/functional/test_client.py +++ b/test/functional/test_client.py @@ -14,8 +14,8 @@ from __future__ import division import io -import logging import itertools +import logging import attr import botocore.client @@ -35,12 +35,17 @@ from aws_encryption_sdk.internal.crypto.wrapping_keys import WrappingKey from aws_encryption_sdk.internal.defaults import LINE_LENGTH from aws_encryption_sdk.internal.formatting.encryption_context import serialize_encryption_context -from aws_encryption_sdk.key_providers.base import MasterKeyProviderConfig, MasterKeyProvider +from aws_encryption_sdk.key_providers.base import MasterKeyProvider, MasterKeyProviderConfig from aws_encryption_sdk.key_providers.raw import RawMasterKeyProvider -from aws_encryption_sdk.materials_managers import DecryptionMaterialsRequest, EncryptionMaterialsRequest from aws_encryption_sdk.keyrings.raw import RawRSAKeyring +from aws_encryption_sdk.materials_managers import DecryptionMaterialsRequest, EncryptionMaterialsRequest -from ..unit.unit_test_utils import ephemeral_raw_aes_keyring, ephemeral_raw_rsa_keyring +from ..unit.unit_test_utils import ( + ephemeral_raw_aes_keyring, + ephemeral_raw_aes_master_key, + ephemeral_raw_rsa_keyring, + ephemeral_raw_rsa_master_key, +) pytestmark = [pytest.mark.functional, pytest.mark.local] @@ -320,31 +325,37 @@ def test_encrypt_ciphertext_message(frame_length, algorithm, encryption_context) def _raw_aes(): - for symmetric_algorithm in (WrappingAlgorithm.AES_128_GCM_IV12_TAG16_NO_PADDING, WrappingAlgorithm.AES_192_GCM_IV12_TAG16_NO_PADDING, WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING): + for symmetric_algorithm in ( + WrappingAlgorithm.AES_128_GCM_IV12_TAG16_NO_PADDING, + WrappingAlgorithm.AES_192_GCM_IV12_TAG16_NO_PADDING, + WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING, + ): yield pytest.param( "key_provider", build_fake_raw_key_provider(symmetric_algorithm, EncryptionKeyType.SYMMETRIC), + "key_provider", build_fake_raw_key_provider(symmetric_algorithm, EncryptionKeyType.SYMMETRIC), - id="raw AES master key provider -- {}".format(symmetric_algorithm.name) + id="raw AES master key provider -- {}".format(symmetric_algorithm.name), ) keyring = ephemeral_raw_aes_keyring(symmetric_algorithm) yield pytest.param( + "keyring", keyring, "keyring", keyring, id="raw AES keyring -- {}".format(symmetric_algorithm.name) + ) + + mkp = ephemeral_raw_aes_master_key(wrapping_algorithm=symmetric_algorithm, key=keyring._wrapping_key) + yield pytest.param( + "key_provider", + mkp, "keyring", keyring, - keyring, - id="raw AES keyring -- {}".format(symmetric_algorithm.name) + id="raw AES -- encrypt with master key provider and decrypt with keyring -- {}".format(symmetric_algorithm), ) - - for wrapping_algorithm, encryption_key_type, decryption_key_type in ( - (WrappingAlgorithm.RSA_PKCS1, EncryptionKeyType.PRIVATE, EncryptionKeyType.PRIVATE), - (WrappingAlgorithm.RSA_PKCS1, EncryptionKeyType.PUBLIC, EncryptionKeyType.PRIVATE), - (WrappingAlgorithm.RSA_OAEP_SHA1_MGF1, EncryptionKeyType.PRIVATE, EncryptionKeyType.PRIVATE), - (WrappingAlgorithm.RSA_OAEP_SHA1_MGF1, EncryptionKeyType.PUBLIC, EncryptionKeyType.PRIVATE), - ): yield pytest.param( + "keyring", + keyring, "key_provider", - build_fake_raw_key_provider(wrapping_algorithm, encryption_key_type), - build_fake_raw_key_provider(wrapping_algorithm, decryption_key_type) + mkp, + id="raw AES -- encrypt with keyring and decrypt with master key provider -- {}".format(symmetric_algorithm), ) @@ -353,23 +364,27 @@ def _raw_rsa(include_pre_sha2=True, include_sha2=True): if include_pre_sha2: wrapping_algorithms.extend([WrappingAlgorithm.RSA_PKCS1, WrappingAlgorithm.RSA_OAEP_SHA1_MGF1]) if include_sha2: - wrapping_algorithms.extend([ - WrappingAlgorithm.RSA_OAEP_SHA256_MGF1, - WrappingAlgorithm.RSA_OAEP_SHA384_MGF1, - WrappingAlgorithm.RSA_OAEP_SHA512_MGF1 - ]) + wrapping_algorithms.extend( + [ + WrappingAlgorithm.RSA_OAEP_SHA256_MGF1, + WrappingAlgorithm.RSA_OAEP_SHA384_MGF1, + WrappingAlgorithm.RSA_OAEP_SHA512_MGF1, + ] + ) for wrapping_algorithm in wrapping_algorithms: yield pytest.param( "key_provider", build_fake_raw_key_provider(wrapping_algorithm, EncryptionKeyType.PRIVATE), + "key_provider", build_fake_raw_key_provider(wrapping_algorithm, EncryptionKeyType.PRIVATE), - id="raw AES master key provider -- private encrypt, private decrypt -- {}".format(wrapping_algorithm.name) + id="raw RSA master key provider -- private encrypt, private decrypt -- {}".format(wrapping_algorithm.name), ) yield pytest.param( "key_provider", build_fake_raw_key_provider(wrapping_algorithm, EncryptionKeyType.PUBLIC), + "key_provider", build_fake_raw_key_provider(wrapping_algorithm, EncryptionKeyType.PRIVATE), - id="raw AES master key provider -- public encrypt, private decrypt -- {}".format(wrapping_algorithm.name) + id="raw RSA master key provider -- public encrypt, private decrypt -- {}".format(wrapping_algorithm.name), ) private_keyring = ephemeral_raw_rsa_keyring(wrapping_algorithm=wrapping_algorithm) @@ -377,19 +392,21 @@ def _raw_rsa(include_pre_sha2=True, include_sha2=True): key_namespace=private_keyring.key_namespace, key_name=private_keyring.key_name, wrapping_algorithm=wrapping_algorithm, - public_wrapping_key=private_keyring._private_wrapping_key.public_key() + public_wrapping_key=private_keyring._private_wrapping_key.public_key(), ) yield pytest.param( "keyring", private_keyring, + "keyring", private_keyring, - id="raw AES keyring -- private encrypt, private decrypt -- {}".format(wrapping_algorithm.name) + id="raw RSA keyring -- private encrypt, private decrypt -- {}".format(wrapping_algorithm.name), ) yield pytest.param( "keyring", public_keyring, + "keyring", private_keyring, - id="raw AES keyring -- public encrypt, private decrypt -- {}".format(wrapping_algorithm.name) + id="raw RSA keyring -- public encrypt, private decrypt -- {}".format(wrapping_algorithm.name), ) @@ -399,11 +416,13 @@ def assert_key_not_logged(provider, log_capture): assert repr(member.config.wrapping_key._wrapping_key)[2:-1] not in log_capture -def run_raw_provider_check(log_capturer, param_name, encrypting_provider, decrypting_provider): +def run_raw_provider_check( + log_capturer, encrypt_param_name, encrypting_provider, decrypt_param_name, decrypting_provider +): log_capturer.set_level(logging.DEBUG) - encrypt_kwargs = {param_name: encrypting_provider} - decrypt_kwargs = {param_name: decrypting_provider} + encrypt_kwargs = {encrypt_param_name: encrypting_provider} + decrypt_kwargs = {decrypt_param_name: decrypting_provider} ciphertext, _ = aws_encryption_sdk.encrypt( source=VALUES["plaintext_128"], @@ -418,22 +437,25 @@ def run_raw_provider_check(log_capturer, param_name, encrypting_provider, decryp @pytest.mark.parametrize( - "param_name, encrypting_provider, decrypting_provider", - itertools.chain.from_iterable((_raw_aes(), _raw_rsa(include_sha2=False))) + "encrypt_param_name, encrypting_provider, decrypt_param_name, decrypting_provider", + itertools.chain.from_iterable((_raw_aes(), _raw_rsa(include_sha2=False))), ) -def test_encryption_cycle_raw_mkp(caplog, param_name, encrypting_provider, decrypting_provider): - run_raw_provider_check(caplog, param_name, encrypting_provider, decrypting_provider) +def test_encryption_cycle_raw_mkp( + caplog, encrypt_param_name, encrypting_provider, decrypt_param_name, decrypting_provider +): + run_raw_provider_check(caplog, encrypt_param_name, encrypting_provider, decrypt_param_name, decrypting_provider) @pytest.mark.skipif( not _mgf1_sha256_supported(), reason="MGF1-SHA2 not supported by this backend: OpenSSL required v1.0.2+" ) @pytest.mark.parametrize( - "param_name, encrypting_provider, decrypting_provider", - _raw_rsa(include_pre_sha2=False) + "encrypt_param_name, encrypting_provider, decrypt_param_name, decrypting_provider", _raw_rsa(include_pre_sha2=False) ) -def test_encryption_cycle_raw_mkp_openssl_102_plus(caplog, param_name, encrypting_provider, decrypting_provider): - run_raw_provider_check(caplog, param_name, encrypting_provider, decrypting_provider) +def test_encryption_cycle_raw_mkp_openssl_102_plus( + caplog, encrypt_param_name, encrypting_provider, decrypt_param_name, decrypting_provider +): + run_raw_provider_check(caplog, encrypt_param_name, encrypting_provider, decrypt_param_name, decrypting_provider) @pytest.mark.parametrize( diff --git a/test/unit/unit_test_utils.py b/test/unit/unit_test_utils.py index b78557f3e..a714fde20 100644 --- a/test/unit/unit_test_utils.py +++ b/test/unit/unit_test_utils.py @@ -33,7 +33,7 @@ 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 + from typing import Iterable, Optional # noqa pylint: disable=unused-import except ImportError: # pragma: no cover # We only actually need these imports when running the mypy checks pass @@ -433,7 +433,7 @@ def ephemeral_raw_rsa_master_key(size=4096): def ephemeral_raw_rsa_keyring(size=4096, wrapping_algorithm=WrappingAlgorithm.RSA_OAEP_SHA256_MGF1): - # type: (int, WrappingAlgorithm) -> RawRSAKeyring + # type: (int, WrappingAlgorithm, Optional[bytes]) -> RawRSAKeyring key_bytes = _generate_rsa_key_bytes(size) return RawRSAKeyring.from_pem_encoding( key_namespace="fake", @@ -443,25 +443,25 @@ def ephemeral_raw_rsa_keyring(size=4096, wrapping_algorithm=WrappingAlgorithm.RS ) -def ephemeral_raw_aes_master_key(wrapping_algorithm=WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING): - # type: (WrappingAlgorithm) -> RawMasterKey +def ephemeral_raw_aes_master_key(wrapping_algorithm=WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING, key=None): + # type: (WrappingAlgorithm, Optional[bytes]) -> RawMasterKey key_length = wrapping_algorithm.algorithm.data_key_len - key = os.urandom(key_length) + if key is None: + key = os.urandom(key_length) return RawMasterKey( provider_id="fake", key_id="aes-{}".format(key_length * 8).encode("utf-8"), wrapping_key=WrappingKey( - wrapping_algorithm=wrapping_algorithm, - wrapping_key=key, - wrapping_key_type=EncryptionKeyType.SYMMETRIC, + wrapping_algorithm=wrapping_algorithm, wrapping_key=key, wrapping_key_type=EncryptionKeyType.SYMMETRIC, ), ) -def ephemeral_raw_aes_keyring(wrapping_algorithm=WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING): - # type: (WrappingAlgorithm) -> RawAESKeyring +def ephemeral_raw_aes_keyring(wrapping_algorithm=WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING, key=None): + # type: (WrappingAlgorithm, Optional[bytes]) -> RawAESKeyring key_length = wrapping_algorithm.algorithm.data_key_len - key = os.urandom(key_length) + if key is None: + key = os.urandom(key_length) return RawAESKeyring( key_namespace="fake", key_name="aes-{}".format(key_length * 8).encode("utf-8"), From fce9fe118a2bc95d0aa8aca1eda67c6dccebb185 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Fri, 21 Feb 2020 17:16:44 -0800 Subject: [PATCH 06/25] add raw RSA MKP-keyring compatibility tests --- test/functional/test_client.py | 20 +++++++++++++++++++ test/unit/unit_test_utils.py | 35 +++++++++++++++++++++++++++++++++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/test/functional/test_client.py b/test/functional/test_client.py index 9fdda6795..42cb86ba6 100644 --- a/test/functional/test_client.py +++ b/test/functional/test_client.py @@ -45,6 +45,7 @@ ephemeral_raw_aes_master_key, ephemeral_raw_rsa_keyring, ephemeral_raw_rsa_master_key, + raw_rsa_mkps_from_keyring, ) pytestmark = [pytest.mark.functional, pytest.mark.local] @@ -408,6 +409,25 @@ def _raw_rsa(include_pre_sha2=True, include_sha2=True): private_keyring, id="raw RSA keyring -- public encrypt, private decrypt -- {}".format(wrapping_algorithm.name), ) + private_mkp, public_mkp = raw_rsa_mkps_from_keyring(private_keyring) + yield pytest.param( + "key_provider", + private_mkp, + "keyring", + private_keyring, + id="raw RSA keyring -- encrypt with master key provider and decrypt with keyring -- {}".format( + wrapping_algorithm + ), + ) + yield pytest.param( + "keyring", + private_keyring, + "key_provider", + private_mkp, + id="raw RSA keyring -- encrypt with keyring and decrypt with master key provider -- {}".format( + wrapping_algorithm + ), + ) def assert_key_not_logged(provider, log_capture): diff --git a/test/unit/unit_test_utils.py b/test/unit/unit_test_utils.py index a714fde20..910755785 100644 --- a/test/unit/unit_test_utils.py +++ b/test/unit/unit_test_utils.py @@ -433,7 +433,7 @@ def ephemeral_raw_rsa_master_key(size=4096): def ephemeral_raw_rsa_keyring(size=4096, wrapping_algorithm=WrappingAlgorithm.RSA_OAEP_SHA256_MGF1): - # type: (int, WrappingAlgorithm, Optional[bytes]) -> RawRSAKeyring + # type: (int, WrappingAlgorithm) -> RawRSAKeyring key_bytes = _generate_rsa_key_bytes(size) return RawRSAKeyring.from_pem_encoding( key_namespace="fake", @@ -443,6 +443,39 @@ def ephemeral_raw_rsa_keyring(size=4096, wrapping_algorithm=WrappingAlgorithm.RS ) +def raw_rsa_mkps_from_keyring(keyring): + # type: (RawRSAKeyring) -> (MasterKeyProvider, MasterKeyProvider) + """Constructs a private and public raw RSA MKP using the private key in the raw RSA keyring.""" + private_key = keyring._private_wrapping_key + private_pem = private_key.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.PKCS8, + encryption_algorithm=serialization.NoEncryption(), + ) + public_pem = private_key.public_key().public_bytes( + encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo + ) + private_key_mkp = RawMasterKey( + provider_id=keyring.key_namespace, + key_id=keyring.key_name, + wrapping_key=WrappingKey( + wrapping_algorithm=keyring._wrapping_algorithm, + wrapping_key=private_pem, + wrapping_key_type=EncryptionKeyType.PRIVATE, + ), + ) + public_key_mkp = RawMasterKey( + provider_id=keyring.key_namespace, + key_id=keyring.key_name, + wrapping_key=WrappingKey( + wrapping_algorithm=keyring._wrapping_algorithm, + wrapping_key=public_pem, + wrapping_key_type=EncryptionKeyType.PUBLIC, + ), + ) + return private_key_mkp, public_key_mkp + + def ephemeral_raw_aes_master_key(wrapping_algorithm=WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING, key=None): # type: (WrappingAlgorithm, Optional[bytes]) -> RawMasterKey key_length = wrapping_algorithm.algorithm.data_key_len From 2a69b2248087286d1781e18ba97c2c8918505da4 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Fri, 21 Feb 2020 17:23:41 -0800 Subject: [PATCH 07/25] fix: fix integ test to run when default region is set --- test/integration/test_client.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/integration/test_client.py b/test/integration/test_client.py index 26df431dc..183f9d49f 100644 --- a/test/integration/test_client.py +++ b/test/integration/test_client.py @@ -64,12 +64,13 @@ def test_encrypt_verify_user_agent_kms_master_key(caplog): def test_remove_bad_client(): test = KMSMasterKeyProvider() - test.add_regional_client("us-fakey-12") + fake_region = "us-fakey-12" + test.add_regional_client(fake_region) with pytest.raises(BotoCoreError): - test._regional_clients["us-fakey-12"].list_keys() + test._regional_clients[fake_region].list_keys() - assert not test._regional_clients + assert fake_region not in test._regional_clients def test_regional_client_does_not_modify_botocore_session(caplog): From 20872fbb7c6f6e199523ebbb69311a9d918de158 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Fri, 21 Feb 2020 18:00:47 -0800 Subject: [PATCH 08/25] chore: add public-private keyring-MKP compat tests --- .../materials_managers/default.py | 1 + test/functional/test_client.py | 24 ++++++++++++++++--- test/unit/materials_managers/test_default.py | 8 +------ 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/aws_encryption_sdk/materials_managers/default.py b/src/aws_encryption_sdk/materials_managers/default.py index 1db23f65b..4b81c2a44 100644 --- a/src/aws_encryption_sdk/materials_managers/default.py +++ b/src/aws_encryption_sdk/materials_managers/default.py @@ -50,6 +50,7 @@ class DefaultCryptoMaterialsManager(CryptoMaterialsManager): keyring = attr.ib(default=None, validator=optional(instance_of(Keyring))) def __attrs_post_init__(self): + """Apply input requirements.""" both = self.master_key_provider is not None and self.keyring is not None neither = self.master_key_provider is None and self.keyring is None diff --git a/test/functional/test_client.py b/test/functional/test_client.py index 42cb86ba6..80cf8668b 100644 --- a/test/functional/test_client.py +++ b/test/functional/test_client.py @@ -44,7 +44,6 @@ ephemeral_raw_aes_keyring, ephemeral_raw_aes_master_key, ephemeral_raw_rsa_keyring, - ephemeral_raw_rsa_master_key, raw_rsa_mkps_from_keyring, ) @@ -410,12 +409,22 @@ def _raw_rsa(include_pre_sha2=True, include_sha2=True): id="raw RSA keyring -- public encrypt, private decrypt -- {}".format(wrapping_algorithm.name), ) private_mkp, public_mkp = raw_rsa_mkps_from_keyring(private_keyring) + yield pytest.param( "key_provider", private_mkp, "keyring", private_keyring, - id="raw RSA keyring -- encrypt with master key provider and decrypt with keyring -- {}".format( + id="raw RSA keyring -- private master key provider encrypt and private keyring decrypt -- {}".format( + wrapping_algorithm + ), + ) + yield pytest.param( + "key_provider", + public_mkp, + "keyring", + private_keyring, + id="raw RSA keyring -- public master key provider encrypt and private keyring decrypt -- {}".format( wrapping_algorithm ), ) @@ -424,7 +433,16 @@ def _raw_rsa(include_pre_sha2=True, include_sha2=True): private_keyring, "key_provider", private_mkp, - id="raw RSA keyring -- encrypt with keyring and decrypt with master key provider -- {}".format( + id="raw RSA keyring -- private keyring encrypt and private master key provider decrypt -- {}".format( + wrapping_algorithm + ), + ) + yield pytest.param( + "keyring", + public_keyring, + "key_provider", + private_mkp, + id="raw RSA keyring -- public keyring encrypt and private master key provider decrypt -- {}".format( wrapping_algorithm ), ) diff --git a/test/unit/materials_managers/test_default.py b/test/unit/materials_managers/test_default.py index 394c49277..cd96b3411 100644 --- a/test/unit/materials_managers/test_default.py +++ b/test/unit/materials_managers/test_default.py @@ -19,17 +19,11 @@ from aws_encryption_sdk.identifiers import Algorithm from aws_encryption_sdk.internal.defaults import ALGORITHM, ENCODED_SIGNER_KEY from aws_encryption_sdk.key_providers.base import MasterKeyProvider -from aws_encryption_sdk.keyrings.base import Keyring from aws_encryption_sdk.materials_managers import EncryptionMaterials from aws_encryption_sdk.materials_managers.default import DefaultCryptoMaterialsManager from aws_encryption_sdk.structures import DataKey, EncryptedDataKey, MasterKeyInfo, RawDataKey -from ..unit_test_utils import ( - ephemeral_raw_aes_keyring, - ephemeral_raw_aes_master_key, - ephemeral_raw_rsa_keyring, - ephemeral_raw_rsa_master_key, -) +from ..unit_test_utils import ephemeral_raw_aes_keyring, ephemeral_raw_aes_master_key pytestmark = [pytest.mark.unit, pytest.mark.local] From 87fed8a1ce815b892ea2216d8e02492703d1f83c Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Mon, 24 Feb 2020 15:27:49 -0800 Subject: [PATCH 09/25] fix: fix typo --- src/aws_encryption_sdk/materials_managers/default.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aws_encryption_sdk/materials_managers/default.py b/src/aws_encryption_sdk/materials_managers/default.py index 4b81c2a44..66c185495 100644 --- a/src/aws_encryption_sdk/materials_managers/default.py +++ b/src/aws_encryption_sdk/materials_managers/default.py @@ -252,7 +252,7 @@ def _decrypt_materials_using_keyring(self, request): final_materials.verification_key is verification_key, ) if not all(materials_are_valid): - raise InvalidCryptographicMaterialsError("Decryption materials do not match reqest!") + raise InvalidCryptographicMaterialsError("Decryption materials do not match request!") return final_materials From 8a27aea212695d49ca5d248125c6338304820ae2 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Mon, 24 Feb 2020 15:28:27 -0800 Subject: [PATCH 10/25] add DefaultCryptographicMaterialsProvider tests for keyrings that return incomplete or broken materials --- test/unit/materials_managers/test_default.py | 94 ++++++++++++- test/unit/unit_test_utils.py | 137 +++++++++++++++++-- 2 files changed, 215 insertions(+), 16 deletions(-) diff --git a/test/unit/materials_managers/test_default.py b/test/unit/materials_managers/test_default.py index cd96b3411..5a353bdb9 100644 --- a/test/unit/materials_managers/test_default.py +++ b/test/unit/materials_managers/test_default.py @@ -15,15 +15,24 @@ from mock import MagicMock, sentinel import aws_encryption_sdk.materials_managers.default -from aws_encryption_sdk.exceptions import MasterKeyProviderError, SerializationError -from aws_encryption_sdk.identifiers import Algorithm +from aws_encryption_sdk.exceptions import InvalidCryptographicMaterialsError, MasterKeyProviderError, SerializationError +from aws_encryption_sdk.identifiers import Algorithm, WrappingAlgorithm from aws_encryption_sdk.internal.defaults import ALGORITHM, ENCODED_SIGNER_KEY from aws_encryption_sdk.key_providers.base import MasterKeyProvider -from aws_encryption_sdk.materials_managers import EncryptionMaterials +from aws_encryption_sdk.materials_managers import ( + DecryptionMaterialsRequest, + EncryptionMaterials, + EncryptionMaterialsRequest, +) from aws_encryption_sdk.materials_managers.default import DefaultCryptoMaterialsManager from aws_encryption_sdk.structures import DataKey, EncryptedDataKey, MasterKeyInfo, RawDataKey -from ..unit_test_utils import ephemeral_raw_aes_keyring, ephemeral_raw_aes_master_key +from ..unit_test_utils import ( + BrokenKeyring, + OnlyGenerateKeyring, + ephemeral_raw_aes_keyring, + ephemeral_raw_aes_master_key, +) pytestmark = [pytest.mark.unit, pytest.mark.local] @@ -253,3 +262,80 @@ def test_decrypt_materials(mocker, patch_for_dcmm_decrypt): ) assert test.data_key == RawDataKey.from_data_key(cmm.master_key_provider.decrypt_data_key_from_list.return_value) assert test.verification_key == patch_for_dcmm_decrypt + + +def test_encrypt_with_keyring_materials_incomplete(): + raw_aes256_keyring = ephemeral_raw_aes_keyring(WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING) + + encrypt_cmm = DefaultCryptoMaterialsManager(keyring=OnlyGenerateKeyring(inner_keyring=raw_aes256_keyring)) + + encryption_materials_request = EncryptionMaterialsRequest(encryption_context={}, frame_length=1024) + + with pytest.raises(InvalidCryptographicMaterialsError) as excinfo: + encrypt_cmm.get_encryption_materials(encryption_materials_request) + + excinfo.match("Encryption materials are incomplete!") + + +def _broken_materials_scenarios(): + yield pytest.param(dict(break_algorithm=True), id="broken algorithm") + yield pytest.param(dict(break_encryption_context=True), id="broken encryption context") + yield pytest.param(dict(break_signing=True), id="broken signing/verification key") + + +@pytest.mark.parametrize("kwargs", _broken_materials_scenarios()) +def test_encrypt_with_keyring_materials_do_not_match_request(kwargs): + raw_aes256_keyring = ephemeral_raw_aes_keyring(WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING) + + encrypt_cmm = DefaultCryptoMaterialsManager(keyring=BrokenKeyring(inner_keyring=raw_aes256_keyring, **kwargs)) + + encryption_materials_request = EncryptionMaterialsRequest(encryption_context={}, frame_length=1024) + + with pytest.raises(InvalidCryptographicMaterialsError) as excinfo: + encrypt_cmm.get_encryption_materials(encryption_materials_request) + + excinfo.match("Encryption materials do not match request!") + + +def test_decrypt_with_keyring_materials_incomplete(): + raw_aes256_keyring = ephemeral_raw_aes_keyring(WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING) + raw_aes128_keyring = ephemeral_raw_aes_keyring(WrappingAlgorithm.AES_128_GCM_IV12_TAG16_NO_PADDING) + + encrypt_cmm = DefaultCryptoMaterialsManager(keyring=raw_aes256_keyring) + decrypt_cmm = DefaultCryptoMaterialsManager(keyring=raw_aes128_keyring) + + encryption_materials_request = EncryptionMaterialsRequest(encryption_context={}, frame_length=1024) + encryption_materials = encrypt_cmm.get_encryption_materials(encryption_materials_request) + + decryption_materials_request = DecryptionMaterialsRequest( + algorithm=encryption_materials.algorithm, + encrypted_data_keys=encryption_materials.encrypted_data_keys, + encryption_context=encryption_materials.encryption_context, + ) + + with pytest.raises(InvalidCryptographicMaterialsError) as excinfo: + decrypt_cmm.decrypt_materials(decryption_materials_request) + + excinfo.match("Decryption materials are incomplete!") + + +@pytest.mark.parametrize("kwargs", _broken_materials_scenarios()) +def test_decrypt_with_keyring_materials_do_not_match_request(kwargs): + raw_aes256_keyring = ephemeral_raw_aes_keyring(WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING) + + encrypt_cmm = DefaultCryptoMaterialsManager(keyring=raw_aes256_keyring) + decrypt_cmm = DefaultCryptoMaterialsManager(keyring=BrokenKeyring(inner_keyring=raw_aes256_keyring, **kwargs)) + + encryption_materials_request = EncryptionMaterialsRequest(encryption_context={}, frame_length=1024) + encryption_materials = encrypt_cmm.get_encryption_materials(encryption_materials_request) + + decryption_materials_request = DecryptionMaterialsRequest( + algorithm=encryption_materials.algorithm, + encrypted_data_keys=encryption_materials.encrypted_data_keys, + encryption_context=encryption_materials.encryption_context, + ) + + with pytest.raises(InvalidCryptographicMaterialsError) as excinfo: + decrypt_cmm.decrypt_materials(decryption_materials_request) + + excinfo.match("Decryption materials do not match request!") diff --git a/test/unit/unit_test_utils.py b/test/unit/unit_test_utils.py index 910755785..0ad61f622 100644 --- a/test/unit/unit_test_utils.py +++ b/test/unit/unit_test_utils.py @@ -11,17 +11,20 @@ # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. """Utility functions to handle common test framework functions.""" +import base64 import copy import io import itertools import os +import attr +from attr.validators import instance_of from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import rsa from aws_encryption_sdk.exceptions import DecryptKeyError -from aws_encryption_sdk.identifiers import Algorithm, EncryptionKeyType, KeyringTraceFlag, WrappingAlgorithm +from aws_encryption_sdk.identifiers import AlgorithmSuite, EncryptionKeyType, KeyringTraceFlag, WrappingAlgorithm from aws_encryption_sdk.internal.crypto.wrapping_keys import WrappingKey from aws_encryption_sdk.internal.utils.streams import InsistentReaderBytesIO from aws_encryption_sdk.key_providers.base import MasterKeyProvider, MasterKeyProviderConfig @@ -33,7 +36,7 @@ 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, Optional # noqa pylint: disable=unused-import + from typing import Dict, Iterable, Optional # noqa pylint: disable=unused-import except ImportError: # pragma: no cover # We only actually need these imports when running the mypy checks pass @@ -120,7 +123,7 @@ def on_decrypt(self, decryption_materials, encrypted_data_keys): def get_encryption_materials_with_data_key(): return EncryptionMaterials( - algorithm=Algorithm.AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384, + algorithm=AlgorithmSuite.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(', @@ -138,7 +141,7 @@ def get_encryption_materials_with_data_key(): def get_encryption_materials_with_data_encryption_key(): return EncryptionMaterials( - algorithm=Algorithm.AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384, + algorithm=AlgorithmSuite.AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384, data_encryption_key=RawDataKey( key_provider=MasterKeyInfo(provider_id=_PROVIDER_ID, key_info=b"5430b043-5843-4629-869c-64794af77ada"), data_key=b'*!\xa1"^-(\xf3\x105\x05i@B\xc2\xa2\xb7\xdd\xd5\xd5\xa9\xddm\xfae\xa8\\$\xf9d\x1e(', @@ -156,7 +159,7 @@ 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, + algorithm=AlgorithmSuite.AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384, encryption_context=_ENCRYPTION_CONTEXT, signing_key=_SIGNING_KEY, ) @@ -164,7 +167,7 @@ def get_encryption_materials_without_data_key(): def get_encryption_materials_with_encrypted_data_key(): return EncryptionMaterials( - algorithm=Algorithm.AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384, + algorithm=AlgorithmSuite.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(', @@ -192,7 +195,7 @@ def get_encryption_materials_with_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, + algorithm=AlgorithmSuite.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(', @@ -214,7 +217,7 @@ def get_encryption_materials_with_encrypted_data_key_aes(): def get_encryption_materials_without_data_encryption_key(): return EncryptionMaterials( - algorithm=Algorithm.AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384, + algorithm=AlgorithmSuite.AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384, encryption_context=_ENCRYPTION_CONTEXT, signing_key=_SIGNING_KEY, ) @@ -222,7 +225,7 @@ def get_encryption_materials_without_data_encryption_key(): def get_decryption_materials_without_data_encryption_key(): return DecryptionMaterials( - algorithm=Algorithm.AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384, + algorithm=AlgorithmSuite.AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384, verification_key=b"ex_verification_key", encryption_context=_ENCRYPTION_CONTEXT, ) @@ -230,7 +233,7 @@ 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, + algorithm=AlgorithmSuite.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(', @@ -248,7 +251,7 @@ def get_decryption_materials_with_data_key(): def get_decryption_materials_with_data_encryption_key(): return DecryptionMaterials( - algorithm=Algorithm.AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384, + algorithm=AlgorithmSuite.AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384, data_encryption_key=RawDataKey( key_provider=MasterKeyInfo(provider_id=_PROVIDER_ID, key_info=b"5430b043-5843-4629-869c-64794af77ada"), data_key=b'*!\xa1"^-(\xf3\x105\x05i@B\xc2\xa2\xb7\xdd\xd5\xd5\xa9\xddm\xfae\xa8\\$\xf9d\x1e(', @@ -509,7 +512,10 @@ class EphemeralRawMasterKeyProvider(RawMasterKeyProvider): provider_id = "fake" def __init__(self): - self.__keys = {b"aes-256": ephemeral_raw_aes_master_key(256), b"rsa-4096": ephemeral_raw_rsa_master_key(4096)} + self.__keys = { + b"aes-256": ephemeral_raw_aes_master_key(WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING), + b"rsa-4096": ephemeral_raw_rsa_master_key(4096), + } def _get_raw_key(self, key_id): return self.__keys[key_id].config.wrapping_key @@ -548,3 +554,110 @@ class FailingDecryptMasterKeyProvider(EphemeralRawMasterKeyProvider): def decrypt_data_key(self, encrypted_data_key, algorithm, encryption_context): raise DecryptKeyError("FailingDecryptMasterKeyProvider cannot decrypt!") + + +@attr.s +class BrokenKeyring(Keyring): + """Keyring that wraps another keyring and selectively breaks the returned values.""" + + _inner_keyring = attr.ib(validator=instance_of(Keyring)) + _break_algorithm = attr.ib(default=False, validator=instance_of(bool)) + _break_encryption_context = attr.ib(default=False, validator=instance_of(bool)) + _break_signing = attr.ib(default=False, validator=instance_of(bool)) + + @staticmethod + def _random_string(bytes_len): + # type: (int) -> str + return base64.b64encode(os.urandom(bytes_len)).decode("utf-8") + + def _broken_algorithm(self, algorithm): + # type: (AlgorithmSuite) -> AlgorithmSuite + if not self._break_algorithm: + return algorithm + + # We want to make sure that we return something different, + # so find this suite in all suites and grab the next one, + # whatever that is. + all_suites = list(AlgorithmSuite) + suite_index = all_suites.index(algorithm) + next_index = (suite_index + 1) % (len(all_suites) - 1) + + return all_suites[next_index] + + def _broken_encryption_context(self, encryption_context): + # type: (Dict[str, str]) -> Dict[str, str] + broken_ec = encryption_context.copy() + + if not self._break_encryption_context: + return broken_ec + + # Remove a random value + try: + broken_ec.popitem() + except KeyError: + pass + + # add a random value + broken_ec[self._random_string(5)] = self._random_string(10) + + return broken_ec + + def _broken_key(self, key): + # type: (bytes) -> bytes + if not self._break_signing: + return key + + return self._random_string(32).encode("utf-8") + + def _break_encryption_materials(self, encryption_materials): + # type: (EncryptionMaterials) -> EncryptionMaterials + return EncryptionMaterials( + algorithm=self._broken_algorithm(encryption_materials.algorithm), + data_encryption_key=encryption_materials.data_encryption_key, + encrypted_data_keys=encryption_materials.encrypted_data_keys, + encryption_context=self._broken_encryption_context(encryption_materials.encryption_context), + signing_key=self._broken_key(encryption_materials.signing_key), + keyring_trace=encryption_materials.keyring_trace, + ) + + def _break_decryption_materials(self, decryption_materials): + # type: (DecryptionMaterials) -> DecryptionMaterials + return DecryptionMaterials( + algorithm=self._broken_algorithm(decryption_materials.algorithm), + data_encryption_key=decryption_materials.data_encryption_key, + encryption_context=self._broken_encryption_context(decryption_materials.encryption_context), + verification_key=self._broken_key(decryption_materials.verification_key), + keyring_trace=decryption_materials.keyring_trace, + ) + + def on_encrypt(self, encryption_materials): + # type: (EncryptionMaterials) -> EncryptionMaterials + return self._break_encryption_materials(self._inner_keyring.on_encrypt(encryption_materials)) + + def on_decrypt(self, decryption_materials, encrypted_data_keys): + # type: (DecryptionMaterials, Iterable[EncryptedDataKey]) -> DecryptionMaterials + return self._break_decryption_materials( + self._inner_keyring.on_decrypt(decryption_materials, encrypted_data_keys) + ) + + +@attr.s +class OnlyGenerateKeyring(Keyring): + """Keyring that wraps another keyring and removes any encrypted data keys.""" + + _inner_keyring = attr.ib(validator=instance_of(Keyring)) + + def on_encrypt(self, encryption_materials): + # type: (EncryptionMaterials) -> EncryptionMaterials + materials = self._inner_keyring.on_encrypt(encryption_materials) + return EncryptionMaterials( + algorithm=materials.algorithm, + data_encryption_key=materials.data_encryption_key, + encryption_context=materials.encryption_context, + signing_key=materials.signing_key, + keyring_trace=materials.keyring_trace, + ) + + def on_decrypt(self, decryption_materials, encrypted_data_keys): + # type: (DecryptionMaterials, Iterable[EncryptedDataKey]) -> DecryptionMaterials + return self._inner_keyring.on_decrypt(decryption_materials, encrypted_data_keys) From bc8bfb298c8a890dde4410f7c7c36964450776f0 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Mon, 24 Feb 2020 21:44:02 -0800 Subject: [PATCH 11/25] feat: enable caching CMM to accept either MKP or keyring --- .../materials_managers/caching.py | 77 ++++++++++--------- .../materials_managers/default.py | 4 +- test/unit/materials_managers/test_caching.py | 36 +++++---- 3 files changed, 66 insertions(+), 51 deletions(-) diff --git a/src/aws_encryption_sdk/materials_managers/caching.py b/src/aws_encryption_sdk/materials_managers/caching.py index 992a39a7a..f13c23c96 100644 --- a/src/aws_encryption_sdk/materials_managers/caching.py +++ b/src/aws_encryption_sdk/materials_managers/caching.py @@ -16,17 +16,20 @@ import attr import six +from attr.validators import instance_of, optional -from ..caches import ( +from aws_encryption_sdk.caches import ( CryptoMaterialsCacheEntryHints, build_decryption_materials_cache_key, build_encryption_materials_cache_key, ) -from ..caches.base import CryptoMaterialsCache -from ..exceptions import CacheKeyError -from ..internal.defaults import MAX_BYTES_PER_KEY, MAX_MESSAGES_PER_KEY -from ..internal.str_ops import to_bytes -from ..key_providers.base import MasterKeyProvider +from aws_encryption_sdk.caches.base import CryptoMaterialsCache +from aws_encryption_sdk.exceptions import CacheKeyError +from aws_encryption_sdk.internal.defaults import MAX_BYTES_PER_KEY, MAX_MESSAGES_PER_KEY +from aws_encryption_sdk.internal.str_ops import to_bytes +from aws_encryption_sdk.key_providers.base import MasterKeyProvider +from aws_encryption_sdk.keyrings.base import Keyring + from . import EncryptionMaterialsRequest from .base import CryptoMaterialsManager from .default import DefaultCryptoMaterialsManager @@ -59,17 +62,17 @@ class CachingCryptoMaterialsManager(CryptoMaterialsManager): value. If no partition name is provided, a random UUID will be used. .. note:: - Either `backing_materials_manager` or `master_key_provider` must be provided. - `backing_materials_manager` will always be used if present. - - :param cache: Crypto cache to use with material manager - :type cache: aws_encryption_sdk.caches.base.CryptoMaterialsCache - :param backing_materials_manager: Crypto material manager to back this caching material manager - (either `backing_materials_manager` or `master_key_provider` required) - :type backing_materials_manager: aws_encryption_sdk.materials_managers.base.CryptoMaterialsManager - :param master_key_provider: Master key provider to use (either `backing_materials_manager` or - `master_key_provider` required) - :type master_key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider + Either ``backing_materials_manager``, ``keyring``, or ``master_key_provider`` must be provided. + ``backing_materials_manager`` will always be used if present. + + :param CryptoMaterialsCache cache: Crypto cache to use with material manager + :param CryptoMaterialsManager backing_materials_manager: + Crypto material manager to back this caching material manager + (either ``backing_materials_manager``, ``keyring``, or ``master_key_provider`` required) + :param MasterKeyProvider master_key_provider: Master key provider to use + (either ``backing_materials_manager``, ``keyring``, or ``master_key_provider`` required) + :param Keyring keyring: Keyring to use + (either ``backing_materials_manager``, ``keyring``, or ``master_key_provider`` required) :param float max_age: Maximum time in seconds that a cache entry may be kept in the cache :param int max_messages_encrypted: Maximum number of messages that may be encrypted under a cache entry (optional) @@ -78,21 +81,14 @@ class CachingCryptoMaterialsManager(CryptoMaterialsManager): :param bytes partition_name: Partition name to use for this instance (optional) """ - cache = attr.ib(validator=attr.validators.instance_of(CryptoMaterialsCache)) - max_age = attr.ib(validator=attr.validators.instance_of(float)) - max_messages_encrypted = attr.ib( - default=MAX_MESSAGES_PER_KEY, validator=attr.validators.instance_of(six.integer_types) - ) - max_bytes_encrypted = attr.ib(default=MAX_BYTES_PER_KEY, validator=attr.validators.instance_of(six.integer_types)) - partition_name = attr.ib( - default=None, converter=to_bytes, validator=attr.validators.optional(attr.validators.instance_of(bytes)) - ) - master_key_provider = attr.ib( - default=None, validator=attr.validators.optional(attr.validators.instance_of(MasterKeyProvider)) - ) - backing_materials_manager = attr.ib( - default=None, validator=attr.validators.optional(attr.validators.instance_of(CryptoMaterialsManager)) - ) + cache = attr.ib(validator=instance_of(CryptoMaterialsCache)) + max_age = attr.ib(validator=instance_of(float)) + max_messages_encrypted = attr.ib(default=MAX_MESSAGES_PER_KEY, validator=instance_of(six.integer_types)) + max_bytes_encrypted = attr.ib(default=MAX_BYTES_PER_KEY, validator=instance_of(six.integer_types)) + partition_name = attr.ib(default=None, converter=to_bytes, validator=optional(instance_of(bytes))) + master_key_provider = attr.ib(default=None, validator=optional(instance_of(MasterKeyProvider))) + backing_materials_manager = attr.ib(default=None, validator=optional(instance_of(CryptoMaterialsManager))) + keyring = attr.ib(default=None, validator=optional(instance_of(Keyring))) def __attrs_post_init__(self): """Applies post-processing which cannot be handled by attrs.""" @@ -111,10 +107,21 @@ def __attrs_post_init__(self): if self.max_age <= 0.0: raise ValueError("max_age cannot be less than or equal to 0") + options_provided = [ + option is not None for option in (self.backing_materials_manager, self.keyring, self.master_key_provider) + ] + provided_count = len([is_set for is_set in options_provided if is_set]) + + if provided_count != 1: + raise TypeError("Exactly one of 'materials_manager', 'keyring', or 'key_provider' must be provided") + if self.backing_materials_manager is None: - if self.master_key_provider is None: - raise TypeError("Either backing_materials_manager or master_key_provider must be defined") - self.backing_materials_manager = DefaultCryptoMaterialsManager(self.master_key_provider) + if self.master_key_provider is not None: + self.backing_materials_manager = DefaultCryptoMaterialsManager( + master_key_provider=self.master_key_provider + ) + else: + self.backing_materials_manager = DefaultCryptoMaterialsManager(keyring=self.keyring) if self.partition_name is None: self.partition_name = to_bytes(str(uuid.uuid4())) diff --git a/src/aws_encryption_sdk/materials_managers/default.py b/src/aws_encryption_sdk/materials_managers/default.py index 66c185495..96ed79c1b 100644 --- a/src/aws_encryption_sdk/materials_managers/default.py +++ b/src/aws_encryption_sdk/materials_managers/default.py @@ -40,9 +40,9 @@ class DefaultCryptoMaterialsManager(CryptoMaterialsManager): The *keyring* parameter. :param MasterKeyProvider master_key_provider: Master key provider to use - (either `keyring` or `master_key_provider` is required) + (either ``keyring`` or ``master_key_provider`` is required) :param Keyring keyring: Keyring to use - (either `keyring` or `master_key_provider` is required) + (either ``keyring`` or ``master_key_provider`` is required) """ algorithm = ALGORITHM diff --git a/test/unit/materials_managers/test_caching.py b/test/unit/materials_managers/test_caching.py index 833d6aa53..ce5e845ad 100644 --- a/test/unit/materials_managers/test_caching.py +++ b/test/unit/materials_managers/test_caching.py @@ -13,16 +13,18 @@ """Unit test suite for CachingCryptoMaterialsManager""" import pytest from mock import MagicMock, sentinel -from pytest_mock import mocker # noqa pylint: disable=unused-import import aws_encryption_sdk.materials_managers.caching from aws_encryption_sdk.caches.base import CryptoMaterialsCache +from aws_encryption_sdk.caches.local import LocalCryptoMaterialsCache from aws_encryption_sdk.exceptions import CacheKeyError from aws_encryption_sdk.internal.defaults import MAX_BYTES_PER_KEY, MAX_MESSAGES_PER_KEY from aws_encryption_sdk.internal.str_ops import to_bytes -from aws_encryption_sdk.key_providers.base import MasterKeyProvider from aws_encryption_sdk.materials_managers.base import CryptoMaterialsManager from aws_encryption_sdk.materials_managers.caching import CachingCryptoMaterialsManager +from aws_encryption_sdk.materials_managers.default import DefaultCryptoMaterialsManager + +from ..unit_test_utils import ephemeral_raw_aes_keyring, ephemeral_raw_aes_master_key pytestmark = [pytest.mark.unit, pytest.mark.local] @@ -54,7 +56,7 @@ def fake_encryption_request(): dict(max_messages_encrypted=None), dict(max_bytes_encrypted=None), dict(partition_name=55), - dict(master_key_provider=None, backing_materials_manager=None), + dict(master_key_provider=None, backing_materials_manager=None, keyring=None), ), ) def test_attrs_fail(invalid_kwargs): @@ -88,20 +90,26 @@ def test_custom_partition_name(patch_uuid4): assert test.partition_name == custom_partition_name -def test_mkp_to_default_cmm(mocker): - mocker.patch.object(aws_encryption_sdk.materials_managers.caching, "DefaultCryptoMaterialsManager") - mock_mkp = MagicMock(__class__=MasterKeyProvider) +def test_mkp_to_default_cmm(): + mkp = ephemeral_raw_aes_master_key() + test = CachingCryptoMaterialsManager( - cache=MagicMock(__class__=CryptoMaterialsCache), max_age=10.0, master_key_provider=mock_mkp + cache=LocalCryptoMaterialsCache(capacity=10), max_age=10.0, master_key_provider=mkp ) - aws_encryption_sdk.materials_managers.caching.DefaultCryptoMaterialsManager.assert_called_once_with( - mock_mkp - ) # noqa pylint: disable=line-too-long - assert ( - test.backing_materials_manager - is aws_encryption_sdk.materials_managers.caching.DefaultCryptoMaterialsManager.return_value - ) # noqa pylint: disable=line-too-long + assert isinstance(test.backing_materials_manager, DefaultCryptoMaterialsManager) + assert test.backing_materials_manager.master_key_provider is mkp + assert test.backing_materials_manager.keyring is None + + +def test_keyring_to_default_cmm(): + keyring = ephemeral_raw_aes_keyring() + + test = CachingCryptoMaterialsManager(cache=LocalCryptoMaterialsCache(capacity=10), max_age=10.0, keyring=keyring) + + assert isinstance(test.backing_materials_manager, DefaultCryptoMaterialsManager) + assert test.backing_materials_manager.keyring is keyring + assert test.backing_materials_manager.master_key_provider is None @pytest.mark.parametrize( From b5da8aa5284b90f8e0a09c7f5213178845b0c733 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Mon, 24 Feb 2020 21:50:15 -0800 Subject: [PATCH 12/25] fix: rename test keyring to avoid name collision --- test/unit/materials_managers/test_default.py | 4 ++-- test/unit/unit_test_utils.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/unit/materials_managers/test_default.py b/test/unit/materials_managers/test_default.py index 5a353bdb9..8615da9e1 100644 --- a/test/unit/materials_managers/test_default.py +++ b/test/unit/materials_managers/test_default.py @@ -29,7 +29,7 @@ from ..unit_test_utils import ( BrokenKeyring, - OnlyGenerateKeyring, + NoEncryptedDataKeysKeyring, ephemeral_raw_aes_keyring, ephemeral_raw_aes_master_key, ) @@ -267,7 +267,7 @@ def test_decrypt_materials(mocker, patch_for_dcmm_decrypt): def test_encrypt_with_keyring_materials_incomplete(): raw_aes256_keyring = ephemeral_raw_aes_keyring(WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING) - encrypt_cmm = DefaultCryptoMaterialsManager(keyring=OnlyGenerateKeyring(inner_keyring=raw_aes256_keyring)) + encrypt_cmm = DefaultCryptoMaterialsManager(keyring=NoEncryptedDataKeysKeyring(inner_keyring=raw_aes256_keyring)) encryption_materials_request = EncryptionMaterialsRequest(encryption_context={}, frame_length=1024) diff --git a/test/unit/unit_test_utils.py b/test/unit/unit_test_utils.py index 0ad61f622..f6005e017 100644 --- a/test/unit/unit_test_utils.py +++ b/test/unit/unit_test_utils.py @@ -642,7 +642,7 @@ def on_decrypt(self, decryption_materials, encrypted_data_keys): @attr.s -class OnlyGenerateKeyring(Keyring): +class NoEncryptedDataKeysKeyring(Keyring): """Keyring that wraps another keyring and removes any encrypted data keys.""" _inner_keyring = attr.ib(validator=instance_of(Keyring)) From cf6bcdf6f972b1cc78de548fef538b65557ee144 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Mon, 24 Feb 2020 21:50:53 -0800 Subject: [PATCH 13/25] chore: caching CMM has too many instance attributes and that's ok --- src/aws_encryption_sdk/materials_managers/caching.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/aws_encryption_sdk/materials_managers/caching.py b/src/aws_encryption_sdk/materials_managers/caching.py index f13c23c96..64304cad6 100644 --- a/src/aws_encryption_sdk/materials_managers/caching.py +++ b/src/aws_encryption_sdk/materials_managers/caching.py @@ -39,6 +39,7 @@ @attr.s(hash=False) class CachingCryptoMaterialsManager(CryptoMaterialsManager): + # pylint: disable=too-many-instance-attributes """Crypto material manager which caches results from an underlying material manager. .. versionadded:: 1.3.0 From d3fcab778944555c76224878d0efb78ecfc37f92 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Mon, 24 Feb 2020 21:56:24 -0800 Subject: [PATCH 14/25] docs: add versionadded flags to docstrings for keyring values --- src/aws_encryption_sdk/__init__.py | 6 ++++++ .../materials_managers/caching.py | 3 +++ src/aws_encryption_sdk/streaming_client.py | 15 +++++++++++++++ 3 files changed, 24 insertions(+) diff --git a/src/aws_encryption_sdk/__init__.py b/src/aws_encryption_sdk/__init__.py index 3f6d86e2e..319767f24 100644 --- a/src/aws_encryption_sdk/__init__.py +++ b/src/aws_encryption_sdk/__init__.py @@ -33,6 +33,9 @@ def encrypt(**kwargs): When using this function, the entire ciphertext message is encrypted into memory before returning any data. If streaming is desired, see :class:`aws_encryption_sdk.stream`. + .. versionadded:: 1.5.0 + The *keyring* parameter. + .. code:: python >>> import aws_encryption_sdk @@ -87,6 +90,9 @@ def decrypt(**kwargs): When using this function, the entire ciphertext message is decrypted into memory before returning any data. If streaming is desired, see :class:`aws_encryption_sdk.stream`. + .. versionadded:: 1.5.0 + The *keyring* parameter. + .. code:: python >>> import aws_encryption_sdk diff --git a/src/aws_encryption_sdk/materials_managers/caching.py b/src/aws_encryption_sdk/materials_managers/caching.py index 64304cad6..d52db190d 100644 --- a/src/aws_encryption_sdk/materials_managers/caching.py +++ b/src/aws_encryption_sdk/materials_managers/caching.py @@ -44,6 +44,9 @@ class CachingCryptoMaterialsManager(CryptoMaterialsManager): .. versionadded:: 1.3.0 + .. versionadded:: 1.5.0 + The *keyring* parameter. + >>> import aws_encryption_sdk >>> kms_key_provider = aws_encryption_sdk.KMSMasterKeyProvider(key_ids=[ ... 'arn:aws:kms:us-east-1:2222222222222:key/22222222-2222-2222-2222-222222222222', diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index f67e9f384..6afd54f0b 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -69,6 +69,9 @@ class _ClientConfig(object): """Parent configuration object for StreamEncryptor and StreamDecryptor objects. + .. versionadded:: 1.5.0 + The *keyring* parameter. + :param source: Source data to encrypt or decrypt :type source: str, bytes, io.IOBase, or file :param CryptoMaterialsManager materials_manager: @@ -314,6 +317,9 @@ def next(self): class EncryptorConfig(_ClientConfig): """Configuration object for StreamEncryptor class. + .. versionadded:: 1.5.0 + The *keyring* parameter. + :param source: Source data to encrypt or decrypt :type source: str, bytes, io.IOBase, or file :param CryptoMaterialsManager materials_manager: @@ -365,6 +371,9 @@ class StreamEncryptor(_EncryptionStream): # pylint: disable=too-many-instance-a .. note:: If config is provided, all other parameters are ignored. + .. versionadded:: 1.5.0 + The *keyring* parameter. + :param config: Client configuration object (config or individual parameters required) :type config: aws_encryption_sdk.streaming_client.EncryptorConfig :param source: Source data to encrypt or decrypt @@ -674,6 +683,9 @@ def close(self): class DecryptorConfig(_ClientConfig): """Configuration object for StreamDecryptor class. + .. versionadded:: 1.5.0 + The *keyring* parameter. + :param source: Source data to encrypt or decrypt :type source: str, bytes, io.IOBase, or file :param CryptoMaterialsManager materials_manager: @@ -710,6 +722,9 @@ class StreamDecryptor(_EncryptionStream): # pylint: disable=too-many-instance-a .. note:: If config is provided, all other parameters are ignored. + .. versionadded:: 1.5.0 + The *keyring* parameter. + :param config: Client configuration object (config or individual parameters required) :type config: aws_encryption_sdk.streaming_client.DecryptorConfig :param source: Source data to encrypt or decrypt From d4bec466fadc2fe55c70757a061b40a08a0c9221 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Mon, 24 Feb 2020 22:01:18 -0800 Subject: [PATCH 15/25] docs: update docs on encrypt/decrypt helper functions to match underlying docs --- src/aws_encryption_sdk/__init__.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/aws_encryption_sdk/__init__.py b/src/aws_encryption_sdk/__init__.py index 319767f24..402aaa5e7 100644 --- a/src/aws_encryption_sdk/__init__.py +++ b/src/aws_encryption_sdk/__init__.py @@ -52,12 +52,14 @@ def encrypt(**kwargs): :type config: aws_encryption_sdk.streaming_client.EncryptorConfig :param source: Source data to encrypt or decrypt :type source: str, bytes, io.IOBase, or file - :param materials_manager: `CryptoMaterialsManager` from which to obtain cryptographic materials - (either `materials_manager` or `key_provider` required) - :type materials_manager: aws_encryption_sdk.materials_managers.base.CryptoMaterialsManager - :param key_provider: `MasterKeyProvider` from which to obtain data keys for encryption - (either `materials_manager` or `key_provider` required) - :type key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider + :param CryptoMaterialsManager materials_manager: + Cryptographic materials manager to use for encryption + (either ``materials_manager``, ``keyring``, ``key_provider`` required) + :param Keyring keyring: Keyring to use for encryption + (either ``materials_manager``, ``keyring``, ``key_provider`` required) + :param MasterKeyProvider key_provider: + Master key provider to use for encryption + (either ``materials_manager``, ``keyring``, ``key_provider`` required) :param int source_length: Length of source data (optional) .. note:: @@ -109,12 +111,14 @@ def decrypt(**kwargs): :type config: aws_encryption_sdk.streaming_client.DecryptorConfig :param source: Source data to encrypt or decrypt :type source: str, bytes, io.IOBase, or file - :param materials_manager: `CryptoMaterialsManager` from which to obtain cryptographic materials - (either `materials_manager` or `key_provider` required) - :type materials_manager: aws_encryption_sdk.materials_managers.base.CryptoMaterialsManager - :param key_provider: `MasterKeyProvider` from which to obtain data keys for decryption - (either `materials_manager` or `key_provider` required) - :type key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider + :param CryptoMaterialsManager materials_manager: + Cryptographic materials manager to use for encryption + (either ``materials_manager``, ``keyring``, ``key_provider`` required) + :param Keyring keyring: Keyring to use for encryption + (either ``materials_manager``, ``keyring``, ``key_provider`` required) + :param MasterKeyProvider key_provider: + Master key provider to use for encryption + (either ``materials_manager``, ``keyring``, ``key_provider`` required) :param int source_length: Length of source data (optional) .. note:: From f9713cc6111647e9e97b9107a2a5408d68efb668 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Mon, 24 Feb 2020 22:25:40 -0800 Subject: [PATCH 16/25] chore: update copyright notices on modified files --- src/aws_encryption_sdk/__init__.py | 14 ++------------ src/aws_encryption_sdk/exceptions.py | 14 ++------------ src/aws_encryption_sdk/keyrings/base.py | 14 ++------------ src/aws_encryption_sdk/keyrings/multi.py | 14 ++------------ src/aws_encryption_sdk/keyrings/raw.py | 14 ++------------ .../materials_managers/caching.py | 14 ++------------ .../materials_managers/default.py | 14 ++------------ src/aws_encryption_sdk/streaming_client.py | 14 ++------------ test/functional/test_client.py | 14 ++------------ test/integration/test_client.py | 14 ++------------ test/unit/materials_managers/test_caching.py | 14 ++------------ test/unit/materials_managers/test_default.py | 14 ++------------ test/unit/unit_test_utils.py | 14 ++------------ 13 files changed, 26 insertions(+), 156 deletions(-) diff --git a/src/aws_encryption_sdk/__init__.py b/src/aws_encryption_sdk/__init__.py index 402aaa5e7..8cadd51b8 100644 --- a/src/aws_encryption_sdk/__init__.py +++ b/src/aws_encryption_sdk/__init__.py @@ -1,15 +1,5 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """High level AWS Encryption SDK client functions.""" # Below are imported for ease of use by implementors from aws_encryption_sdk.caches.local import LocalCryptoMaterialsCache # noqa diff --git a/src/aws_encryption_sdk/exceptions.py b/src/aws_encryption_sdk/exceptions.py index fb699e25b..3c58dcea1 100644 --- a/src/aws_encryption_sdk/exceptions.py +++ b/src/aws_encryption_sdk/exceptions.py @@ -1,15 +1,5 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Contains exception classes for AWS Encryption SDK.""" diff --git a/src/aws_encryption_sdk/keyrings/base.py b/src/aws_encryption_sdk/keyrings/base.py index e5ad9a2ea..51111499e 100644 --- a/src/aws_encryption_sdk/keyrings/base.py +++ b/src/aws_encryption_sdk/keyrings/base.py @@ -1,15 +1,5 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Base class interface for Keyrings.""" from aws_encryption_sdk.materials_managers import ( # only used for mypy; pylint: disable=unused-import DecryptionMaterials, diff --git a/src/aws_encryption_sdk/keyrings/multi.py b/src/aws_encryption_sdk/keyrings/multi.py index 5792732ce..d2ebb5c5a 100644 --- a/src/aws_encryption_sdk/keyrings/multi.py +++ b/src/aws_encryption_sdk/keyrings/multi.py @@ -1,15 +1,5 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Resources required for Multi Keyrings.""" import itertools diff --git a/src/aws_encryption_sdk/keyrings/raw.py b/src/aws_encryption_sdk/keyrings/raw.py index 60e7462ac..1d923b058 100644 --- a/src/aws_encryption_sdk/keyrings/raw.py +++ b/src/aws_encryption_sdk/keyrings/raw.py @@ -1,15 +1,5 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Resources required for Raw Keyrings.""" import logging import os diff --git a/src/aws_encryption_sdk/materials_managers/caching.py b/src/aws_encryption_sdk/materials_managers/caching.py index d52db190d..fe11625d3 100644 --- a/src/aws_encryption_sdk/materials_managers/caching.py +++ b/src/aws_encryption_sdk/materials_managers/caching.py @@ -1,15 +1,5 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Caching crypto material manager.""" import logging import uuid diff --git a/src/aws_encryption_sdk/materials_managers/default.py b/src/aws_encryption_sdk/materials_managers/default.py index 96ed79c1b..d87a38f05 100644 --- a/src/aws_encryption_sdk/materials_managers/default.py +++ b/src/aws_encryption_sdk/materials_managers/default.py @@ -1,15 +1,5 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Default crypto material manager class.""" import logging diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 6afd54f0b..d22fb7c70 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -1,15 +1,5 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """High level AWS Encryption SDK client for streaming objects.""" from __future__ import division diff --git a/test/functional/test_client.py b/test/functional/test_client.py index 80cf8668b..3888479d4 100644 --- a/test/functional/test_client.py +++ b/test/functional/test_client.py @@ -1,15 +1,5 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Functional test suite for aws_encryption_sdk.kms_thick_client""" from __future__ import division diff --git a/test/integration/test_client.py b/test/integration/test_client.py index 183f9d49f..2b66fb408 100644 --- a/test/integration/test_client.py +++ b/test/integration/test_client.py @@ -1,15 +1,5 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Integration test suite for `aws_encryption_sdk`.""" import io import logging diff --git a/test/unit/materials_managers/test_caching.py b/test/unit/materials_managers/test_caching.py index ce5e845ad..cea3e86b6 100644 --- a/test/unit/materials_managers/test_caching.py +++ b/test/unit/materials_managers/test_caching.py @@ -1,15 +1,5 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test suite for CachingCryptoMaterialsManager""" import pytest from mock import MagicMock, sentinel diff --git a/test/unit/materials_managers/test_default.py b/test/unit/materials_managers/test_default.py index 8615da9e1..4642623f3 100644 --- a/test/unit/materials_managers/test_default.py +++ b/test/unit/materials_managers/test_default.py @@ -1,15 +1,5 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Test suite for aws_encryption_sdk.materials_managers.default""" import pytest from mock import MagicMock, sentinel diff --git a/test/unit/unit_test_utils.py b/test/unit/unit_test_utils.py index f6005e017..ff1a1b158 100644 --- a/test/unit/unit_test_utils.py +++ b/test/unit/unit_test_utils.py @@ -1,15 +1,5 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Utility functions to handle common test framework functions.""" import base64 import copy From 1c76c17eee573879cd11ac0f5aa3905794b2a3df Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Tue, 25 Feb 2020 13:11:25 -0800 Subject: [PATCH 17/25] docs: render keyring docs --- doc/index.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/index.rst b/doc/index.rst index 10957074e..b4a0476f9 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -14,6 +14,9 @@ Modules aws_encryption_sdk.caches.base aws_encryption_sdk.caches.local aws_encryption_sdk.caches.null + aws_encryption_sdk.keyrings.base + aws_encryption_sdk.keyrings.multi + aws_encryption_sdk.keyrings.raw aws_encryption_sdk.key_providers.base aws_encryption_sdk.key_providers.kms aws_encryption_sdk.key_providers.raw From 50f529b284e4410097cced8c469892a464d9997e Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Tue, 25 Feb 2020 13:20:45 -0800 Subject: [PATCH 18/25] docs: clean up keyring method docs --- src/aws_encryption_sdk/keyrings/base.py | 13 ++--- src/aws_encryption_sdk/keyrings/multi.py | 20 +++---- src/aws_encryption_sdk/keyrings/raw.py | 68 ++++++++++-------------- 3 files changed, 41 insertions(+), 60 deletions(-) diff --git a/src/aws_encryption_sdk/keyrings/base.py b/src/aws_encryption_sdk/keyrings/base.py index 51111499e..c854faf27 100644 --- a/src/aws_encryption_sdk/keyrings/base.py +++ b/src/aws_encryption_sdk/keyrings/base.py @@ -26,10 +26,9 @@ def on_encrypt(self, encryption_materials): # type: (EncryptionMaterials) -> EncryptionMaterials """Generate a data key if not present and encrypt it using any available wrapping key. - :param encryption_materials: Encryption materials for the keyring to modify. - :type encryption_materials: aws_encryption_sdk.materials_managers.EncryptionMaterials + :param EncryptionMaterials encryption_materials: Encryption materials for keyring to modify. :returns: Optionally modified encryption materials. - :rtype: aws_encryption_sdk.materials_managers.EncryptionMaterials + :rtype: EncryptionMaterials :raises NotImplementedError: if method is not implemented """ raise NotImplementedError("Keyring does not implement on_encrypt function") @@ -38,12 +37,10 @@ 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 the keyring to modify. - :type decryption_materials: aws_encryption_sdk.materials_managers.DecryptionMaterials - :param encrypted_data_keys: List of encrypted data keys. - :type: Iterable of :class:`aws_encryption_sdk.structures.EncryptedDataKey` + :param DecryptionMaterials decryption_materials: Decryption materials for keyring to modify. + :param List[EncryptedDataKey] encrypted_data_keys: List of encrypted data keys. :returns: Optionally modified decryption materials. - :rtype: aws_encryption_sdk.materials_managers.DecryptionMaterials + :rtype: DecryptionMaterials :raises NotImplementedError: if method is not implemented """ raise NotImplementedError("Keyring does not implement on_decrypt function") diff --git a/src/aws_encryption_sdk/keyrings/multi.py b/src/aws_encryption_sdk/keyrings/multi.py index d2ebb5c5a..d42ea365d 100644 --- a/src/aws_encryption_sdk/keyrings/multi.py +++ b/src/aws_encryption_sdk/keyrings/multi.py @@ -30,16 +30,15 @@ class MultiKeyring(Keyring): .. versionadded:: 1.5.0 - :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) + :param Keyring generator: Generator keyring used to generate data encryption key (optional) + :param List[Keyring] children: List of keyrings used to encrypt the data encryption key (optional) :raises EncryptKeyError: if encryption of data key fails for any reason """ + generator = attr.ib(default=None, validator=optional(instance_of(Keyring))) 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 @@ -56,10 +55,9 @@ def on_encrypt(self, encryption_materials): """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 + :param EncryptionMaterials encryption_materials: Encryption materials for keyring to modify. :returns: Optionally modified encryption materials. - :rtype: aws_encryption_sdk.materials_managers.EncryptionMaterials + :rtype: EncryptionMaterials :raises EncryptKeyError: if unable to encrypt data key. """ # Check if generator keyring is not provided and data key is not generated @@ -88,12 +86,10 @@ 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` + :param DecryptionMaterials decryption_materials: Decryption materials for keyring to modify. + :param List[EncryptedDataKey] encrypted_data_keys: List of encrypted data keys. :returns: Optionally modified decryption materials. - :rtype: aws_encryption_sdk.materials_managers.DecryptionMaterials + :rtype: DecryptionMaterials """ # Call on_decrypt on all keyrings till decryption is successful for keyring in self._decryption_keyrings: diff --git a/src/aws_encryption_sdk/keyrings/raw.py b/src/aws_encryption_sdk/keyrings/raw.py index 1d923b058..d97f24cdb 100644 --- a/src/aws_encryption_sdk/keyrings/raw.py +++ b/src/aws_encryption_sdk/keyrings/raw.py @@ -10,6 +10,7 @@ from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import rsa +from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey, RSAPublicKey from aws_encryption_sdk.exceptions import GenerateKeyError from aws_encryption_sdk.identifiers import EncryptionKeyType, KeyringTraceFlag, WrappingAlgorithm @@ -47,10 +48,8 @@ def _generate_data_key( # type: (...) -> bytes """Generates plaintext data key for the keyring. - :param encryption_materials: Encryption materials for the keyring to modify. - :type encryption_materials: aws_encryption_sdk.materials_managers.EncryptionMaterials - :param key_provider: Information about the key in the keyring. - :type key_provider: MasterKeyInfo + :param EncryptionMaterials encryption_materials: Encryption materials for the keyring to modify. + :param MasterKeyInfo key_provider: Information about the key in the keyring. :return bytes: Plaintext data key """ # Check if encryption materials contain data encryption key @@ -87,8 +86,7 @@ class RawAESKeyring(Keyring): :param str key_namespace: String defining the keyring. :param bytes key_name: Key ID :param bytes wrapping_key: Encryption key with which to wrap plaintext data key. - :param wrapping_algorithm: Wrapping Algorithm with which to wrap plaintext data key. - :type wrapping_algorithm: WrappingAlgorithm + :param WrappingAlgorithm wrapping_algorithm: Wrapping Algorithm with which to wrap plaintext data key. .. note:: Only one wrapping key can be specified in a Raw AES Keyring @@ -121,8 +119,7 @@ def _get_key_info_prefix(key_namespace, key_name, wrapping_key): :param str key_namespace: String defining the keyring. :param bytes key_name: Key ID - :param wrapping_key: Encryption key with which to wrap plaintext data key. - :type wrapping_key: WrappingKey + :param WrappingKey wrapping_key: Encryption key with which to wrap plaintext data key. :return: Serialized key_info prefix :rtype: bytes """ @@ -135,10 +132,9 @@ def on_encrypt(self, encryption_materials): # type: (EncryptionMaterials) -> EncryptionMaterials """Generate a data key if not present and encrypt it using any available wrapping key - :param encryption_materials: Encryption materials for the keyring to modify - :type encryption_materials: aws_encryption_sdk.materials_managers.EncryptionMaterials + :param EncryptionMaterials encryption_materials: Encryption materials for the keyring to modify :returns: Optionally modified encryption materials - :rtype: aws_encryption_sdk.materials_managers.EncryptionMaterials + :rtype: EncryptionMaterials """ if encryption_materials.data_encryption_key is None: _generate_data_key(encryption_materials=encryption_materials, key_provider=self._key_provider) @@ -175,12 +171,10 @@ 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 the 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` + :param DecryptionMaterials decryption_materials: Decryption materials for the keyring to modify + :param List[EncryptedDataKey] encrypted_data_keys: List of encrypted data keys :returns: Optionally modified decryption materials - :rtype: aws_encryption_sdk.materials_managers.DecryptionMaterials + :rtype: DecryptionMaterials """ if decryption_materials.data_encryption_key is not None: return decryption_materials @@ -237,17 +231,14 @@ class RawRSAKeyring(Keyring): :param str key_namespace: String defining the keyring ID :param bytes key_name: Key ID - :param private_wrapping_key: Private encryption key with which to wrap plaintext data key (optional) - :type private_wrapping_key: RSAPrivateKey - :param public_wrapping_key: Public encryption key with which to wrap plaintext data key (optional) - :type public_wrapping_key: RSAPublicKey - :param wrapping_algorithm: Wrapping Algorithm with which to wrap plaintext data key - :type wrapping_algorithm: WrappingAlgorithm - :param key_provider: Complete information about the key in the keyring - :type key_provider: MasterKeyInfo + :param RSAPrivateKey private_wrapping_key: Private encryption key with which to wrap plaintext data key (optional) + :param RSAPublicKey public_wrapping_key: Public encryption key with which to wrap plaintext data key (optional) + :param WrappingAlgorithm wrapping_algorithm: Wrapping Algorithm with which to wrap plaintext data key + :param MasterKeyInfo key_provider: Complete information about the key in the keyring .. note:: - At least one of public wrapping key or private wrapping key must be provided. + + At least one of public wrapping key or private wrapping key must be provided. """ key_namespace = attr.ib(validator=instance_of(six.string_types)) @@ -282,12 +273,11 @@ def from_pem_encoding( :param str key_namespace: String defining the keyring ID :param bytes key_name: Key ID - :param wrapping_algorithm: Wrapping Algorithm with which to wrap plaintext data key - :type wrapping_algorithm: WrappingAlgorithm + :param WrappingAlgorithm wrapping_algorithm: Wrapping Algorithm with which to wrap plaintext data key :param bytes public_encoded_key: PEM encoded public key (optional) :param bytes private_encoded_key: PEM encoded private key (optional) :param bytes password: Password to load private key (optional) - :return: Calls RawRSAKeyring class with required parameters + :return: :class:`RawRSAKeyring` constructed using required parameters """ loaded_private_wrapping_key = loaded_public_wrapping_key = None if private_encoded_key is not None: @@ -321,12 +311,11 @@ def from_der_encoding( :param str key_namespace: String defining the keyring ID :param bytes key_name: Key ID - :param wrapping_algorithm: Wrapping Algorithm with which to wrap plaintext data key - :type wrapping_algorithm: WrappingAlgorithm + :param WrappingAlgorithm wrapping_algorithm: Wrapping Algorithm with which to wrap plaintext data key :param bytes public_encoded_key: DER encoded public key (optional) :param bytes private_encoded_key: DER encoded private key (optional) - :param password: Password to load private key (optional) - :return: Calls RawRSAKeyring class with required parameters + :param bytes password: Password to load private key (optional) + :return: :class:`RawRSAKeyring` constructed using required parameters """ loaded_private_wrapping_key = loaded_public_wrapping_key = None if private_encoded_key is not None: @@ -348,12 +337,12 @@ def from_der_encoding( def on_encrypt(self, encryption_materials): # type: (EncryptionMaterials) -> EncryptionMaterials - """Generate a data key if not present and encrypt it using any available wrapping key. + """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 the keyring to modify. - :type encryption_materials: aws_encryption_sdk.materials_managers.EncryptionMaterials + :param EncryptionMaterials encryption_materials: Encryption materials for keyring to modify. :returns: Optionally modified encryption materials. - :rtype: aws_encryption_sdk.materials_managers.EncryptionMaterials + :rtype: EncryptionMaterials """ if encryption_materials.data_encryption_key is None: _generate_data_key(encryption_materials=encryption_materials, key_provider=self._key_provider) @@ -398,12 +387,11 @@ 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 the keyring to modify. - :type decryption_materials: aws_encryption_sdk.materials_managers.DecryptionMaterials + :param DecryptionMaterials decryption_materials: Decryption materials for keyring to modify. :param encrypted_data_keys: List of encrypted data keys. - :type: List of `aws_encryption_sdk.structures.EncryptedDataKey` + :type: List[EncryptedDataKey] :returns: Optionally modified decryption materials. - :rtype: aws_encryption_sdk.materials_managers.DecryptionMaterials + :rtype: DecryptionMaterials """ if self._private_wrapping_key is None: return decryption_materials From 56db42ffd80ecd6bb5de340405b89b2587df06e7 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Tue, 25 Feb 2020 15:32:49 -0800 Subject: [PATCH 19/25] fix: fix linting issues --- src/aws_encryption_sdk/keyrings/raw.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/aws_encryption_sdk/keyrings/raw.py b/src/aws_encryption_sdk/keyrings/raw.py index d97f24cdb..9ac921516 100644 --- a/src/aws_encryption_sdk/keyrings/raw.py +++ b/src/aws_encryption_sdk/keyrings/raw.py @@ -10,7 +10,8 @@ from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import rsa -from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey, RSAPublicKey +from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey # noqa pylint: disable=unused-import +from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey # noqa pylint: disable=unused-import from aws_encryption_sdk.exceptions import GenerateKeyError from aws_encryption_sdk.identifiers import EncryptionKeyType, KeyringTraceFlag, WrappingAlgorithm @@ -19,14 +20,14 @@ from aws_encryption_sdk.internal.formatting.serialize import serialize_raw_master_key_prefix, serialize_wrapped_key from aws_encryption_sdk.key_providers.raw import RawMasterKey from aws_encryption_sdk.keyrings.base import Keyring -from aws_encryption_sdk.structures import ( # pylint: disable=unused-import +from aws_encryption_sdk.structures import ( EncryptedDataKey, KeyringTrace, MasterKeyInfo, RawDataKey, ) -from aws_encryption_sdk.materials_managers import ( # only used for mypy; pylint: disable=unused-import +from aws_encryption_sdk.materials_managers import ( DecryptionMaterials, EncryptionMaterials, ) From 8f83d2bb704dfe19a051c45a8a7474d7ae38594c Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Tue, 25 Feb 2020 16:10:31 -0800 Subject: [PATCH 20/25] fix: autoformat --- src/aws_encryption_sdk/keyrings/raw.py | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/aws_encryption_sdk/keyrings/raw.py b/src/aws_encryption_sdk/keyrings/raw.py index 9ac921516..fa9a30885 100644 --- a/src/aws_encryption_sdk/keyrings/raw.py +++ b/src/aws_encryption_sdk/keyrings/raw.py @@ -20,17 +20,8 @@ from aws_encryption_sdk.internal.formatting.serialize import serialize_raw_master_key_prefix, serialize_wrapped_key from aws_encryption_sdk.key_providers.raw import RawMasterKey from aws_encryption_sdk.keyrings.base import Keyring -from aws_encryption_sdk.structures import ( - EncryptedDataKey, - KeyringTrace, - MasterKeyInfo, - RawDataKey, -) - -from aws_encryption_sdk.materials_managers import ( - DecryptionMaterials, - EncryptionMaterials, -) +from aws_encryption_sdk.materials_managers import DecryptionMaterials, EncryptionMaterials +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 From bbb6056cbe3374321333d8d80452ce85c289c973 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Wed, 26 Feb 2020 16:12:03 -0800 Subject: [PATCH 21/25] docs: fix typo --- src/aws_encryption_sdk/materials_managers/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aws_encryption_sdk/materials_managers/__init__.py b/src/aws_encryption_sdk/materials_managers/__init__.py index 0a6dcd2f0..e6e86c5cd 100644 --- a/src/aws_encryption_sdk/materials_managers/__init__.py +++ b/src/aws_encryption_sdk/materials_managers/__init__.py @@ -248,7 +248,7 @@ def encrypted_data_keys(self): @property def is_complete(self): # type: () -> bool - """Determine whether these materials are sufficiently complete for use as decryption materials. + """Determine whether these materials are sufficiently complete for use as encryption materials. :rtype: bool """ From bcc6781979ff1011bcee4119c3342e553bdc48bc Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Wed, 26 Feb 2020 16:12:38 -0800 Subject: [PATCH 22/25] fix: fix docs and error message inconsistency --- src/aws_encryption_sdk/materials_managers/caching.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/aws_encryption_sdk/materials_managers/caching.py b/src/aws_encryption_sdk/materials_managers/caching.py index fe11625d3..5a8e9e9bd 100644 --- a/src/aws_encryption_sdk/materials_managers/caching.py +++ b/src/aws_encryption_sdk/materials_managers/caching.py @@ -56,8 +56,7 @@ class CachingCryptoMaterialsManager(CryptoMaterialsManager): value. If no partition name is provided, a random UUID will be used. .. note:: - Either ``backing_materials_manager``, ``keyring``, or ``master_key_provider`` must be provided. - ``backing_materials_manager`` will always be used if present. + Exactly one of ``backing_materials_manager``, ``keyring``, or ``master_key_provider`` must be provided. :param CryptoMaterialsCache cache: Crypto cache to use with material manager :param CryptoMaterialsManager backing_materials_manager: @@ -107,7 +106,7 @@ def __attrs_post_init__(self): provided_count = len([is_set for is_set in options_provided if is_set]) if provided_count != 1: - raise TypeError("Exactly one of 'materials_manager', 'keyring', or 'key_provider' must be provided") + raise TypeError("Exactly one of 'backing_materials_manager', 'keyring', or 'key_provider' must be provided") if self.backing_materials_manager is None: if self.master_key_provider is not None: From 4856ab10a89f1b4afeb7e184793f441ce591eb26 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Wed, 26 Feb 2020 16:19:08 -0800 Subject: [PATCH 23/25] fix: re-order checks to avoid misleading error messages if materials are invalid --- src/aws_encryption_sdk/materials_managers/default.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/aws_encryption_sdk/materials_managers/default.py b/src/aws_encryption_sdk/materials_managers/default.py index d87a38f05..336e5243f 100644 --- a/src/aws_encryption_sdk/materials_managers/default.py +++ b/src/aws_encryption_sdk/materials_managers/default.py @@ -131,9 +131,6 @@ def _get_encryption_materials_using_keyring(self, request): final_materials = self.keyring.on_encrypt(encryption_materials=encryption_materials) - if not final_materials.is_complete: - raise InvalidCryptographicMaterialsError("Encryption materials are incomplete!") - materials_are_valid = ( final_materials.algorithm is algorithm, final_materials.encryption_context == expected_encryption_context, @@ -142,6 +139,9 @@ def _get_encryption_materials_using_keyring(self, request): if not all(materials_are_valid): raise InvalidCryptographicMaterialsError("Encryption materials do not match request!") + if not final_materials.is_complete: + raise InvalidCryptographicMaterialsError("Encryption materials are incomplete!") + _LOGGER.debug("Post-encrypt encryption context: %s", encryption_context) return final_materials @@ -233,9 +233,6 @@ def _decrypt_materials_using_keyring(self, request): decryption_materials=decryption_materials, encrypted_data_keys=request.encrypted_data_keys ) - if not final_materials.is_complete: - raise InvalidCryptographicMaterialsError("Decryption materials are incomplete!") - materials_are_valid = ( final_materials.algorithm is request.algorithm, final_materials.encryption_context == request.encryption_context, @@ -244,6 +241,9 @@ def _decrypt_materials_using_keyring(self, request): if not all(materials_are_valid): raise InvalidCryptographicMaterialsError("Decryption materials do not match request!") + if not final_materials.is_complete: + raise InvalidCryptographicMaterialsError("Decryption materials are incomplete!") + return final_materials def decrypt_materials(self, request): From a46b80df7be3ea2441926fb81f35bf9c3a1970f1 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Wed, 26 Feb 2020 16:20:19 -0800 Subject: [PATCH 24/25] chore: test broken paths in default CMM with more algorithm suites --- test/unit/materials_managers/test_default.py | 28 ++++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/test/unit/materials_managers/test_default.py b/test/unit/materials_managers/test_default.py index 4642623f3..9a86e59b8 100644 --- a/test/unit/materials_managers/test_default.py +++ b/test/unit/materials_managers/test_default.py @@ -254,12 +254,15 @@ def test_decrypt_materials(mocker, patch_for_dcmm_decrypt): assert test.verification_key == patch_for_dcmm_decrypt -def test_encrypt_with_keyring_materials_incomplete(): +@pytest.mark.parametrize("algorithm_suite", Algorithm) +def test_encrypt_with_keyring_materials_incomplete(algorithm_suite): raw_aes256_keyring = ephemeral_raw_aes_keyring(WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING) encrypt_cmm = DefaultCryptoMaterialsManager(keyring=NoEncryptedDataKeysKeyring(inner_keyring=raw_aes256_keyring)) - encryption_materials_request = EncryptionMaterialsRequest(encryption_context={}, frame_length=1024) + encryption_materials_request = EncryptionMaterialsRequest( + encryption_context={}, frame_length=1024, algorithm=algorithm_suite + ) with pytest.raises(InvalidCryptographicMaterialsError) as excinfo: encrypt_cmm.get_encryption_materials(encryption_materials_request) @@ -273,13 +276,16 @@ def _broken_materials_scenarios(): yield pytest.param(dict(break_signing=True), id="broken signing/verification key") +@pytest.mark.parametrize("algorithm_suite", Algorithm) @pytest.mark.parametrize("kwargs", _broken_materials_scenarios()) -def test_encrypt_with_keyring_materials_do_not_match_request(kwargs): +def test_encrypt_with_keyring_materials_do_not_match_request(kwargs, algorithm_suite): raw_aes256_keyring = ephemeral_raw_aes_keyring(WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING) encrypt_cmm = DefaultCryptoMaterialsManager(keyring=BrokenKeyring(inner_keyring=raw_aes256_keyring, **kwargs)) - encryption_materials_request = EncryptionMaterialsRequest(encryption_context={}, frame_length=1024) + encryption_materials_request = EncryptionMaterialsRequest( + encryption_context={}, frame_length=1024, algorithm=algorithm_suite + ) with pytest.raises(InvalidCryptographicMaterialsError) as excinfo: encrypt_cmm.get_encryption_materials(encryption_materials_request) @@ -287,14 +293,17 @@ def test_encrypt_with_keyring_materials_do_not_match_request(kwargs): excinfo.match("Encryption materials do not match request!") -def test_decrypt_with_keyring_materials_incomplete(): +@pytest.mark.parametrize("algorithm_suite", Algorithm) +def test_decrypt_with_keyring_materials_incomplete(algorithm_suite): raw_aes256_keyring = ephemeral_raw_aes_keyring(WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING) raw_aes128_keyring = ephemeral_raw_aes_keyring(WrappingAlgorithm.AES_128_GCM_IV12_TAG16_NO_PADDING) encrypt_cmm = DefaultCryptoMaterialsManager(keyring=raw_aes256_keyring) decrypt_cmm = DefaultCryptoMaterialsManager(keyring=raw_aes128_keyring) - encryption_materials_request = EncryptionMaterialsRequest(encryption_context={}, frame_length=1024) + encryption_materials_request = EncryptionMaterialsRequest( + encryption_context={}, frame_length=1024, algorithm=algorithm_suite + ) encryption_materials = encrypt_cmm.get_encryption_materials(encryption_materials_request) decryption_materials_request = DecryptionMaterialsRequest( @@ -309,14 +318,17 @@ def test_decrypt_with_keyring_materials_incomplete(): excinfo.match("Decryption materials are incomplete!") +@pytest.mark.parametrize("algorithm_suite", Algorithm) @pytest.mark.parametrize("kwargs", _broken_materials_scenarios()) -def test_decrypt_with_keyring_materials_do_not_match_request(kwargs): +def test_decrypt_with_keyring_materials_do_not_match_request(kwargs, algorithm_suite): raw_aes256_keyring = ephemeral_raw_aes_keyring(WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING) encrypt_cmm = DefaultCryptoMaterialsManager(keyring=raw_aes256_keyring) decrypt_cmm = DefaultCryptoMaterialsManager(keyring=BrokenKeyring(inner_keyring=raw_aes256_keyring, **kwargs)) - encryption_materials_request = EncryptionMaterialsRequest(encryption_context={}, frame_length=1024) + encryption_materials_request = EncryptionMaterialsRequest( + encryption_context={}, frame_length=1024, algorithm=algorithm_suite + ) encryption_materials = encrypt_cmm.get_encryption_materials(encryption_materials_request) decryption_materials_request = DecryptionMaterialsRequest( From 0438910f794cb5d87505e279f7e4177ff0c7d7b8 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Mon, 2 Mar 2020 20:00:44 -0800 Subject: [PATCH 25/25] docs: make docs correctly link to pyca/cryptography docs --- doc/conf.py | 5 ++++- src/aws_encryption_sdk/keyrings/raw.py | 16 ++++++++-------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/doc/conf.py b/doc/conf.py index 2164a52a6..42a7771b4 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -67,7 +67,10 @@ def get_version(): htmlhelp_basename = "%sdoc" % project # Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {"http://docs.python.org/": None} +intersphinx_mapping = { + "python": ("http://docs.python.org/3/", None), + "cryptography": ("https://cryptography.io/en/latest/", None), +} # autosummary autosummary_generate = True diff --git a/src/aws_encryption_sdk/keyrings/raw.py b/src/aws_encryption_sdk/keyrings/raw.py index fa9a30885..1f4eebe14 100644 --- a/src/aws_encryption_sdk/keyrings/raw.py +++ b/src/aws_encryption_sdk/keyrings/raw.py @@ -9,9 +9,7 @@ from attr.validators import instance_of, optional from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import serialization -from cryptography.hazmat.primitives.asymmetric import rsa -from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey # noqa pylint: disable=unused-import -from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey # noqa pylint: disable=unused-import +from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey, RSAPublicKey from aws_encryption_sdk.exceptions import GenerateKeyError from aws_encryption_sdk.identifiers import EncryptionKeyType, KeyringTraceFlag, WrappingAlgorithm @@ -223,21 +221,23 @@ class RawRSAKeyring(Keyring): :param str key_namespace: String defining the keyring ID :param bytes key_name: Key ID - :param RSAPrivateKey private_wrapping_key: Private encryption key with which to wrap plaintext data key (optional) - :param RSAPublicKey public_wrapping_key: Public encryption key with which to wrap plaintext data key (optional) + :param cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey private_wrapping_key: + Private encryption key with which to wrap plaintext data key (optional) + :param cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey public_wrapping_key: + Public encryption key with which to wrap plaintext data key (optional) :param WrappingAlgorithm wrapping_algorithm: Wrapping Algorithm with which to wrap plaintext data key :param MasterKeyInfo key_provider: Complete information about the key in the keyring .. note:: - At least one of public wrapping key or private wrapping key must be provided. + At least one of public wrapping key or private wrapping key must be provided. """ key_namespace = attr.ib(validator=instance_of(six.string_types)) key_name = attr.ib(validator=instance_of(six.binary_type)) _wrapping_algorithm = attr.ib(repr=False, validator=instance_of(WrappingAlgorithm)) - _private_wrapping_key = attr.ib(default=None, repr=False, validator=optional(instance_of(rsa.RSAPrivateKey))) - _public_wrapping_key = attr.ib(default=None, repr=False, validator=optional(instance_of(rsa.RSAPublicKey))) + _private_wrapping_key = attr.ib(default=None, repr=False, validator=optional(instance_of(RSAPrivateKey))) + _public_wrapping_key = attr.ib(default=None, repr=False, validator=optional(instance_of(RSAPublicKey))) def __attrs_post_init__(self): # type: () -> None