# Copyright 2018 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.
"""Cryptographic materials provider to use ephemeral content encryption keys wrapped by delegated keys."""
import attr
import six
from dynamodb_encryption_sdk.delegated_keys import DelegatedKey
from dynamodb_encryption_sdk.exceptions import UnwrappingError, WrappingError
from dynamodb_encryption_sdk.internal.validators import dictionary_validator
from dynamodb_encryption_sdk.materials.wrapped import WrappedCryptographicMaterials
from dynamodb_encryption_sdk.structures import EncryptionContext # noqa pylint: disable=unused-import
from . import CryptographicMaterialsProvider
try: # Python 3.5.0 and 3.5.1 have incompatible typing modules
from typing import Dict, Optional, Text # noqa pylint: disable=unused-import
except ImportError: # pragma: no cover
# We only actually need these imports when running the mypy checks
pass
try: # Python 3.5.0 and 3.5.1 have incompatible typing modules
from typing import Optional # noqa pylint: disable=unused-import
except ImportError: # pragma: no cover
# We only actually need these imports when running the mypy checks
pass
__all__ = ("WrappedCryptographicMaterialsProvider",)
[docs]@attr.s(init=False)
class WrappedCryptographicMaterialsProvider(CryptographicMaterialsProvider):
"""Cryptographic materials provider to use ephemeral content encryption keys wrapped by delegated keys.
:param DelegatedKey signing_key: Delegated key used as signing and verification key
:param DelegatedKey wrapping_key: Delegated key used to wrap content key
.. note::
``wrapping_key`` must be provided if providing encryption materials
:param DelegatedKey unwrapping_key: Delegated key used to unwrap content key
.. note::
``unwrapping_key`` must be provided if providing decryption materials or loading
materials from material description
"""
_signing_key = attr.ib(validator=attr.validators.instance_of(DelegatedKey))
_wrapping_key = attr.ib(validator=attr.validators.optional(attr.validators.instance_of(DelegatedKey)), default=None)
_unwrapping_key = attr.ib(
validator=attr.validators.optional(attr.validators.instance_of(DelegatedKey)), default=None
)
_material_description = attr.ib(
validator=attr.validators.optional(dictionary_validator(six.string_types, six.string_types)),
default=attr.Factory(dict),
)
def __init__(
self,
signing_key, # type: DelegatedKey
wrapping_key=None, # type: Optional[DelegatedKey]
unwrapping_key=None, # type: Optional[DelegatedKey]
material_description=None, # type: Optional[Dict[Text, Text]]
): # noqa=D107
# type: (...) -> None
# Workaround pending resolution of attrs/mypy interaction.
# https://github.com/python/mypy/issues/2088
# https://github.com/python-attrs/attrs/issues/215
if material_description is None:
material_description = {}
self._signing_key = signing_key
self._wrapping_key = wrapping_key
self._unwrapping_key = unwrapping_key
self._material_description = material_description
attr.validate(self)
def _build_materials(self, encryption_context):
# type: (EncryptionContext) -> WrappedCryptographicMaterials
"""Construct
:param EncryptionContext encryption_context: Encryption context for request
:returns: Wrapped cryptographic materials
:rtype: WrappedCryptographicMaterials
"""
material_description = self._material_description.copy()
material_description.update(encryption_context.material_description)
return WrappedCryptographicMaterials(
wrapping_key=self._wrapping_key,
unwrapping_key=self._unwrapping_key,
signing_key=self._signing_key,
material_description=material_description,
)
[docs] def encryption_materials(self, encryption_context):
# type: (EncryptionContext) -> WrappedCryptographicMaterials
"""Provide encryption materials.
:param EncryptionContext encryption_context: Encryption context for request
:returns: Encryption materials
:rtype: WrappedCryptographicMaterials
:raises WrappingError: if no wrapping key is available
"""
if self._wrapping_key is None:
raise WrappingError("Encryption materials cannot be provided: no wrapping key")
return self._build_materials(encryption_context)
[docs] def decryption_materials(self, encryption_context):
# type: (EncryptionContext) -> WrappedCryptographicMaterials
"""Provide decryption materials.
:param EncryptionContext encryption_context: Encryption context for request
:returns: Decryption materials
:rtype: WrappedCryptographicMaterials
:raises UnwrappingError: if no unwrapping key is available
"""
if self._unwrapping_key is None:
raise UnwrappingError("Decryption materials cannot be provided: no unwrapping key")
return self._build_materials(encryption_context)