def test_encryption_cycle_default_algorithm_framed_stream_many_lines_iterator( self): """Test that the streaming enrypt/decrypt cycle completes successfully for a framed message with multiple frames using the default algorithm. """ ciphertext = b'' encryptor = aws_encryption_sdk.stream( source=io.BytesIO(VALUES['plaintext_128'] * 100), key_provider=self.mock_kms_key_provider, mode='e', encryption_context=VALUES['encryption_context'], frame_length=128) for chunk in encryptor: ciphertext += chunk encryptor.close() header_1 = encryptor.header plaintext = b'' decryptor = aws_encryption_sdk.stream( source=io.BytesIO(ciphertext), key_provider=self.mock_kms_key_provider, mode='d') for chunk in decryptor: plaintext += chunk decryptor.close() header_2 = decryptor.header assert plaintext == VALUES['plaintext_128'] * 100 assert header_1.encryption_context == header_2.encryption_context
def test_encryption_cycle_default_algorithm_framed_stream_many_lines(self): """Test that the enrypt/decrypt cycle completes successfully for a framed message with many frames using the default algorithm. """ ciphertext = b"" with aws_encryption_sdk.stream( source=io.BytesIO(VALUES["plaintext_128"] * 10), key_provider=self.kms_master_key_provider, mode="e", encryption_context=VALUES["encryption_context"], frame_length=128, ) as encryptor: for chunk in encryptor: ciphertext += chunk header_1 = encryptor.header plaintext = b"" with aws_encryption_sdk.stream( source=io.BytesIO(ciphertext), key_provider=self.kms_master_key_provider, mode="d") as decryptor: for chunk in decryptor: plaintext += chunk header_2 = decryptor.header assert plaintext == VALUES["plaintext_128"] * 10 assert header_1.encryption_context == header_2.encryption_context
def test_stream_unknown(self): with pytest.raises(ValueError) as excinfo: aws_encryption_sdk.stream(mode="ERROR", a=sentinel.a, b=sentinel.b, c=sentinel.b) excinfo.match("Unsupported mode: *")
def test_encryption_cycle_stream_kms(frame_length, algorithm, encryption_context): key_provider = fake_kms_key_provider(algorithm.kdf_input_len) ciphertext = bytearray() with aws_encryption_sdk.stream( mode="e", source=VALUES["plaintext_128"] * 10, key_provider=key_provider, frame_length=frame_length, algorithm=algorithm, encryption_context=encryption_context, ) as encryptor: for chunk in encryptor: ciphertext.extend(chunk) ciphertext = bytes(ciphertext) plaintext = bytearray() with aws_encryption_sdk.stream(mode="d", source=io.BytesIO(ciphertext), key_provider=key_provider) as decryptor: for chunk in decryptor: plaintext.extend(chunk) plaintext = bytes(plaintext) assert ciphertext != plaintext assert plaintext == VALUES["plaintext_128"] * 10 assert encryptor.header.encryption_context == decryptor.header.encryption_context
def test_stream_unknown(self): with six.assertRaisesRegex(self, ValueError, 'Unsupported mode: *'): aws_encryption_sdk.stream( mode='ERROR', a=sentinel.a, b=sentinel.b, c=sentinel.b )
def encrypt_decrypt_stream(key_arn, source_plaintext_filename, botocore_session=None): """Encrypts and then decrypts streaming data under one KMS customer master key (CMK). :param str key_arn: Amazon Resource Name (ARN) of the KMS CMK :param str source_plaintext_filename: Filename of file to encrypt :param botocore_session: existing botocore session instance :type botocore_session: botocore.session.Session """ kwargs = dict() kwargs["key_ids"] = [key_arn] if botocore_session is not None: kwargs["botocore_session"] = botocore_session # Create master key provider using the ARN of the key and the session (botocore_session) kms_key_provider = aws_encryption_sdk.KMSMasterKeyProvider(**kwargs) ciphertext_filename = source_plaintext_filename + ".encrypted" decrypted_text_filename = source_plaintext_filename + ".decrypted" # Encrypt the plaintext using the AWS Encryption SDK. with open(source_plaintext_filename, "rb") as plaintext, open(ciphertext_filename, "wb") as ciphertext: with aws_encryption_sdk.stream( source=plaintext, mode="e", key_provider=kms_key_provider) as encryptor: for chunk in encryptor: ciphertext.write(chunk) # Decrypt the encrypted message using the AWS Encryption SDK. with open(ciphertext_filename, "rb") as ciphertext, open(decrypted_text_filename, "wb") as plaintext: with aws_encryption_sdk.stream( source=ciphertext, mode="d", key_provider=kms_key_provider) as decryptor: for chunk in decryptor: plaintext.write(chunk) # Check if the original message and the decrypted message are the same assert filecmp.cmp(source_plaintext_filename, decrypted_text_filename) # Check if the headers of the encrypted message and decrypted message match assert all(pair in encryptor.header.encryption_context.items() for pair in decryptor.header.encryption_context.items()) return ciphertext_filename, decrypted_text_filename
def encrypt_file(source_plaintext_filename, path_to_pem_file): """Encrypts a file..... :param str xxxxxxx: xxxxxxx :param str xxxxx: yyyyyyyy """ print("Plaintext file in: %s [%s bytes]" % (source_plaintext_filename, len(source_plaintext_filename))) print("Public key file in: %s" % path_to_pem_file) ciphertext_filename = source_plaintext_filename + '.encrypted' # Load the key local_key_id = os.urandom(8) local_master_key_provider = LocalRSAMasterKeyProvider() local_master_key_provider.set_public_key_path(path_to_pem_file) local_master_key_provider.add_master_key(local_key_id) # Encrypt plaintext with local master keys with open(source_plaintext_filename, 'rb') as plaintext, open(ciphertext_filename, 'wb') as ciphertext: with aws_encryption_sdk.stream( source=plaintext, mode='e', key_provider=local_master_key_provider ) as encryptor: for chunk in encryptor: ciphertext.write(chunk) print("Encrypted file in: %s [%s bytes]" % (ciphertext_filename, len(ciphertext_filename))) return ciphertext_filename
def decrypt_file_to_env(self, key_alias, input_filename): key_provider = self.build_kms_master_key_provider(key_alias) used_keys = [] output = cStringIO.StringIO() p = subprocess.Popen(['/bin/bash'], env={}, stdin=subprocess.PIPE, stdout=subprocess.PIPE) with open(input_filename, 'rb') as infile, \ aws_encryption_sdk.stream( mode='d', source=infile, key_provider=key_provider ) as decryptor: for chunk in decryptor: output.write(chunk) output.seek(0) for output_line in output: (key, _, _) = output_line.partition("=") used_keys.append(key) p.stdin.write(output_line) p.stdin.write('\nset\nexit\n') for line in p.stdout: (key, _, value) = line.partition("=") if key in used_keys: print(line.strip())
def test_decryptor_deprecated_attributes(caplog, attribute, no_later_than): caplog.set_level(logging.WARNING) plaintext = exact_length_plaintext(100) key_provider = fake_kms_key_provider() ciphertext, _header = aws_encryption_sdk.encrypt(source=plaintext, key_provider=key_provider, frame_length=0) with aws_encryption_sdk.stream(mode="decrypt", source=ciphertext, key_provider=key_provider) as decryptor: decrypted = decryptor.read() assert decrypted == plaintext if aws_encryption_sdk.__version__ < no_later_than: _assert_deprecated_but_not_yet_removed( logcap=caplog, instance=decryptor, attribute_name=attribute, error_message= "StreamDecryptor.{name} is deprecated and will be removed in {version}" .format(name=attribute, version=no_later_than), no_later_than=no_later_than, ) else: _assert_decrypted_and_removed(instance=decryptor, attribute_name=attribute, removed_in=no_later_than)
def encrypt_file(self, source_file_name, target_file_name): """ Encrypt a file using aws_encryption SDK and a KMS key :param source_file_name: File name (path) to encrypt :type source_file_name: str :param target_file_name: File name (path) to be used as target encrypted file. Use to be a NamedTemporaryFile :type target_file_name: str """ logging.info('Open a AWS session for KMS actions using ') kms_key_provider = aws_encryption_sdk.KMSMasterKeyProvider( config=self.get_conn(), key_ids=[self.aws_kms_key_arn]) with open(source_file_name, 'rb') as pt_file, open(target_file_name, 'wb') as ct_file: with aws_encryption_sdk.stream(mode='e', source=pt_file, key_provider=kms_key_provider, frame_length=1024) as encryptor: for chunk in encryptor: ct_file.write(chunk) logging.info('Comparing source and target files:') logging.info('Source file size: {}kb'.format( os.stat(source_file_name).st_size >> 10)) logging.info('Source file type: [{}]'.format( magic.from_file(source_file_name))) logging.info('Taget file size: {}kb'.format( os.stat(target_file_name).st_size >> 10)) logging.info('Target file type: [{}]'.format( magic.from_file(target_file_name))) return target_file_name
def _stream(self, source, destination, mode): with aws_encryption_sdk.stream( mode=mode, source=source, key_provider=self._kms_key_provider) as encryptor: for chunk in encryptor: destination.write(chunk) destination.seek(0)
def test_stream_decryptor_decrypt(self): test = aws_encryption_sdk.stream( mode='DECRYPT', a=sentinel.a, b=sentinel.b, c=sentinel.b ) assert test is self.mock_stream_decryptor_instance
def run(source_plaintext_filename): """Encrypts and then decrypts a file under a custom static master key provider. :param str source_plaintext_filename: Filename of file to encrypt """ # Create a static random master key provider key_id = os.urandom(8) master_key_provider = StaticRandomMasterKeyProvider() master_key_provider.add_master_key(key_id) ciphertext_filename = source_plaintext_filename + ".encrypted" cycled_plaintext_filename = source_plaintext_filename + ".decrypted" # Encrypt the plaintext source data with open(source_plaintext_filename, "rb") as plaintext, open(ciphertext_filename, "wb") as ciphertext: with aws_encryption_sdk.stream( mode="e", source=plaintext, key_provider=master_key_provider) as encryptor: for chunk in encryptor: ciphertext.write(chunk) # Decrypt the ciphertext with open(ciphertext_filename, "rb") as ciphertext, open(cycled_plaintext_filename, "wb") as plaintext: with aws_encryption_sdk.stream( mode="d", source=ciphertext, key_provider=master_key_provider) as decryptor: for chunk in decryptor: plaintext.write(chunk) # Verify that the "cycled" (encrypted, then decrypted) plaintext is identical to the source # plaintext assert filecmp.cmp(source_plaintext_filename, cycled_plaintext_filename) # Verify that the encryption context used in the decrypt operation includes all key pairs from # the encrypt operation # # In production, always use a meaningful encryption context. In this sample, we omit the # encryption context (no key pairs). assert all(pair in decryptor.header.encryption_context.items() for pair in encryptor.header.encryption_context.items()) return ciphertext_filename, cycled_plaintext_filename
def test_stream_decryptor_d(self): test = aws_encryption_sdk.stream(mode="d", a=sentinel.a, b=sentinel.b, c=sentinel.b) assert test is self.mock_stream_decryptor_instance self.mock_stream_decryptor.assert_called_once_with(a=sentinel.a, b=sentinel.b, c=sentinel.b)
def test_encryption_cycle_default_algorithm_framed_stream(self): """Test that the enrypt/decrypt cycle completes successfully for a framed message using the default algorithm. """ with aws_encryption_sdk.stream( source=io.BytesIO(VALUES['plaintext_128']), key_provider=self.kms_master_key_provider, mode='e', encryption_context=VALUES['encryption_context']) as encryptor: ciphertext = encryptor.read() header_1 = encryptor.header with aws_encryption_sdk.stream( source=io.BytesIO(ciphertext), key_provider=self.kms_master_key_provider, mode='d') as decryptor: plaintext = decryptor.read() header_2 = decryptor.header assert plaintext == VALUES['plaintext_128'] assert header_1.encryption_context == header_2.encryption_context
def test_incomplete_read_stream_cycle(frame_length): chunk_size = 21 # Will never be an exact match for the frame size key_provider = fake_kms_key_provider() plaintext = exact_length_plaintext(384) ciphertext = b"" cycle_count = 0 with aws_encryption_sdk.stream( mode="encrypt", source=SometimesIncompleteReaderIO(plaintext), key_provider=key_provider, frame_length=frame_length, ) as encryptor: while True: cycle_count += 1 chunk = encryptor.read(chunk_size) if not chunk: break ciphertext += chunk if cycle_count > len(VALUES["plaintext_128"]): raise aws_encryption_sdk.exceptions.AWSEncryptionSDKClientError( "Unexpected error encrypting message: infinite loop detected." ) decrypted = b"" cycle_count = 0 with aws_encryption_sdk.stream( mode="decrypt", source=SometimesIncompleteReaderIO(ciphertext), key_provider=key_provider) as decryptor: while True: cycle_count += 1 chunk = decryptor.read(chunk_size) if not chunk: break decrypted += chunk if cycle_count > len(VALUES["plaintext_128"]): raise aws_encryption_sdk.exceptions.AWSEncryptionSDKClientError( "Unexpected error encrypting message: infinite loop detected." ) assert ciphertext != decrypted == plaintext
def encrypt_file(file): ct_filename = str(file) + '.ct' with open(file, 'rb') as pt_file, open(ct_filename, 'wb') as ct_file: with aws_encryption_sdk.stream( mode='e', source=pt_file, key_provider=kms_key_provider ) as encryptor: for chunk in encryptor: ct_file.write(chunk) return ct_filename
def decrypt_file(self, key_alias, input_filename, output_filename): key_provider = self.build_kms_master_key_provider(key_alias) with open(input_filename, 'rb') as infile, \ open(output_filename, 'wb') as outfile, \ aws_encryption_sdk.stream( mode='d', source=infile, key_provider=key_provider ) as decryptor: for chunk in decryptor: outfile.write(chunk)
def _single_io_write(self, stream_args, source, destination_writer): # type: (STREAM_KWARGS, IO, IO) -> OperationResult """Performs the actual write operations for a single operation. :param dict stream_args: kwargs to pass to `aws_encryption_sdk.stream` :param source: source to write :type source: file-like object :param destination_writer: destination object to which to write :type destination_writer: file-like object :returns: OperationResult stating whether the file was written :rtype: aws_encryption_sdk_cli.internal.identifiers.OperationResult """ with _encoder(source, self.decode_input) as _source, _encoder( destination_writer, self.encode_output ) as _destination: # noqa pylint: disable=line-too-long with aws_encryption_sdk.stream(source=_source, **stream_args) as handler, self.metadata_writer as metadata: metadata_kwargs = dict( mode=stream_args["mode"], input=source.name, output=destination_writer.name, header=json_ready_header(handler.header), ) try: header_auth = handler.header_auth except AttributeError: # EncryptStream doesn't expose the header auth at this time pass else: metadata_kwargs["header_auth"] = json_ready_header_auth(header_auth) if stream_args["mode"] == "decrypt": discovered_ec = handler.header.encryption_context missing_keys = set(self.required_encryption_context_keys).difference(set(discovered_ec.keys())) missing_pairs = set(self.required_encryption_context.items()).difference(set(discovered_ec.items())) if missing_keys or missing_pairs: _LOGGER.warning( "Skipping decrypt because discovered encryption context did not match required elements." ) metadata_kwargs.update( dict( skipped=True, reason="Missing encryption context key or value", missing_encryption_context_keys=list(missing_keys), missing_encryption_context_pairs=list(missing_pairs), ) ) metadata.write_metadata(**metadata_kwargs) return OperationResult.FAILED_VALIDATION metadata.write_metadata(**metadata_kwargs) for chunk in handler: _destination.write(chunk) _destination.flush() return OperationResult.SUCCESS
def decrypt_file(file): pt_filename = splitext(file)[0] with open(file, 'rb') as ct_file, open(pt_filename, 'wb') as pt_file: with aws_encryption_sdk.stream( mode='d', source=ct_file, key_provider=kms_key_provider ) as decryptor: for chunk in decryptor: pt_file.write(chunk) return pt_filename
def cycle_file(source_plaintext_filename): """Encrypts and then decrypts a file under a custom static Master Key Provider. :param str source_plaintext_filename: Filename of file to encrypt """ # Create the Static Random Master Key Provider key_id = os.urandom(8) master_key_provider = StaticRandomMasterKeyProvider() master_key_provider.add_master_key(key_id) ciphertext_filename = source_plaintext_filename + '.encrypted' cycled_plaintext_filename = source_plaintext_filename + '.decrypted' # Encrypt the source plaintext with open(source_plaintext_filename, 'rb') as plaintext, open(ciphertext_filename, 'wb') as ciphertext: with aws_encryption_sdk.stream( mode='e', source=plaintext, key_provider=master_key_provider) as encryptor: for chunk in encryptor: ciphertext.write(chunk) # Decrypt the ciphertext with open(ciphertext_filename, 'rb') as ciphertext, open(cycled_plaintext_filename, 'wb') as plaintext: with aws_encryption_sdk.stream( mode='d', source=ciphertext, key_provider=master_key_provider) as decryptor: for chunk in decryptor: plaintext.write(chunk) # Validate that the cycled plaintext is identical to the source plaintext assert filecmp.cmp(source_plaintext_filename, cycled_plaintext_filename) # Validate that the encryption context used by the decryptor has all the key-pairs from the encryptor assert all(pair in decryptor.header.encryption_context.items() for pair in encryptor.header.encryption_context.items()) return ciphertext_filename, cycled_plaintext_filename
def test_plaintext_logs_stream(caplog, capsys, plaintext_length, frame_size): plaintext, key_provider = _prep_plaintext_and_logs(caplog, plaintext_length) ciphertext = b"" with aws_encryption_sdk.stream( mode="encrypt", source=plaintext, key_provider=key_provider, frame_length=frame_size ) as encryptor: for line in encryptor: ciphertext += line _look_in_logs(caplog, plaintext) _error_check(capsys)
def test_encrypt_decrypt_header_only(): """Test that StreamDecryptor can extract header without reading ciphertext.""" ciphertext, encryptor_header = aws_encryption_sdk.encrypt( source=VALUES["plaintext_128"], key_provider=fake_kms_key_provider(), encryption_context=VALUES["encryption_context"], ) with aws_encryption_sdk.stream(mode="d", source=ciphertext, key_provider=fake_kms_key_provider()) as decryptor: decryptor_header = decryptor.header assert decryptor.output_buffer == b"" assert all( pair in decryptor_header.encryption_context.items() for pair in encryptor_header.encryption_context.items() )
def test_encrypt_ciphertext_message(frame_length, algorithm, encryption_context): with aws_encryption_sdk.stream( mode="e", source=VALUES["plaintext_128"] * 10, key_provider=fake_kms_key_provider(algorithm.kdf_input_len), encryption_context=encryption_context, algorithm=algorithm, frame_length=frame_length, ) as encryptor: results_length = encryptor.ciphertext_length() ciphertext = encryptor.read() assert len(ciphertext) == results_length
def test_encrypt_decrypt_header_only(self): """Test that StreamDecryptor can extract header without reading ciphertext.""" ciphertext, encryptor_header = aws_encryption_sdk.encrypt( source=VALUES['plaintext_128'], key_provider=self.mock_kms_key_provider, encryption_context=VALUES['encryption_context']) with aws_encryption_sdk.stream( mode='d', source=ciphertext, key_provider=self.mock_kms_key_provider) as decryptor: decryptor_header = decryptor.header assert decryptor.output_buffer == b'' assert all(pair in decryptor_header.encryption_context.items() for pair in encryptor_header.encryption_context.items())
def test_encrypt_decrypt_cycle_aws_kms_streaming(plaintext): keyring = build_aws_kms_keyring() ciphertext = b"" with aws_encryption_sdk.stream( source=io.BytesIO(plaintext), keyring=keyring, mode="e", encryption_context=VALUES["encryption_context"], ) as encryptor: for chunk in encryptor: ciphertext += chunk header_1 = encryptor.header decrypted = b"" with aws_encryption_sdk.stream(source=io.BytesIO(ciphertext), keyring=keyring, mode="d") as decryptor: for chunk in decryptor: decrypted += chunk header_2 = decryptor.header assert decrypted == plaintext assert header_1.encryption_context == header_2.encryption_context
def encrypt(self): kms_key_provider = KMSMasterKeyProvider( key_ids=[self.kms_key], botocore_session=Session(profile=self.aws_profile)) with open(self.path, 'rb') as plain_file, open(self.out_path, 'wb') as cipher_file: with aws_encryption_sdk.stream( mode='e', source=plain_file, key_provider=kms_key_provider, encryption_context=self.encryption_context, frame_length=1048576) as encryptor: for chunk in encryptor: cipher_file.write(chunk)
def test_encryption_cycle_default_algorithm_framed_stream_many_lines_with_statement( self): """Test that the streaming enrypt/decrypt cycle completes successfully using the iterator behavior. """ ciphertext = b'' with aws_encryption_sdk.stream( source=io.BytesIO(VALUES['plaintext_128'] * 100), key_provider=self.mock_kms_key_provider, mode='e', encryption_context=VALUES['encryption_context'], frame_length=128) as encryptor: for chunk in encryptor: ciphertext += chunk header_1 = encryptor.header plaintext = b'' with aws_encryption_sdk.stream(source=io.BytesIO(ciphertext), key_provider=self.mock_kms_key_provider, mode='d') as decryptor: for chunk in decryptor: plaintext += chunk header_2 = decryptor.header assert plaintext == VALUES['plaintext_128'] * 100 assert header_1.encryption_context == header_2.encryption_context
def decrypt(self): kms_key_provider = KMSMasterKeyProvider( key_ids=[self.kms_key], botocore_session=Session(profile=self.aws_profile)) with open(self.path, 'rb') as cipher_file, open(self.out_path, 'wb') as plain_file: with aws_encryption_sdk.stream( mode='d', source=cipher_file, key_provider=kms_key_provider) as decryptor: encrypted_context = decryptor.header.encryption_context if self._dictionary_is_subset( encrypted_context, self.encryption_context) is False: decryptor.footer = None # Set the footer so that when decryptor is closed when leaving the with # context due to the raised exception, it doesn't throw its own unneeded exception raise EncryptionContextMismatch() for chunk in decryptor: plain_file.write(chunk)
def test_decryptor_deprecated_attributes(caplog, attribute, no_later_than): caplog.set_level(logging.WARNING) plaintext = exact_length_plaintext(100) key_provider = fake_kms_key_provider() ciphertext, _header = aws_encryption_sdk.encrypt(source=plaintext, key_provider=key_provider, frame_length=0) with aws_encryption_sdk.stream(mode="decrypt", source=ciphertext, key_provider=key_provider) as decryptor: decrypted = decryptor.read() assert decrypted == plaintext assert hasattr(decryptor, attribute) watch_string = "StreamDecryptor.{name} is deprecated and will be removed in {version}".format( name=attribute, version=no_later_than) assert watch_string in caplog.text assert aws_encryption_sdk.__version__ < no_later_than