def test_read_bytes_no_read_required(self, mock_read_frame, mock_read_block): ct_stream = io.BytesIO(VALUES["data_128"]) test_decryptor = StreamDecryptor(key_provider=self.mock_key_provider, source=ct_stream) test_decryptor.output_buffer = b"1234567" test_decryptor._read_bytes(5) assert not mock_read_frame.called assert not mock_read_block.called
def test_close_no_footer(self, mock_close): self.mock_header.content_type = ContentType.FRAMED_DATA test_decryptor = StreamDecryptor(key_provider=self.mock_key_provider, source=self.mock_input_stream) with six.assertRaisesRegex(self, SerializationError, 'Footer not read'): test_decryptor.close()
def test_read_bytes_completed(self, mock_read_frame, mock_read_block): ct_stream = io.BytesIO(VALUES["data_128"]) test_decryptor = StreamDecryptor(key_provider=self.mock_key_provider, source=ct_stream) test_decryptor.footer = None test_decryptor._read_bytes(5) assert not mock_read_frame.called assert not mock_read_block.called
def test_close_no_footer(self, mock_close): self.mock_header.content_type = ContentType.FRAMED_DATA test_decryptor = StreamDecryptor(key_provider=self.mock_key_provider, source=self.mock_input_stream) with pytest.raises(SerializationError) as excinfo: test_decryptor.close() excinfo.match("Footer not read")
def test_read_bytes_closed(self, mock_read_frame, mock_read_block): ct_stream = io.BytesIO(VALUES['data_128']) test_decryptor = StreamDecryptor(key_provider=self.mock_key_provider, source=ct_stream) test_decryptor.source_stream.close() test_decryptor._read_bytes(5) assert not mock_read_frame.called assert not mock_read_block.called
def test_read_bytes_non_framed(self, mock_read_frame, mock_read_block): ct_stream = io.BytesIO(VALUES["data_128"]) test_decryptor = StreamDecryptor(key_provider=self.mock_key_provider, source=ct_stream) test_decryptor._header = MagicMock() test_decryptor._header.content_type = ContentType.NO_FRAMING test_decryptor._read_bytes(5) mock_read_block.assert_called_once_with(5) assert not mock_read_frame.called
def test_read_bytes_unknown(self, mock_read_frame, mock_read_block): ct_stream = io.BytesIO(VALUES["data_128"]) test_decryptor = StreamDecryptor(key_provider=self.mock_key_provider, source=ct_stream) test_decryptor._header = MagicMock() test_decryptor._header.content_type = None with pytest.raises(NotSupportedError) as excinfo: test_decryptor._read_bytes(5) excinfo.match("Unsupported content type")
def test_prep_message_non_framed_message(self, mock_read_header, mock_prep_non_framed): self.mock_header.content_type = ContentType.NO_FRAMING mock_read_header.return_value = self.mock_header, sentinel.header_auth test_decryptor = StreamDecryptor(key_provider=self.mock_key_provider, source=self.mock_input_stream) test_decryptor._prep_message() mock_prep_non_framed.assert_called_once_with()
def test_read_bytes_unknown(self, mock_read_frame, mock_read_block): ct_stream = io.BytesIO(VALUES['data_128']) test_decryptor = StreamDecryptor(key_provider=self.mock_key_provider, source=ct_stream) test_decryptor._header = MagicMock() test_decryptor._header.content_type = None with six.assertRaisesRegex(self, NotSupportedError, 'Unsupported content type'): test_decryptor._read_bytes(5)
def test_prep_message_framed_message(self, mock_read_header, mock_prep_non_framed): self.mock_header.content_type = ContentType.FRAMED_DATA mock_read_header.return_value = self.mock_header, sentinel.header_auth test_decryptor = StreamDecryptor(key_provider=self.mock_key_provider, source=self.mock_input_stream) test_decryptor._prep_message() mock_read_header.assert_called_once_with() assert test_decryptor._header is self.mock_header assert test_decryptor.header_auth is sentinel.header_auth assert not mock_prep_non_framed.called assert test_decryptor._message_prepped
def test_close_no_footer(self, mock_close): self.mock_header.content_type = ContentType.FRAMED_DATA test_decryptor = StreamDecryptor( materials_manager=self.mock_materials_manager, source=self.mock_input_stream, commitment_policy=self.mock_commitment_policy, ) with pytest.raises(SerializationError) as excinfo: test_decryptor.close() excinfo.match("Footer not read")
def test_read_bytes_from_non_framed_finalize(self): ct_stream = io.BytesIO(VALUES["data_128"]) test_decryptor = StreamDecryptor( materials_manager=self.mock_materials_manager, source=ct_stream, commitment_policy=self.mock_commitment_policy, ) test_decryptor.body_length = len(VALUES["data_128"]) test_decryptor.decryptor = self.mock_decryptor_instance test_decryptor.verifier = MagicMock() test_decryptor._header = self.mock_header test_decryptor._derived_data_key = sentinel.derived_data_key test_decryptor._unframed_body_iv = sentinel.unframed_body_iv self.mock_decryptor_instance.update.return_value = b"1234" self.mock_decryptor_instance.finalize.return_value = b"5678" test = test_decryptor._read_bytes_from_non_framed_body( len(VALUES["data_128"]) + 1) test_decryptor.verifier.update.assert_called_once_with( VALUES["data_128"]) self.mock_decryptor_instance.update.assert_called_once_with( VALUES["data_128"]) self.mock_deserialize_footer.assert_called_once_with( stream=test_decryptor.source_stream, verifier=test_decryptor.verifier) assert test == b"12345678"
def test_read_bytes_completed(self, mock_read_frame, mock_read_block): ct_stream = io.BytesIO(VALUES["data_128"]) test_decryptor = StreamDecryptor( materials_manager=self.mock_materials_manager, source=ct_stream, commitment_policy=self.mock_commitment_policy, ) test_decryptor.footer = None test_decryptor._read_bytes(5) assert not mock_read_frame.called assert not mock_read_block.called
def test_prep_message_non_framed_message(self, mock_read_header, mock_prep_non_framed): self.mock_header.content_type = ContentType.NO_FRAMING mock_read_header.return_value = self.mock_header, sentinel.header_auth test_decryptor = StreamDecryptor( materials_manager=self.mock_materials_manager, source=self.mock_input_stream, commitment_policy=self.mock_commitment_policy, ) test_decryptor._prep_message() mock_prep_non_framed.assert_called_once_with()
def test_read_bytes_from_non_framed_no_verifier(self): ct_stream = io.BytesIO(VALUES["data_128"]) test_decryptor = StreamDecryptor(key_provider=self.mock_key_provider, source=ct_stream) test_decryptor.body_length = len(VALUES["data_128"]) test_decryptor.decryptor = self.mock_decryptor_instance test_decryptor._header = self.mock_header test_decryptor._derived_data_key = sentinel.derived_data_key test_decryptor._unframed_body_iv = sentinel.unframed_body_iv test_decryptor.verifier = None self.mock_decryptor_instance.update.return_value = b"1234" test_decryptor._read_bytes_from_non_framed_body(5)
def test_read_bytes_no_read_required(self, mock_read_frame, mock_read_block): ct_stream = io.BytesIO(VALUES["data_128"]) test_decryptor = StreamDecryptor( materials_manager=self.mock_materials_manager, source=ct_stream, commitment_policy=self.mock_commitment_policy, ) test_decryptor.output_buffer = b"1234567" test_decryptor._read_bytes(5) assert not mock_read_frame.called assert not mock_read_block.called
def test_read_bytes_non_framed(self, mock_read_frame, mock_read_block): ct_stream = io.BytesIO(VALUES["data_128"]) test_decryptor = StreamDecryptor( materials_manager=self.mock_materials_manager, source=ct_stream, commitment_policy=self.mock_commitment_policy, ) test_decryptor._header = MagicMock() test_decryptor._header.content_type = ContentType.NO_FRAMING test_decryptor._read_bytes(5) mock_read_block.assert_called_once_with(5) assert not mock_read_frame.called
def test_read_header(self, mock_derive_datakey, mock_decrypt_materials_request, mock_verifier): mock_verifier_instance = MagicMock() mock_verifier.from_key_bytes.return_value = mock_verifier_instance ct_stream = io.BytesIO(VALUES["data_128"]) mock_commitment_policy = MagicMock(__class__=CommitmentPolicy) test_decryptor = StreamDecryptor( materials_manager=self.mock_materials_manager, source=ct_stream, commitment_policy=mock_commitment_policy, ) test_decryptor.source_stream = ct_stream test_decryptor._stream_length = len(VALUES["data_128"]) test_header, test_header_auth = test_decryptor._read_header() self.mock_deserialize_header.assert_called_once_with(ct_stream) mock_verifier.from_key_bytes.assert_called_once_with( algorithm=self.mock_header.algorithm, key_bytes=sentinel.verification_key) mock_decrypt_materials_request.assert_called_once_with( encrypted_data_keys=sentinel.encrypted_data_keys, algorithm=self.mock_header.algorithm, encryption_context=sentinel.encryption_context, commitment_policy=mock_commitment_policy, ) self.mock_materials_manager.decrypt_materials.assert_called_once_with( request=mock_decrypt_materials_request.return_value) mock_verifier_instance.update.assert_called_once_with( self.mock_raw_header) self.mock_deserialize_header_auth.assert_called_once_with( version=self.mock_header.version, stream=ct_stream, algorithm=self.mock_header.algorithm, verifier=mock_verifier_instance, ) mock_derive_datakey.assert_called_once_with( source_key=VALUES["data_key_obj"].data_key, algorithm=self.mock_header.algorithm, message_id=self.mock_header.message_id, ) assert test_decryptor._derived_data_key is mock_derive_datakey.return_value self.mock_validate_header.assert_called_once_with( header=self.mock_header, header_auth=sentinel.header_auth, raw_header=self.mock_raw_header, data_key=mock_derive_datakey.return_value, ) assert test_header is self.mock_header assert test_header_auth is sentinel.header_auth
def test_init(self): ct_stream = io.BytesIO(VALUES['data_128']) test_decryptor = StreamDecryptor( key_provider=self.mock_key_provider, source=ct_stream ) assert test_decryptor.last_sequence_number == 0
def decrypt(**kwargs): """Deserializes and decrypts provided ciphertext. .. note:: 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. .. versionadded:: 1.5.0 For backwards compatibility, the new :class:`CryptoResult` return value also unpacks like a 2-member tuple. This allows for backwards compatibility with the previous outputs so this change should not break any existing consumers. .. code:: python >>> import aws_encryption_sdk >>> from aws_encryption_sdk.keyrings.aws_kms import AwsKmsKeyring >>> keyring = AwsKmsKeyring( ... generator_key_id="arn:aws:kms:us-east-1:2222222222222:key/22222222-2222-2222-2222-222222222222", ... key_ids=["arn:aws:kms:us-east-1:3333333333333:key/33333333-3333-3333-3333-333333333333"], ... ) >>> my_ciphertext, decryptor_header = aws_encryption_sdk.decrypt( ... source=my_ciphertext, ... keyring=keyring, ... ) :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 :type source: str, bytes, io.IOBase, or file :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:: If source_length is not provided and read() is called, will attempt to seek() to the end of the stream and tell() to find the length of source data. :param int max_body_length: Maximum frame size (or content length for non-framed messages) in bytes to read from ciphertext message. :returns: Decrypted plaintext, message metadata (header), and keyring trace :rtype: CryptoResult """ with StreamDecryptor(**kwargs) as decryptor: plaintext = decryptor.read() header_copy = copy.deepcopy(decryptor.header) keyring_trace_copy = copy.deepcopy(decryptor.keyring_trace) return CryptoResult(result=plaintext, header=header_copy, keyring_trace=keyring_trace_copy)
def test_init(self): ct_stream = io.BytesIO(VALUES["data_128"]) test_decryptor = StreamDecryptor( materials_manager=self.mock_materials_manager, source=ct_stream, commitment_policy=self.mock_commitment_policy, ) assert test_decryptor.last_sequence_number == 0
def test_prep_non_framed(self): test_decryptor = StreamDecryptor(key_provider=self.mock_key_provider, source=self.mock_input_stream) test_decryptor._header = self.mock_header test_decryptor.verifier = sentinel.verifier test_decryptor._derived_data_key = sentinel.derived_data_key test_decryptor._prep_non_framed() self.mock_deserialize_non_framed_values.assert_called_once_with( stream=self.mock_input_stream, header=self.mock_header, verifier=sentinel.verifier) assert test_decryptor.body_length == len(VALUES['data_128']) self.mock_get_aad_content_string.assert_called_once_with( content_type=self.mock_header.content_type, is_final_frame=True) self.mock_assemble_content_aad.assert_called_once_with( message_id=self.mock_header.message_id, aad_content_string=sentinel.aad_content_string, seq_num=1, length=len(VALUES['data_128'])) self.mock_decryptor.assert_called_once_with( algorithm=self.mock_header.algorithm, key=sentinel.derived_data_key, associated_data=sentinel.associated_data, iv=sentinel.iv, tag=sentinel.tag) assert test_decryptor.decryptor is self.mock_decryptor_instance assert test_decryptor.body_start == 0 assert test_decryptor.body_end == len(VALUES['data_128'])
def test_read_bytes_from_framed_body_single_frame(self): ct_stream = io.BytesIO(VALUES["data_128"]) test_decryptor = StreamDecryptor( materials_manager=self.mock_materials_manager, source=ct_stream, commitment_policy=self.mock_commitment_policy, ) test_decryptor.verifier = MagicMock() test_decryptor.data_key = MagicMock() test_decryptor._header = self.mock_header test_decryptor._derived_data_key = sentinel.derived_data_key frame_data = MagicMock() frame_data.sequence_number = 1 frame_data.final_frame = False frame_data.ciphertext = b"asdfzxcv" self.mock_deserialize_frame.return_value = (frame_data, False) self.mock_get_aad_content_string.return_value = sentinel.aad_content_string self.mock_assemble_content_aad.return_value = sentinel.associated_data self.mock_decrypt.return_value = b"1234" test = test_decryptor._read_bytes_from_framed_body(4) self.mock_deserialize_frame.assert_called_once_with( stream=test_decryptor.source_stream, header=test_decryptor._header, verifier=test_decryptor.verifier) assert not self.mock_deserialize_footer.called assert test == b"1234"
def test_read_bytes_from_non_framed_finalize(self): ct_stream = io.BytesIO(VALUES['data_128']) test_decryptor = StreamDecryptor( key_provider=self.mock_key_provider, source=ct_stream ) test_decryptor.body_start = 0 test_decryptor.body_length = test_decryptor.body_end = len(VALUES['data_128']) test_decryptor.decryptor = self.mock_decryptor_instance test_decryptor.verifier = MagicMock() test_decryptor._header = self.mock_header self.mock_decryptor_instance.update.return_value = b'1234' self.mock_decryptor_instance.finalize.return_value = b'5678' test = test_decryptor._read_bytes_from_non_framed_body(len(VALUES['data_128']) + 1) test_decryptor.verifier.update.assert_called_once_with(VALUES['data_128']) self.mock_decryptor_instance.update.assert_called_once_with(VALUES['data_128']) self.mock_update_verifier_with_tag.assert_called_once_with( stream=test_decryptor.source_stream, header=test_decryptor._header, verifier=test_decryptor.verifier ) self.mock_deserialize_footer.assert_called_once_with( stream=test_decryptor.source_stream, verifier=test_decryptor.verifier ) assert test_decryptor.source_stream.closed assert test == b'12345678'
def test_read_bytes_from_non_framed_no_verifier(self): ct_stream = io.BytesIO(VALUES['data_128']) test_decryptor = StreamDecryptor(key_provider=self.mock_key_provider, source=ct_stream) test_decryptor.body_start = 0 test_decryptor.body_length = test_decryptor.body_end = len( VALUES['data_128']) test_decryptor.decryptor = self.mock_decryptor_instance test_decryptor._header = self.mock_header test_decryptor.verifier = None self.mock_decryptor_instance.update.return_value = b'1234' test_decryptor._read_bytes_from_non_framed_body(5)
def test_read_header(self, mock_init, mock_derive_datakey, mock_decrypt_materials_request, mock_verifier): mock_verifier_instance = MagicMock() mock_verifier.from_key_bytes.return_value = mock_verifier_instance mock_init.return_value = None ct_stream = io.BytesIO(VALUES['data_128']) test_decryptor = StreamDecryptor( materials_manager=self.mock_materials_manager, source=ct_stream) test_decryptor.source_stream = ct_stream test_decryptor._stream_length = len(VALUES['data_128']) test_header, test_header_auth = test_decryptor._read_header() self.mock_deserialize_header.assert_called_once_with(ct_stream) mock_verifier.from_key_bytes.assert_called_once_with( algorithm=self.mock_header.algorithm, key_bytes=sentinel.verification_key) mock_decrypt_materials_request.assert_called_once_with( encrypted_data_keys=sentinel.encrypted_data_keys, algorithm=self.mock_header.algorithm, encryption_context=sentinel.encryption_context) self.mock_materials_manager.decrypt_materials.assert_called_once_with( request=mock_decrypt_materials_request.return_value) mock_verifier_instance.update.assert_called_once_with(b'') self.mock_deserialize_header_auth.assert_called_once_with( stream=ct_stream, algorithm=self.mock_header.algorithm, verifier=mock_verifier_instance) mock_derive_datakey.assert_called_once_with( source_key=VALUES['data_key_obj'].data_key, algorithm=self.mock_header.algorithm, message_id=self.mock_header.message_id) assert test_decryptor._derived_data_key is mock_derive_datakey.return_value self.mock_validate_header.assert_called_once_with( header=self.mock_header, header_auth=sentinel.header_auth, stream=ct_stream, header_start=0, header_end= 0, # Because we mock out deserialize_header, this stays at the start of the stream data_key=mock_derive_datakey.return_value) assert test_header is self.mock_header assert test_header_auth is sentinel.header_auth
def test_commitment_uncommitting_algorithm_policy_allows( self, mock_derive_datakey, mock_decrypt_materials_request, mock_verifier, policy): """Verifies that we can successfully read the header on a message encrypted with an algorithm that does not provide commitment when the policy allows it.""" self.mock_header.algorithm = MagicMock( __class__=Algorithm, iv_len=12, is_committing=MagicMock(return_value=False)) test_decryptor = StreamDecryptor( materials_manager=self.mock_materials_manager, source=self.mock_input_stream, commitment_policy=policy, ) test_decryptor._read_header() self.mock_deserialize_header.assert_called_once_with( self.mock_input_stream) self.mock_compare_digest.assert_not_called()
def test_commitment_uncommitting_algorithm_policy_requires_encrypt( self, mock_derive_datakey, mock_decrypt_materials_request, mock_verifier): """Verifies that we emit the correct exception on a message encrypted with an algorithm that does not provide commitment when the policy requires commitment.""" self.mock_header.algorithm = MagicMock( __class__=Algorithm, iv_len=12, is_committing=MagicMock(return_value=False)) test_decryptor = StreamDecryptor( materials_manager=self.mock_materials_manager, source=self.mock_input_stream, commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT, ) with pytest.raises(ActionNotAllowedError) as excinfo: test_decryptor._read_header() excinfo.match( "Configuration conflict. Cannot decrypt due to .* requiring only committed messages" )
def test_commitment_committing_algorithm_policy_allows_check_fails( self, mock_derive_datakey, mock_decrypt_materials_request, mock_verifier): """Verifies that when the commitment check fails for a committing algorithm on decrypt, we emit the correct exception.""" self.mock_compare_digest.return_value = False self.mock_header.algorithm = MagicMock( __class__=Algorithm, iv_len=12, is_committing=MagicMock(return_value=True)) test_decryptor = StreamDecryptor( materials_manager=self.mock_materials_manager, source=self.mock_input_stream, commitment_policy=CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT, ) with pytest.raises(MasterKeyProviderError) as excinfo: test_decryptor._read_header() excinfo.match("Key commitment validation failed")
def test_read_header_no_verifier(self, mock_derive_datakey, mock_decrypt_materials_request, mock_verifier): self.mock_materials_manager.decrypt_materials.return_value = MagicMock( data_key=VALUES["data_key_obj"], verification_key=None ) test_decryptor = StreamDecryptor(materials_manager=self.mock_materials_manager, source=self.mock_input_stream) test_decryptor.key_provider = self.mock_key_provider test_decryptor.source_stream = self.mock_input_stream test_decryptor._stream_length = len(VALUES["data_128"]) test_decryptor._read_header() assert test_decryptor.verifier is None