def test_prep_message_framed_message( self, mock_write_header, mock_prep_non_framed, mock_rostream, mock_derive_datakey, mock_encryption_materials_request ): mock_rostream.return_value = sentinel.plaintext_rostream test_encryptor = StreamEncryptor( source=self.mock_input_stream, materials_manager=self.mock_materials_manager, frame_length=self.mock_frame_length, source_length=5, encryption_context=VALUES['encryption_context'] ) test_encryptor.content_type = ContentType.FRAMED_DATA test_encryption_context = {aws_encryption_sdk.internal.defaults.ENCODED_SIGNER_KEY: sentinel.decoded_bytes} self.mock_encryption_materials.encryption_context = test_encryption_context self.mock_encryption_materials.encrypted_data_keys = self.mock_encrypted_data_keys test_encryptor._prep_message() mock_encryption_materials_request.assert_called_once_with( algorithm=test_encryptor.config.algorithm, encryption_context=VALUES['encryption_context'], plaintext_rostream=sentinel.plaintext_rostream, frame_length=test_encryptor.config.frame_length, plaintext_length=5 ) self.mock_materials_manager.get_encryption_materials.assert_called_once_with( request=mock_encryption_materials_request.return_value ) self.mock_validate_frame_length.assert_called_once_with( frame_length=self.mock_frame_length, algorithm=self.mock_encryption_materials.algorithm ) mock_derive_datakey.assert_called_once_with( source_key=self.mock_encryption_materials.data_encryption_key.data_key, algorithm=self.mock_encryption_materials.algorithm, message_id=VALUES['message_id'] ) assert test_encryptor._derived_data_key is mock_derive_datakey.return_value assert test_encryptor._header == MessageHeader( version=aws_encryption_sdk.internal.defaults.VERSION, type=aws_encryption_sdk.internal.defaults.TYPE, algorithm=self.mock_encryption_materials.algorithm, message_id=VALUES['message_id'], encryption_context=test_encryption_context, encrypted_data_keys=self.mock_encrypted_data_keys, content_type=test_encryptor.content_type, content_aad_length=0, header_iv_length=self.mock_encryption_materials.algorithm.iv_len, frame_length=self.mock_frame_length ) mock_write_header.assert_called_once_with() assert not mock_prep_non_framed.called assert test_encryptor._message_prepped
def test_message_header_attributes_succeeds(): MessageHeader(version=MagicMock(__class__=SerializationVersion), type=MagicMock(__class__=ObjectType), algorithm=MagicMock(__class__=Algorithm), message_id=b'', encryption_context={}, encrypted_data_keys=set([]), content_type=MagicMock(__class__=ContentType), content_aad_length=5, header_iv_length=5, frame_length=5)
def _deserialize_header_v1(header, tee_stream, max_encrypted_data_keys): # type: (IO, Union[int, None]) -> MessageHeader """Deserializes the header from a source stream in SerializationVersion.V1. :param header: A dictionary in which to store deserialized values :type header: dict :param tee_stream: The stream from which to read bytes :type tee_stream: aws_encryption_sdk.internal.utils.streams.TeeStream :param max_encrypted_data_keys: Maximum number of encrypted keys to deserialize :type max_encrypted_data_keys: None or positive int :returns: Deserialized MessageHeader object :rtype: :class:`aws_encryption_sdk.structures.MessageHeader` :raises NotSupportedError: if unsupported data types are found :raises UnknownIdentityError: if unknown data types are found :raises SerializationError: if IV length does not match algorithm """ _LOGGER.debug("Deserializing header in version V1") (message_type_id, ) = unpack_values(">B", tee_stream) header["type"] = _verified_message_type_from_id(message_type_id) algorithm_id, message_id, ser_encryption_context_length = unpack_values( ">H16sH", tee_stream) header["algorithm"] = _verified_algorithm_from_id(algorithm_id) header["message_id"] = message_id header["encryption_context"] = deserialize_encryption_context( tee_stream.read(ser_encryption_context_length)) header["encrypted_data_keys"] = deserialize_encrypted_data_keys( tee_stream, max_encrypted_data_keys) (content_type_id, ) = unpack_values(">B", tee_stream) header["content_type"] = _verified_content_type_from_id(content_type_id) (content_aad_length, ) = unpack_values(">I", tee_stream) header["content_aad_length"] = _verified_content_aad_length( content_aad_length) (iv_length, ) = unpack_values(">B", tee_stream) header["header_iv_length"] = _verified_iv_length(iv_length, header["algorithm"]) (frame_length, ) = unpack_values(">I", tee_stream) header["frame_length"] = _verified_frame_length(frame_length, header["content_type"]) return MessageHeader(**header)
def deserialize_header(stream): # type: (IO) -> MessageHeader """Deserializes the header from a source stream :param stream: Source data stream :type stream: io.BytesIO :returns: Deserialized MessageHeader object :rtype: :class:`aws_encryption_sdk.structures.MessageHeader` and bytes :raises NotSupportedError: if unsupported data types are found :raises UnknownIdentityError: if unknown data types are found :raises SerializationError: if IV length does not match algorithm """ _LOGGER.debug("Starting header deserialization") tee = io.BytesIO() tee_stream = TeeStream(stream, tee) version_id, message_type_id = unpack_values(">BB", tee_stream) header = dict() header["version"] = _verified_version_from_id(version_id) header["type"] = _verified_message_type_from_id(message_type_id) algorithm_id, message_id, ser_encryption_context_length = unpack_values( ">H16sH", tee_stream) header["algorithm"] = _verified_algorithm_from_id(algorithm_id) header["message_id"] = message_id header["encryption_context"] = deserialize_encryption_context( tee_stream.read(ser_encryption_context_length)) header["encrypted_data_keys"] = _deserialize_encrypted_data_keys( tee_stream) (content_type_id, ) = unpack_values(">B", tee_stream) header["content_type"] = _verified_content_type_from_id(content_type_id) (content_aad_length, ) = unpack_values(">I", tee_stream) header["content_aad_length"] = _verified_content_aad_length( content_aad_length) (iv_length, ) = unpack_values(">B", tee_stream) header["header_iv_length"] = _verified_iv_length(iv_length, header["algorithm"]) (frame_length, ) = unpack_values(">I", tee_stream) header["frame_length"] = _verified_frame_length(frame_length, header["content_type"]) return MessageHeader(**header), tee.getvalue()
def test_message_header_attributes_fails(version, message_type, algorithm, message_id, encryption_context, encrypted_data_keys, content_type, content_aad_length, header_iv_length, frame_length): with pytest.raises(TypeError): MessageHeader(version=version, type=message_type, algorithm=algorithm, message_id=message_id, encryption_context=encryption_context, encrypted_data_keys=encrypted_data_keys, content_type=content_type, content_aad_length=content_aad_length, header_iv_length=header_iv_length, frame_length=frame_length)
def _deserialize_header_v2(header, tee_stream): # type: (IO) -> MessageHeader """Deserializes the header from a source stream in SerializationVersion.V2. :param header: A dictionary in which to store deserialized values :type header: dict :param tee_stream: The stream from which to read bytes :type tee_stream: aws_encryption_sdk.internal.utils.streams.TeeStream :returns: Deserialized MessageHeader object :rtype: :class:`aws_encryption_sdk.structures.MessageHeader` :raises NotSupportedError: if unsupported data types are found :raises UnknownIdentityError: if unknown data types are found :raises SerializationError: if IV length does not match algorithm """ _LOGGER.debug("Deserializing header in version V2") algorithm_id, message_id, ser_encryption_context_length = unpack_values( ">H32sH", tee_stream) header["algorithm"] = _verified_algorithm_from_id(algorithm_id) header["message_id"] = message_id header["encryption_context"] = deserialize_encryption_context( tee_stream.read(ser_encryption_context_length)) header["encrypted_data_keys"] = _deserialize_encrypted_data_keys( tee_stream) (content_type_id, ) = unpack_values(">B", tee_stream) header["content_type"] = _verified_content_type_from_id(content_type_id) (frame_length, ) = unpack_values(">I", tee_stream) header["frame_length"] = _verified_frame_length(frame_length, header["content_type"]) algorithm_suite_data_length = header[ "algorithm"].algorithm_suite_data_length() (algorithm_suite_data, ) = unpack_values( ">{}s".format(algorithm_suite_data_length), tee_stream) header["commitment_key"] = algorithm_suite_data return MessageHeader(**header)
def _prep_message(self): """Performs initial message setup.""" encryption_context = self.config.encryption_context.copy() message_id = aws_encryption_sdk.internal.utils.message_id() if self.config.algorithm.signing_algorithm_info is None: self.signer = None else: self.signer = aws_encryption_sdk.internal.crypto.Signer( self.config.algorithm) encryption_context[aws_encryption_sdk.internal.defaults. ENCODED_SIGNER_KEY] = codecs.decode( self.signer.encoded_public_key()) self.encryption_data_key, encrypted_data_keys = aws_encryption_sdk.internal.utils.prepare_data_keys( key_provider=self.config.key_provider, algorithm=self.config.algorithm, encryption_context=encryption_context, plaintext_rostream=aws_encryption_sdk.internal.utils.ROStream( self.source_stream), plaintext_length=self.config.source_length, data_key=self.config.data_key) self._header = MessageHeader( version=aws_encryption_sdk.internal.defaults.VERSION, type=aws_encryption_sdk.internal.defaults.TYPE, algorithm=self.config.algorithm, message_id=message_id, encryption_context=encryption_context, encrypted_data_keys=encrypted_data_keys, content_type=self.content_type, content_aad_length=0, header_iv_length=self.config.algorithm.iv_len, frame_length=self.config.frame_length) self._write_header() if self.content_type == ContentType.NO_FRAMING: self._prep_non_framed() self._message_prepped = True
def test_prep_message_framed_message(self, mock_write_header, mock_prep_non_framed, mock_rostream): mock_rostream.return_value = sentinel.plaintext_rostream test_encryptor = StreamEncryptor( source=self.mock_input_stream, key_provider=self.mock_key_provider, frame_length=self.mock_frame_length, source_length=5 ) test_encryptor.content_type = ContentType.FRAMED_DATA test_encryptor._prep_message() self.mock_signer.assert_called_once_with(test_encryptor.config.algorithm) self.mock_signer_instance.encoded_public_key.assert_called_once_with() self.mock_codecs.decode.assert_called_once_with(sentinel.encoded_public_key) test_encryption_context = {aws_encryption_sdk.internal.defaults.ENCODED_SIGNER_KEY: sentinel.decoded_bytes} mock_rostream.assert_called_once_with(self.mock_input_stream) self.mock_prepare_data_keys.assert_called_once_with( key_provider=self.mock_key_provider, algorithm=test_encryptor.config.algorithm, encryption_context=test_encryption_context, plaintext_rostream=sentinel.plaintext_rostream, plaintext_length=5, data_key=test_encryptor.config.data_key ) assert test_encryptor._header == MessageHeader( version=aws_encryption_sdk.internal.defaults.VERSION, type=aws_encryption_sdk.internal.defaults.TYPE, algorithm=test_encryptor.config.algorithm, message_id=VALUES['message_id'], encryption_context=test_encryption_context, encrypted_data_keys=self.mock_encrypted_data_keys, content_type=test_encryptor.content_type, content_aad_length=0, header_iv_length=test_encryptor.config.algorithm.iv_len, frame_length=self.mock_frame_length ) mock_write_header.assert_called_once_with() assert not mock_prep_non_framed.called assert test_encryptor._message_prepped
def generate_header(self, message_id): """Generates the header object. :param message_id: The randomly generated id for the message :type message_id: bytes """ version = VERSION if self._encryption_materials.algorithm.message_format_version == 0x02: version = SerializationVersion.V2 kwargs = dict( version=version, algorithm=self._encryption_materials.algorithm, message_id=message_id, encryption_context=self._encryption_materials.encryption_context, encrypted_data_keys=self._encryption_materials.encrypted_data_keys, content_type=self.content_type, frame_length=self.config.frame_length, ) if self._encryption_materials.algorithm.is_committing(): commitment_key = calculate_commitment_key( source_key=self._encryption_materials.data_encryption_key. data_key, algorithm=self._encryption_materials.algorithm, message_id=message_id, ) kwargs["commitment_key"] = commitment_key if version == SerializationVersion.V1: kwargs["type"] = TYPE kwargs["content_aad_length"] = 0 kwargs[ "header_iv_length"] = self._encryption_materials.algorithm.iv_len return MessageHeader(**kwargs)
VALUES["body_final_frame"], VALUES["footer"], ]) VALUES["message_truncated_frames"] = b"".join([ VALUES["header"], VALUES["header_auth"], VALUES["body_frame"], VALUES["body_frame"], VALUES["footer"] ]) VALUES["deserialized_header_block_no_signature"] = MessageHeader( version=SerializationVersion.V1, type=ObjectType.CUSTOMER_AE_DATA, algorithm=Algorithm.AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384, message_id=VALUES["message_id"], encryption_context=VALUES["encryption_context"], encrypted_data_keys=set([ EncryptedDataKey( key_provider=VALUES["data_keys"][0].key_provider, encrypted_data_key=VALUES["data_keys"][0].encrypted_data_key, ) ]), content_type=ContentType.NO_FRAMING, content_aad_length=0, header_iv_length=Algorithm.AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384. iv_len, frame_length=0, ) VALUES["deserialized_header_block"] = MessageHeader( version=SerializationVersion.V1, type=ObjectType.CUSTOMER_AE_DATA, algorithm=Algorithm.AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384, message_id=VALUES["message_id"], encryption_context=VALUES["updated_encryption_context"], encrypted_data_keys=set([
def _prep_message(self): """Performs initial message setup. :raises MasterKeyProviderError: if primary master key is not a member of supplied MasterKeyProvider :raises MasterKeyProviderError: if no Master Keys are returned from key_provider """ message_id = aws_encryption_sdk.internal.utils.message_id() try: plaintext_length = self.stream_length except NotSupportedError: plaintext_length = None encryption_materials_request = EncryptionMaterialsRequest( algorithm=self.config.algorithm, encryption_context=self.config.encryption_context.copy(), frame_length=self.config.frame_length, plaintext_rostream=aws_encryption_sdk.internal.utils.ROStream( self.source_stream), plaintext_length=plaintext_length) self._encryption_materials = self.config.materials_manager.get_encryption_materials( request=encryption_materials_request) if self.config.algorithm is not None and self._encryption_materials.algorithm != self.config.algorithm: raise ActionNotAllowedError( ('Cryptographic materials manager provided algorithm suite' ' differs from algorithm suite in request.\n' 'Required: {requested}\n' 'Provided: {provided}').format( requested=self.config.algorithm, provided=self._encryption_materials.algorithm)) if self._encryption_materials.signing_key is None: self.signer = None else: self.signer = Signer.from_key_bytes( algorithm=self._encryption_materials.algorithm, key_bytes=self._encryption_materials.signing_key) aws_encryption_sdk.internal.utils.validate_frame_length( frame_length=self.config.frame_length, algorithm=self._encryption_materials.algorithm) self._derived_data_key = derive_data_encryption_key( source_key=self._encryption_materials.data_encryption_key.data_key, algorithm=self._encryption_materials.algorithm, message_id=message_id) self._header = MessageHeader( version=VERSION, type=TYPE, algorithm=self._encryption_materials.algorithm, message_id=message_id, encryption_context=self._encryption_materials.encryption_context, encrypted_data_keys=self._encryption_materials.encrypted_data_keys, content_type=self.content_type, content_aad_length=0, header_iv_length=self._encryption_materials.algorithm.iv_len, frame_length=self.config.frame_length) self._write_header() if self.content_type == ContentType.NO_FRAMING: self._prep_non_framed() self._message_prepped = True
def deserialize_header(stream): """Deserializes the header from a source stream :param stream: Source data stream :type stream: io.BytesIO :returns: Deserialized MessageHeader object :rtype: :class:`aws_encryption_sdk.structures.MessageHeader` and bytes :raises NotSupportedError: if unsupported data types are found :raises UnknownIdentityError: if unknown data types are found :raises SerializationError: if IV length does not match algorithm """ _LOGGER.debug('Starting header deserialization') tee = io.BytesIO() tee_stream = TeeStream(stream, tee) version_id, message_type_id = unpack_values('>BB', tee_stream) try: message_type = ObjectType(message_type_id) except ValueError as error: raise NotSupportedError( 'Unsupported type {} discovered in data stream'.format( message_type_id), error) try: version = SerializationVersion(version_id) except ValueError as error: raise NotSupportedError('Unsupported version {}'.format(version_id), error) header = {'version': version, 'type': message_type} algorithm_id, message_id, ser_encryption_context_length = unpack_values( '>H16sH', tee_stream) try: alg = Algorithm.get_by_id(algorithm_id) except KeyError as error: raise UnknownIdentityError('Unknown algorithm {}'.format(algorithm_id), error) if not alg.allowed: raise NotSupportedError('Unsupported algorithm: {}'.format(alg)) header['algorithm'] = alg header['message_id'] = message_id header['encryption_context'] = deserialize_encryption_context( tee_stream.read(ser_encryption_context_length)) (encrypted_data_key_count, ) = unpack_values('>H', tee_stream) encrypted_data_keys = set([]) for _ in range(encrypted_data_key_count): (key_provider_length, ) = unpack_values('>H', tee_stream) (key_provider_identifier, ) = unpack_values( '>{}s'.format(key_provider_length), tee_stream) (key_provider_information_length, ) = unpack_values('>H', tee_stream) (key_provider_information, ) = unpack_values( '>{}s'.format(key_provider_information_length), tee_stream) (encrypted_data_key_length, ) = unpack_values('>H', tee_stream) encrypted_data_key = tee_stream.read(encrypted_data_key_length) encrypted_data_keys.add( EncryptedDataKey(key_provider=MasterKeyInfo( provider_id=to_str(key_provider_identifier), key_info=key_provider_information), encrypted_data_key=encrypted_data_key)) header['encrypted_data_keys'] = encrypted_data_keys (content_type_id, ) = unpack_values('>B', tee_stream) try: content_type = ContentType(content_type_id) except ValueError as error: raise UnknownIdentityError( 'Unknown content type {}'.format(content_type_id), error) header['content_type'] = content_type (content_aad_length, ) = unpack_values('>I', tee_stream) if content_aad_length != 0: raise SerializationError( 'Content AAD length field is currently unused, its value must be always 0' ) header['content_aad_length'] = 0 (iv_length, ) = unpack_values('>B', tee_stream) if iv_length != alg.iv_len: raise SerializationError( 'Specified IV length ({length}) does not match algorithm IV length ({alg})' .format(length=iv_length, alg=alg)) header['header_iv_length'] = iv_length (frame_length, ) = unpack_values('>I', tee_stream) if content_type == ContentType.FRAMED_DATA and frame_length > MAX_FRAME_SIZE: raise SerializationError( 'Specified frame length larger than allowed maximum: {found} > {max}' .format(found=frame_length, max=MAX_FRAME_SIZE)) elif content_type == ContentType.NO_FRAMING and frame_length != 0: raise SerializationError( 'Non-zero frame length found for non-framed message') header['frame_length'] = frame_length return MessageHeader(**header), tee.getvalue()
]) VALUES['message_truncated_frames'] = b''.join([ VALUES['header'], VALUES['header_auth'], VALUES['body_frame'], VALUES['body_frame'], VALUES['footer'] ]) VALUES['deserialized_header_block_no_signature'] = MessageHeader( version=SerializationVersion.V1, type=ObjectType.CUSTOMER_AE_DATA, algorithm=Algorithm.AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384, message_id=VALUES['message_id'], encryption_context=VALUES['encryption_context'], encrypted_data_keys=set([EncryptedDataKey( key_provider=VALUES['data_keys'][0].key_provider, encrypted_data_key=VALUES['data_keys'][0].encrypted_data_key )]), content_type=ContentType.NO_FRAMING, content_aad_length=0, header_iv_length=Algorithm.AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384.iv_len, frame_length=0 ) VALUES['deserialized_header_block'] = MessageHeader( version=SerializationVersion.V1, type=ObjectType.CUSTOMER_AE_DATA, algorithm=Algorithm.AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384, message_id=VALUES['message_id'], encryption_context=VALUES['updated_encryption_context'], encrypted_data_keys=set([EncryptedDataKey( key_provider=VALUES['data_keys'][0].key_provider,
def test_json_ready_message_header(): # pylint: disable=too-many-locals message_id = b"a message ID" encryption_context = {"a": "b", "c": "d"} content_aad_length = 8 iv_length = 17 frame_length = 99 master_key_provider_id_1 = b"provider 1" master_key_provider_info_1 = b"master key 1" encrypted_data_key_1 = b"an encrypted data key1" master_key_provider_id_2 = b"provider 1" master_key_provider_info_2 = b"master key 2" encrypted_data_key_2 = b"an encrypted data key2" master_key_provider_id_3 = b"another provider" master_key_provider_info_3 = b"master key 3" encrypted_data_key_3 = b"an encrypted data key3" raw_header = MessageHeader( version=SerializationVersion.V1, type=ObjectType.CUSTOMER_AE_DATA, algorithm=Algorithm.AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384, message_id=message_id, encryption_context=encryption_context, encrypted_data_keys=set([ EncryptedDataKey( key_provider=MasterKeyInfo( provider_id=master_key_provider_id_1, key_info=master_key_provider_info_1), encrypted_data_key=encrypted_data_key_1, ), EncryptedDataKey( key_provider=MasterKeyInfo( provider_id=master_key_provider_id_2, key_info=master_key_provider_info_2), encrypted_data_key=encrypted_data_key_2, ), EncryptedDataKey( key_provider=MasterKeyInfo( provider_id=master_key_provider_id_3, key_info=master_key_provider_info_3), encrypted_data_key=encrypted_data_key_3, ), ]), content_type=ContentType.FRAMED_DATA, content_aad_length=content_aad_length, header_iv_length=iv_length, frame_length=frame_length, ) expected_header_dict = { "version": "1.0", "type": ObjectType.CUSTOMER_AE_DATA.value, "commitment_key": None, "algorithm": Algorithm.AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384.name, "message_id": metadata.unicode_b64_encode(message_id), "encryption_context": encryption_context, "encrypted_data_keys": [ { "key_provider": { "provider_id": metadata.unicode_b64_encode(master_key_provider_id_3), "key_info": metadata.unicode_b64_encode(master_key_provider_info_3), }, "encrypted_data_key": metadata.unicode_b64_encode(encrypted_data_key_3), }, { "key_provider": { "provider_id": metadata.unicode_b64_encode(master_key_provider_id_1), "key_info": metadata.unicode_b64_encode(master_key_provider_info_1), }, "encrypted_data_key": metadata.unicode_b64_encode(encrypted_data_key_1), }, { "key_provider": { "provider_id": metadata.unicode_b64_encode(master_key_provider_id_2), "key_info": metadata.unicode_b64_encode(master_key_provider_info_2), }, "encrypted_data_key": metadata.unicode_b64_encode(encrypted_data_key_2), }, ], "content_type": ContentType.FRAMED_DATA.value, "header_iv_length": iv_length, "frame_length": frame_length, } test = metadata.json_ready_header(raw_header) assert test == expected_header_dict # verify that the dict is actually JSON-encodable json.dumps(test)