def test_wrapping_key_encrypt_symmetric( patch_default_backend, patch_serialization, patch_serialize_encryption_context, patch_derive_data_encryption_key, patch_encrypt, patch_urandom, ): wrapping_algorithm = MagicMock() wrapping_key = MagicMock() test_wrapping_key = WrappingKey( wrapping_algorithm=wrapping_algorithm, wrapping_key=wrapping_key, wrapping_key_type=EncryptionKeyType.SYMMETRIC) test = test_wrapping_key.encrypt( plaintext_data_key=sentinel.plaintext_data_key, encryption_context=sentinel.encryption_context) assert not patch_serialization.load_pem_private_key.called assert not patch_serialization.load_pem_public_key.called patch_serialize_encryption_context.assert_called_once_with( encryption_context=sentinel.encryption_context) patch_urandom.assert_called_once_with(wrapping_algorithm.algorithm.iv_len) patch_encrypt.assert_called_once_with( algorithm=wrapping_algorithm.algorithm, key=patch_derive_data_encryption_key.return_value, plaintext=sentinel.plaintext_data_key, associated_data=patch_serialize_encryption_context.return_value, iv=patch_urandom.return_value, ) assert test is patch_encrypt.return_value
def __attrs_post_init__(self): # type: () -> None """Prepares initial values not handled by attrs.""" key_size_to_wrapping_algorithm = { wrapper.algorithm.kdf_input_len: wrapper for wrapper 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, ) } try: self._wrapping_algorithm = key_size_to_wrapping_algorithm[len( self._wrapping_key)] except KeyError: raise ValueError( "Invalid wrapping key length. Must be one of {} bytes.".format( sorted(key_size_to_wrapping_algorithm.keys()))) self._key_provider = MasterKeyInfo(provider_id=self.key_namespace, key_info=self.key_name) self._wrapping_key_structure = WrappingKey( wrapping_algorithm=self._wrapping_algorithm, wrapping_key=self._wrapping_key, wrapping_key_type=EncryptionKeyType.SYMMETRIC, ) self._key_info_prefix = self._get_key_info_prefix( key_namespace=self.key_namespace, key_name=self.key_name, wrapping_key=self._wrapping_key_structure)
def test_wrapping_key_encrypt_public(patch_default_backend, patch_serialization, patch_serialize_encryption_context, patch_encrypt): _, public_key = mock_wrapping_rsa_keys() patch_serialization.load_pem_public_key.return_value = public_key public_key.encrypt.return_value = VALUES["ciphertext"] mock_wrapping_algorithm = MagicMock( encryption_type=EncryptionType.ASYMMETRIC) test_wrapping_key = WrappingKey( wrapping_algorithm=mock_wrapping_algorithm, wrapping_key=sentinel.wrapping_key, wrapping_key_type=EncryptionKeyType.PUBLIC, ) test = test_wrapping_key.encrypt( plaintext_data_key=sentinel.plaintext_data_key, encryption_context=sentinel.encryption_context) public_key.encrypt.assert_called_once_with( plaintext=sentinel.plaintext_data_key, padding=mock_wrapping_algorithm.padding) assert not patch_serialize_encryption_context.called assert not patch_encrypt.called assert test == EncryptedData(iv=None, ciphertext=VALUES["ciphertext"], tag=None)
def test_wrapping_key_decrypt_symmetric( patch_default_backend, patch_serialization, patch_serialize_encryption_context, patch_derive_data_encryption_key, patch_decrypt, ): mock_wrapping_algorithm = MagicMock() test_wrapping_key = WrappingKey( wrapping_algorithm=mock_wrapping_algorithm, wrapping_key=sentinel.wrapping_key, wrapping_key_type=EncryptionKeyType.SYMMETRIC, ) test = test_wrapping_key.decrypt( encrypted_wrapped_data_key=VALUES["ciphertext"], encryption_context=sentinel.encryption_context) patch_serialize_encryption_context.assert_called_once_with( encryption_context=sentinel.encryption_context) patch_decrypt.assert_called_once_with( algorithm=mock_wrapping_algorithm.algorithm, key=patch_derive_data_encryption_key.return_value, encrypted_data=VALUES["ciphertext"], associated_data=patch_serialize_encryption_context.return_value, ) assert test is patch_decrypt.return_value
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 test_wrapping_key_decrypt_private( patch_default_backend, patch_serialization, patch_serialize_encryption_context, patch_decrypt ): private_key, _ = mock_wrapping_rsa_keys() patch_serialization.load_pem_private_key.return_value = private_key private_key.decrypt.return_value = sentinel.plaintext_data mock_wrapping_algorithm = MagicMock(encryption_type=EncryptionType.ASYMMETRIC) test_wrapping_key = WrappingKey( wrapping_algorithm=mock_wrapping_algorithm, wrapping_key=sentinel.wrapping_key, wrapping_key_type=EncryptionKeyType.PRIVATE ) test = test_wrapping_key.decrypt( encrypted_wrapped_data_key=mock_encrypted_data(), encryption_context=sentinel.encryption_context ) private_key.decrypt.assert_called_once_with( ciphertext=VALUES['ciphertext'], padding=mock_wrapping_algorithm.padding ) assert not patch_serialize_encryption_context.called assert not patch_decrypt.called assert test is sentinel.plaintext_data
def _get_raw_key(self, key_id) -> WrappingKey: return WrappingKey( wrapping_algorithm=WrappingAlgorithm. AES_256_GCM_IV12_TAG16_NO_PADDING, wrapping_key=urandom(32), wrapping_key_type=EncryptionKeyType.SYMMETRIC, )
def test_wrapping_key_init_invalid_key_type(): with pytest.raises(InvalidDataKeyError) as excinfo: WrappingKey(wrapping_algorithm=MagicMock(), wrapping_key=MagicMock(), wrapping_key_type=sentinel.key_type) excinfo.match(r"Invalid wrapping_key_type: *")
def _get_raw_key(self, key_id): wrapping_key = VALUES['raw'][key_id][self.config.encryption_key_type] if key_id == b'sym1': wrapping_key = wrapping_key[:self.config.wrapping_algorithm. algorithm.data_key_len] return WrappingKey(wrapping_algorithm=self.config.wrapping_algorithm, wrapping_key=wrapping_key, wrapping_key_type=self.config.encryption_key_type)
def test_wrapping_key_decrypt_public(patch_default_backend, patch_serialization, patch_serialize_encryption_context, patch_decrypt): private_key, _ = mock_wrapping_rsa_keys() patch_serialization.load_pem_private_key.return_value = private_key private_key.decrypt.return_value = sentinel.plaintext_data mock_wrapping_algorithm = MagicMock( encryption_type=EncryptionType.ASYMMETRIC) test_wrapping_key = WrappingKey(wrapping_algorithm=mock_wrapping_algorithm, wrapping_key=sentinel.wrapping_key, wrapping_key_type=EncryptionKeyType.PUBLIC) with pytest.raises(IncorrectMasterKeyError) as excinfo: test_wrapping_key.decrypt( encrypted_wrapped_data_key=mock_encrypted_data(), encryption_context=sentinel.encryption_context) excinfo.match(r'Public key cannot decrypt')
def test_wrapping_key_init_public(patch_default_backend, patch_serialization): wrapping_algorithm = MagicMock() wrapping_key = MagicMock() test_wrapping_key = WrappingKey(wrapping_algorithm=wrapping_algorithm, wrapping_key=wrapping_key, wrapping_key_type=EncryptionKeyType.PUBLIC) patch_serialization.load_pem_public_key.assert_called_once_with( data=wrapping_key, backend=patch_default_backend.return_value) assert not patch_serialization.load_pem_private_key.called assert test_wrapping_key._wrapping_key is patch_serialization.load_pem_public_key.return_value
def test_wrapping_key_init_private_with_password(patch_default_backend, patch_serialization): wrapping_algorithm = MagicMock() wrapping_key = MagicMock() WrappingKey(wrapping_algorithm=wrapping_algorithm, wrapping_key=wrapping_key, wrapping_key_type=EncryptionKeyType.PRIVATE, password=sentinel.password) patch_serialization.load_pem_private_key.assert_called_once_with( data=wrapping_key, password=sentinel.password, backend=patch_default_backend.return_value)
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 _wrapping_key(self, key_spec): # type: (KeySpec) -> WrappingKey """Build the correct wrapping key if this is a raw master key. :param KeySpec key_spec: Key specification to use with this master key :return: Wrapping key to use :rtype: WrappingKey :raises TypeError: if this is not a raw master key specification """ if not self.type_name == "raw": raise TypeError("This is not a raw master key") algorithm = self._wrapping_algorithm(key_spec.bits) material = key_spec.raw_material key_type = _RAW_ENCRYPTION_KEY_TYPE[key_spec.type_name] return WrappingKey(wrapping_algorithm=algorithm, wrapping_key=material, wrapping_key_type=key_type)
def _get_raw_key(self, key_id): """Returns a static, randomly-generated symmetric key for the specified key ID. :param str key_id: Key ID :returns: Wrapping key that contains the specified static key :rtype: :class:`aws_encryption_sdk.internal.crypto.WrappingKey` """ try: static_key = self._static_keys[key_id] except KeyError: static_key = os.urandom(32) self._static_keys[key_id] = static_key return WrappingKey(wrapping_algorithm=WrappingAlgorithm. AES_256_GCM_IV12_TAG16_NO_PADDING, wrapping_key=static_key, wrapping_key_type=EncryptionKeyType.SYMMETRIC)
def _get_raw_key(self, key_id): """Finds a loaded raw key.""" try: algorithm, key_bits, padding_algorithm, padding_hash = key_id.upper( ).split(b".", 3) key_bits = int(key_bits) key_type = _KEY_TYPES_MAP[algorithm] wrapping_algorithm = _WRAPPING_ALGORITHM_MAP[algorithm][key_bits][ padding_algorithm][padding_hash] static_key = _STATIC_KEYS[algorithm][key_bits] return WrappingKey(wrapping_algorithm=wrapping_algorithm, wrapping_key=static_key, wrapping_key_type=key_type) except KeyError: _LOGGER.exception("Unknown Key ID: %s", key_id) raise InvalidKeyIdError("Unknown Key ID: {}".format(key_id))
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 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, ), )
def test_wrapping_key_init_symmetric(patch_default_backend, patch_serialization, patch_derive_data_encryption_key): wrapping_algorithm = MagicMock() wrapping_key = MagicMock() test_wrapping_key = WrappingKey( wrapping_algorithm=wrapping_algorithm, wrapping_key=wrapping_key, wrapping_key_type=EncryptionKeyType.SYMMETRIC) assert not patch_serialization.load_pem_private_key.called assert not patch_serialization.load_pem_public_key.called assert test_wrapping_key._wrapping_key is wrapping_key patch_derive_data_encryption_key.assert_called_once_with( source_key=wrapping_key, algorithm=wrapping_algorithm.algorithm, message_id=None) assert test_wrapping_key._derived_wrapping_key is patch_derive_data_encryption_key.return_value
def _get_raw_key(self, key_id): """Retrieves a static, randomly generated, RSA key for the specified key id. :param str key_id: User-defined ID for the static key :returns: Wrapping key that contains the specified static key :rtype: :class:`aws_encryption_sdk.internal.crypto.WrappingKey` """ try: static_key = self._static_keys[key_id] except KeyError: private_key = rsa.generate_private_key(public_exponent=65537, key_size=4096, backend=default_backend()) static_key = private_key.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.PKCS8, encryption_algorithm=serialization.NoEncryption(), ) self._static_keys[key_id] = static_key return WrappingKey( wrapping_algorithm=WrappingAlgorithm.RSA_OAEP_SHA1_MGF1, wrapping_key=static_key, wrapping_key_type=EncryptionKeyType.PRIVATE, )
# ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. """Unit test suite to validate aws_encryption_sdk.key_providers.raw.RawMasterKeyConfig""" import pytest import six from aws_encryption_sdk.identifiers import EncryptionKeyType, WrappingAlgorithm from aws_encryption_sdk.internal.crypto.wrapping_keys import WrappingKey from aws_encryption_sdk.key_providers.base import MasterKeyConfig from aws_encryption_sdk.key_providers.raw import RawMasterKeyConfig from .unit_test_utils import all_invalid_kwargs, all_valid_kwargs pytestmark = [pytest.mark.unit, pytest.mark.local] STATIC_WRAPPING_KEY = WrappingKey( wrapping_algorithm=WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING, wrapping_key=b'_________a symmetric key________', wrapping_key_type=EncryptionKeyType.SYMMETRIC) VALID_KWARGS = { RawMasterKeyConfig: [ dict(key_id=b'a raw key', provider_id='a provider', wrapping_key=STATIC_WRAPPING_KEY), dict(key_id=b'a raw key', provider_id=b'a provider', wrapping_key=STATIC_WRAPPING_KEY) ] } @pytest.mark.parametrize('cls, kwargs', all_valid_kwargs(VALID_KWARGS)) def test_attributes_valid_kwargs(cls, kwargs):
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. .. note:: Only one wrapping key can be specified in a Raw AES Keyring """ key_namespace = attr.ib(validator=instance_of(six.string_types)) key_name = attr.ib(validator=instance_of(six.binary_type)) _wrapping_key = attr.ib(repr=False, validator=instance_of(six.binary_type)) def __attrs_post_init__(self): # type: () -> None """Prepares initial values not handled by attrs.""" key_size_to_wrapping_algorithm = { wrapper.algorithm.kdf_input_len: wrapper for wrapper 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, ) } try: self._wrapping_algorithm = key_size_to_wrapping_algorithm[len( self._wrapping_key)] except KeyError: raise ValueError( "Invalid wrapping key length. Must be one of {} bytes.".format( sorted(key_size_to_wrapping_algorithm.keys()))) self._key_provider = MasterKeyInfo(provider_id=self.key_namespace, key_info=self.key_name) self._wrapping_key_structure = WrappingKey( wrapping_algorithm=self._wrapping_algorithm, wrapping_key=self._wrapping_key, wrapping_key_type=EncryptionKeyType.SYMMETRIC, ) self._key_info_prefix = self._get_key_info_prefix( key_namespace=self.key_namespace, key_name=self.key_name, wrapping_key=self._wrapping_key_structure) @staticmethod def _get_key_info_prefix(key_namespace, key_name, wrapping_key): # type: (str, bytes, WrappingKey) -> six.binary_type """Helper function to get key info prefix :param str key_namespace: String defining the keyring. :param bytes key_name: Key ID :param WrappingKey wrapping_key: Encryption key with which to wrap plaintext data key. :return: Serialized key_info prefix :rtype: bytes """ key_info_prefix = serialize_raw_master_key_prefix( RawMasterKey(provider_id=key_namespace, key_id=key_name, wrapping_key=wrapping_key)) return key_info_prefix 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 EncryptionMaterials encryption_materials: Encryption materials for the keyring to modify :returns: Encryption materials containing data key and encrypted data key :rtype: EncryptionMaterials """ new_materials = encryption_materials if new_materials.data_encryption_key is None: # Get encryption materials with a new data key. new_materials = _generate_data_key( encryption_materials=new_materials, key_provider=self._key_provider) try: # Encrypt data key encrypted_wrapped_key = self._wrapping_key_structure.encrypt( plaintext_data_key=new_materials.data_encryption_key.data_key, encryption_context=new_materials.encryption_context, ) # EncryptedData to EncryptedDataKey encrypted_data_key = serialize_wrapped_key( key_provider=self._key_provider, wrapping_algorithm=self._wrapping_algorithm, wrapping_key_id=self.key_name, encrypted_wrapped_key=encrypted_wrapped_key, ) except Exception: # pylint: disable=broad-except error_message = "Raw AES keyring unable to encrypt data key" _LOGGER.exception(error_message) raise EncryptKeyError(error_message) # Update Keyring Trace keyring_trace = KeyringTrace( wrapping_key=self._key_provider, flags={ KeyringTraceFlag.ENCRYPTED_DATA_KEY, KeyringTraceFlag.SIGNED_ENCRYPTION_CONTEXT }, ) return new_materials.with_encrypted_data_key( encrypted_data_key=encrypted_data_key, keyring_trace=keyring_trace) def on_decrypt(self, decryption_materials, encrypted_data_keys): # type: (DecryptionMaterials, Iterable[EncryptedDataKey]) -> DecryptionMaterials """Attempt to decrypt the encrypted data keys. :param DecryptionMaterials decryption_materials: Decryption materials for the keyring to modify :param List[EncryptedDataKey] encrypted_data_keys: List of encrypted data keys :returns: Decryption materials that MAY include a plaintext data key :rtype: DecryptionMaterials """ new_materials = decryption_materials if new_materials.data_encryption_key is not None: return new_materials # Decrypt data key expected_key_info_len = len( self._key_info_prefix) + self._wrapping_algorithm.algorithm.iv_len for key in encrypted_data_keys: if (key.key_provider.provider_id != self._key_provider.provider_id or len(key.key_provider.key_info) != expected_key_info_len or not key.key_provider.key_info.startswith( self._key_info_prefix)): continue # Wrapped EncryptedDataKey to deserialized EncryptedData encrypted_wrapped_key = deserialize_wrapped_key( wrapping_algorithm=self._wrapping_algorithm, wrapping_key_id=self.key_name, wrapped_encrypted_key=key) # EncryptedData to raw key string try: plaintext_data_key = self._wrapping_key_structure.decrypt( encrypted_wrapped_data_key=encrypted_wrapped_key, encryption_context=new_materials.encryption_context, ) except Exception: # pylint: disable=broad-except # We intentionally WANT to catch all exceptions here error_message = "Raw AES Keyring unable to decrypt data key" _LOGGER.exception(error_message) # The Raw AES keyring MUST evaluate every encrypted data key # until it either succeeds or runs out of encrypted data keys. continue # Create a keyring trace keyring_trace = KeyringTrace( wrapping_key=self._key_provider, flags={ KeyringTraceFlag.DECRYPTED_DATA_KEY, KeyringTraceFlag.VERIFIED_ENCRYPTION_CONTEXT }, ) # Update decryption materials data_encryption_key = RawDataKey(key_provider=self._key_provider, data_key=plaintext_data_key) return new_materials.with_data_encryption_key( data_encryption_key=data_encryption_key, keyring_trace=keyring_trace) return new_materials