def setUp(self): # Set up KMSMasterKeyProvider patch self.mock_kms_client = MagicMock() self.mock_kms_client.__class__ = botocore.client.BaseClient self.mock_kms_client.generate_data_key.return_value = { 'Plaintext': six.b(''.join(VALUES['data_key_256'])), 'CiphertextBlob': VALUES['encrypted_data_key'], 'KeyId': VALUES['arn'] } self.mock_kms_client.encrypt.return_value = { 'CiphertextBlob': VALUES['encrypted_data_key'], 'KeyId': VALUES['arn'] } self.mock_kms_client.decrypt.return_value = { 'Plaintext': six.b(''.join(VALUES['data_key_256'])), 'KeyId': VALUES['arn'] } self.mock_kms_key_provider = KMSMasterKeyProvider() self.mock_kms_key_provider._regional_clients[ 'us-east-1'] = self.mock_kms_client self.mock_kms_key_provider.add_master_key(VALUES['arn']) self.fake_raw_key_provider_aes_gcm = FakeRawMasterKeyProvider( wrapping_algorithm=WrappingAlgorithm. AES_256_GCM_IV12_TAG16_NO_PADDING, encryption_key_type=EncryptionKeyType.SYMMETRIC) self.fake_raw_key_provider_aes_gcm.add_master_key('sym1') self.fake_raw_key_provider_rsa_pkcs1_private_key = FakeRawMasterKeyProvider( wrapping_algorithm=WrappingAlgorithm.RSA_PKCS1, encryption_key_type=EncryptionKeyType.PRIVATE) self.fake_raw_key_provider_rsa_pkcs1_private_key.add_master_key( 'asym1') self.fake_raw_key_provider_rsa_pkcs1_public_key = FakeRawMasterKeyProvider( wrapping_algorithm=WrappingAlgorithm.RSA_PKCS1, encryption_key_type=EncryptionKeyType.PUBLIC) self.fake_raw_key_provider_rsa_pkcs1_public_key.add_master_key('asym1') self.fake_raw_key_provider_rsa_oaep_sha1_private_key = FakeRawMasterKeyProvider( wrapping_algorithm=WrappingAlgorithm.RSA_OAEP_SHA1_MGF1, encryption_key_type=EncryptionKeyType.PRIVATE) self.fake_raw_key_provider_rsa_oaep_sha1_private_key.add_master_key( 'asym1') self.fake_raw_key_provider_rsa_oaep_sha1_public_key = FakeRawMasterKeyProvider( wrapping_algorithm=WrappingAlgorithm.RSA_OAEP_SHA1_MGF1, encryption_key_type=EncryptionKeyType.PUBLIC) self.fake_raw_key_provider_rsa_oaep_sha1_public_key.add_master_key( 'asym1') self.fake_raw_key_provider_rsa_oaep_sha256_private_key = FakeRawMasterKeyProvider( wrapping_algorithm=WrappingAlgorithm.RSA_OAEP_SHA256_MGF1, encryption_key_type=EncryptionKeyType.PRIVATE) self.fake_raw_key_provider_rsa_oaep_sha256_private_key.add_master_key( 'asym1') self.fake_raw_key_provider_rsa_oaep_sha256_public_key = FakeRawMasterKeyProvider( wrapping_algorithm=WrappingAlgorithm.RSA_OAEP_SHA256_MGF1, encryption_key_type=EncryptionKeyType.PUBLIC) self.fake_raw_key_provider_rsa_oaep_sha256_public_key.add_master_key( 'asym1')
def encrypt_decrypt(key_arn, source_plaintext, botocore_session=None): """Encrypts and then decrypts a string under one KMS customer master key (CMK) with an unsigned algorithm. :param str key_arn: Amazon Resource Name (ARN) of the KMS CMK :param bytes source_plaintext: Data to encrypt :param botocore_session: existing botocore session instance :type botocore_session: botocore.session.Session """ kwargs = dict(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 = KMSMasterKeyProvider(**kwargs) # Encrypt the plaintext using the AWS Encryption SDK. It returns the encrypted message and the header ciphertext, encrypted_message_header = encrypt( algorithm=Algorithm.AES_256_GCM_IV12_TAG16_HKDF_SHA256, source=source_plaintext, key_provider=kms_key_provider) # Decrypt the encrypted message using the AWS Encryption SDK. It returns the decrypted message and the header plaintext, decrypted_message_header = decrypt( source=ciphertext, key_provider=kms_key_provider) # Check if the original message and the decrypted message are the same assert source_plaintext == plaintext # Check if the headers of the encrypted message and decrypted message match assert all(pair in encrypted_message_header.encryption_context.items() for pair in decrypted_message_header.encryption_context.items())
def aws_kms_master_key_provider(**kwargs): # type: (**List[Union[Text, str]]) -> KMSMasterKeyProvider """Apply post-processing to transform ``KMSMasterKeyProvider``-specific values from CLI arguments to valid ``KMSMasterKeyProvider`` parameters, then call ``KMSMasterKeyprovider`` with those parameters. :param dict kwargs: Named parameters collected from CLI arguments as prepared in aws_encryption_sdk_cli.internal.master_key_parsing._parse_master_key_providers_from_args :rtype: aws_encryption_sdk.key_providers.kms.KMSMasterKeyProvider """ kwargs = copy.deepcopy(kwargs) try: profile_names = kwargs.pop("profile") if len(profile_names) != 1: raise BadUserArgumentError( "Only one profile may be specified per master key provider configuration. {} provided." .format(len(profile_names))) profile_name = profile_names[0] except KeyError: profile_name = None botocore_session = botocore.session.Session(profile=profile_name) botocore_session.user_agent_extra = USER_AGENT_SUFFIX kwargs["botocore_session"] = botocore_session try: region_name = kwargs.pop("region") if len(region_name) != 1: raise BadUserArgumentError( "Only one region may be specified per master key provider configuration. {} provided." .format(len(region_name))) kwargs["region_names"] = region_name except KeyError: pass return KMSMasterKeyProvider(**kwargs)
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 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 fake_kms_key_provider(keysize=32): mock_kms_key_provider = KMSMasterKeyProvider() mock_kms_key_provider._regional_clients["us-east-1"] = fake_kms_client( keysize) mock_kms_key_provider.add_master_key(VALUES["arn"]) return mock_kms_key_provider
class TestAwsEncryptionSdkFunctional(unittest.TestCase): def setUp(self): # Set up KMSMasterKeyProvider patch self.mock_kms_client = MagicMock() self.mock_kms_client.__class__ = botocore.client.BaseClient self.mock_kms_client.generate_data_key.return_value = { 'Plaintext': six.b(''.join(VALUES['data_key_256'])), 'CiphertextBlob': VALUES['encrypted_data_key'], 'KeyId': VALUES['arn'] } self.mock_kms_client.encrypt.return_value = { 'CiphertextBlob': VALUES['encrypted_data_key'], 'KeyId': VALUES['arn'] } self.mock_kms_client.decrypt.return_value = { 'Plaintext': six.b(''.join(VALUES['data_key_256'])), 'KeyId': VALUES['arn'] } self.mock_kms_key_provider = KMSMasterKeyProvider() self.mock_kms_key_provider._regional_clients[ 'us-east-1'] = self.mock_kms_client self.mock_kms_key_provider.add_master_key(VALUES['arn']) self.fake_raw_key_provider_aes_gcm = FakeRawMasterKeyProvider( wrapping_algorithm=WrappingAlgorithm. AES_256_GCM_IV12_TAG16_NO_PADDING, encryption_key_type=EncryptionKeyType.SYMMETRIC) self.fake_raw_key_provider_aes_gcm.add_master_key('sym1') self.fake_raw_key_provider_rsa_pkcs1_private_key = FakeRawMasterKeyProvider( wrapping_algorithm=WrappingAlgorithm.RSA_PKCS1, encryption_key_type=EncryptionKeyType.PRIVATE) self.fake_raw_key_provider_rsa_pkcs1_private_key.add_master_key( 'asym1') self.fake_raw_key_provider_rsa_pkcs1_public_key = FakeRawMasterKeyProvider( wrapping_algorithm=WrappingAlgorithm.RSA_PKCS1, encryption_key_type=EncryptionKeyType.PUBLIC) self.fake_raw_key_provider_rsa_pkcs1_public_key.add_master_key('asym1') self.fake_raw_key_provider_rsa_oaep_sha1_private_key = FakeRawMasterKeyProvider( wrapping_algorithm=WrappingAlgorithm.RSA_OAEP_SHA1_MGF1, encryption_key_type=EncryptionKeyType.PRIVATE) self.fake_raw_key_provider_rsa_oaep_sha1_private_key.add_master_key( 'asym1') self.fake_raw_key_provider_rsa_oaep_sha1_public_key = FakeRawMasterKeyProvider( wrapping_algorithm=WrappingAlgorithm.RSA_OAEP_SHA1_MGF1, encryption_key_type=EncryptionKeyType.PUBLIC) self.fake_raw_key_provider_rsa_oaep_sha1_public_key.add_master_key( 'asym1') self.fake_raw_key_provider_rsa_oaep_sha256_private_key = FakeRawMasterKeyProvider( wrapping_algorithm=WrappingAlgorithm.RSA_OAEP_SHA256_MGF1, encryption_key_type=EncryptionKeyType.PRIVATE) self.fake_raw_key_provider_rsa_oaep_sha256_private_key.add_master_key( 'asym1') self.fake_raw_key_provider_rsa_oaep_sha256_public_key = FakeRawMasterKeyProvider( wrapping_algorithm=WrappingAlgorithm.RSA_OAEP_SHA256_MGF1, encryption_key_type=EncryptionKeyType.PUBLIC) self.fake_raw_key_provider_rsa_oaep_sha256_public_key.add_master_key( 'asym1') def test_no_infinite_encryption_cycle_on_empty_source(self): """This catches a race condition where when calling encrypt with an empty byte string, encrypt would enter an infinite loop. If this test does not hang, the race condition is not present. """ aws_encryption_sdk.encrypt(source=b'', key_provider=self.mock_kms_key_provider) def test_encrypt_load_header(self): """Test that StreamEncryptor can extract header without reading plaintext.""" # Using a non-signed algorithm to simplify header size calculation algorithm = aws_encryption_sdk.Algorithm.AES_256_GCM_IV12_TAG16_HKDF_SHA256 header_length = len( serialize_encryption_context(VALUES['encryption_context'])) header_length += 34 header_length += algorithm.iv_len header_length += algorithm.auth_len header_length += 6 + 7 + len(VALUES['arn']) + len( VALUES['encrypted_data_key']) with aws_encryption_sdk.stream( mode='e', source=VALUES['plaintext_128'], key_provider=self.mock_kms_key_provider, encryption_context=VALUES['encryption_context'], algorithm=algorithm, frame_length=1024) as encryptor: encryptor_header = encryptor.header # Ensure that only the header has been written into the output buffer assert len(encryptor.output_buffer) == header_length assert encryptor_header.encryption_context == VALUES[ 'encryption_context'] 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_encryption_cycle_default_algorithm_non_framed(self): """Test that the enrypt/decrypt cycle completes successfully for a non-framed message using the default algorithm. """ ciphertext, _ = aws_encryption_sdk.encrypt( source=VALUES['plaintext_128'], key_provider=self.mock_kms_key_provider, encryption_context=VALUES['encryption_context'], frame_length=0) plaintext, _ = aws_encryption_sdk.decrypt( source=ciphertext, key_provider=self.mock_kms_key_provider) assert plaintext == VALUES['plaintext_128'] def test_encryption_cycle_default_algorithm_non_framed_fake_aes(self): """Test that the enrypt/decrypt cycle completes successfully for a non-framed message using the default algorithm and the fake AES-GCM key provider. """ key_provider = self.fake_raw_key_provider_aes_gcm ciphertext, _ = aws_encryption_sdk.encrypt( source=VALUES['plaintext_128'], key_provider=key_provider, encryption_context=VALUES['encryption_context'], frame_length=0) plaintext, _ = aws_encryption_sdk.decrypt(source=ciphertext, key_provider=key_provider) assert plaintext == VALUES['plaintext_128'] def test_encryption_cycle_default_algorithm_non_framed_fake_rsa_pkcs(self): """Test that the enrypt/decrypt cycle completes successfully for a non-framed message using the default algorithm and the fake RSA-PKCS key provider. """ key_provider = self.fake_raw_key_provider_rsa_pkcs1_private_key ciphertext, _ = aws_encryption_sdk.encrypt( source=VALUES['plaintext_128'], key_provider=key_provider, encryption_context=VALUES['encryption_context'], frame_length=0) plaintext, _ = aws_encryption_sdk.decrypt(source=ciphertext, key_provider=key_provider) assert plaintext == VALUES['plaintext_128'] def test_encryption_cycle_default_algorithm_non_framed_fake_rsa_pkcs_asymmetric( self): """Test that the enrypt/decrypt cycle completes successfully for a non-framed message using the default algorithm and the fake RSA-PKCS key provider. """ ciphertext, _ = aws_encryption_sdk.encrypt( source=VALUES['plaintext_128'], key_provider=self.fake_raw_key_provider_rsa_pkcs1_public_key, encryption_context=VALUES['encryption_context'], frame_length=0) plaintext, _ = aws_encryption_sdk.decrypt( source=ciphertext, key_provider=self.fake_raw_key_provider_rsa_pkcs1_private_key) assert plaintext == VALUES['plaintext_128'] def test_encryption_cycle_default_algorithm_non_framed_fake_rsa_oaep_sha1( self): """Test that the enrypt/decrypt cycle completes successfully for a non-framed message using the default algorithm and the fake RSA-OAEP-SHA1 key provider. """ key_provider = self.fake_raw_key_provider_rsa_oaep_sha1_private_key ciphertext, _ = aws_encryption_sdk.encrypt( source=VALUES['plaintext_128'], key_provider=key_provider, encryption_context=VALUES['encryption_context'], frame_length=0) plaintext, _ = aws_encryption_sdk.decrypt(source=ciphertext, key_provider=key_provider) assert plaintext == VALUES['plaintext_128'] def test_encryption_cycle_default_algorithm_non_framed_fake_rsa_oaep_sha1_asymmetric( self): """Test that the enrypt/decrypt cycle completes successfully for a non-framed message using the default algorithm and the fake RSA-OAEP-SHA1 key provider. """ ciphertext, _ = aws_encryption_sdk.encrypt( source=VALUES['plaintext_128'], key_provider=self.fake_raw_key_provider_rsa_oaep_sha1_public_key, encryption_context=VALUES['encryption_context'], frame_length=0) plaintext, _ = aws_encryption_sdk.decrypt( source=ciphertext, key_provider=self.fake_raw_key_provider_rsa_oaep_sha1_private_key) assert plaintext == VALUES['plaintext_128'] @unittest.skipUnless(_mgf1_sha256_supported(), 'MGF1-SHA256 not supported by this backend') def test_encryption_cycle_default_algorithm_non_framed_fake_rsa_oaep_sha256( self): """Test that the enrypt/decrypt cycle completes successfully for a non-framed message using the default algorithm and the fake RSA-OAEP-SHA256 key provider. """ key_provider = self.fake_raw_key_provider_rsa_oaep_sha256_private_key ciphertext, _ = aws_encryption_sdk.encrypt( source=VALUES['plaintext_128'], key_provider=key_provider, encryption_context=VALUES['encryption_context'], frame_length=0) plaintext, _ = aws_encryption_sdk.decrypt(source=ciphertext, key_provider=key_provider) assert plaintext == VALUES['plaintext_128'] @unittest.skipUnless(_mgf1_sha256_supported(), 'MGF1-SHA256 not supported by this backend') def test_encryption_cycle_default_algorithm_non_framed_fake_rsa_oaep_sha256_asymmetric( self): """Test that the enrypt/decrypt cycle completes successfully for a non-framed message using the default algorithm and the fake RSA-OAEP-SHA256 key provider. """ ciphertext, _ = aws_encryption_sdk.encrypt( source=VALUES['plaintext_128'], key_provider=self.fake_raw_key_provider_rsa_oaep_sha256_public_key, encryption_context=VALUES['encryption_context'], frame_length=0) plaintext, _ = aws_encryption_sdk.decrypt( source=ciphertext, key_provider=self.fake_raw_key_provider_rsa_oaep_sha256_private_key ) assert plaintext == VALUES['plaintext_128'] def test_encryption_cycle_default_algorithm_non_framed_no_encryption_context( self): """Test that the enrypt/decrypt cycle completes successfully for a non-framed message using the default algorithm. """ ciphertext, _ = aws_encryption_sdk.encrypt( source=VALUES['plaintext_128'], key_provider=self.mock_kms_key_provider, frame_length=0) plaintext, _ = aws_encryption_sdk.decrypt( source=ciphertext, key_provider=self.mock_kms_key_provider) assert plaintext == VALUES['plaintext_128'] def test_encryption_cycle_default_algorithm_single_frame(self): """Test that the enrypt/decrypt cycle completes successfully for a single frame message using the default algorithm. """ ciphertext, _ = aws_encryption_sdk.encrypt( source=VALUES['plaintext_128'], key_provider=self.mock_kms_key_provider, encryption_context=VALUES['encryption_context'], frame_length=1024) plaintext, _ = aws_encryption_sdk.decrypt( source=ciphertext, key_provider=self.mock_kms_key_provider) assert plaintext == VALUES['plaintext_128'] def test_encryption_cycle_default_algorithm_multiple_frames(self): """Test that the enrypt/decrypt cycle completes successfully for a framed message with multiple frames using the default algorithm. """ ciphertext, _ = aws_encryption_sdk.encrypt( source=VALUES['plaintext_128'] * 100, key_provider=self.mock_kms_key_provider, encryption_context=VALUES['encryption_context'], frame_length=1024) plaintext, _ = aws_encryption_sdk.decrypt( source=ciphertext, key_provider=self.mock_kms_key_provider) assert plaintext == VALUES['plaintext_128'] * 100 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. """ encryptor = aws_encryption_sdk.stream( source=io.BytesIO(VALUES['plaintext_128']), key_provider=self.mock_kms_key_provider, mode='e', encryption_context=VALUES['encryption_context']) ciphertext = encryptor.read() encryptor.close() header_1 = encryptor.header decryptor = aws_encryption_sdk.stream( source=io.BytesIO(ciphertext), key_provider=self.mock_kms_key_provider, mode='d') plaintext = decryptor.read() decryptor.close() header_2 = decryptor.header assert plaintext == VALUES['plaintext_128'] assert header_1.encryption_context == header_2.encryption_context def test_encryption_cycle_default_algorithm_framed_stream_many_lines_readlines( 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.readlines(): 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.readlines(): 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_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_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 test_decrypt_legacy_provided_message(self): """Tests backwards compatiblity against some legacy provided ciphertext.""" region = 'us-west-2' key_info = 'arn:aws:kms:us-west-2:249645522726:key/d1720f4e-953b-44bb-b9dd-fc8b9d0baa5f' self.mock_kms_client.decrypt.return_value = { 'Plaintext': six.b(''.join(VALUES['provided']['key'])) } self.mock_kms_key_provider._regional_clients[ region] = self.mock_kms_client self.mock_kms_key_provider.add_master_key(key_info) plaintext, _ = aws_encryption_sdk.decrypt( source=VALUES['provided']['ciphertext'], key_provider=self.mock_kms_key_provider) self.assertEqual(plaintext, VALUES['provided']['plaintext'])