def deserialize_wrapped_key(wrapping_algorithm, wrapping_key_id, wrapped_encrypted_key): """Extracts and deserializes EncryptedData from a Wrapped EncryptedDataKey. :param wrapping_algorithm: Wrapping Algorithm with which to wrap plaintext_data_key :type wrapping_algorithm: aws_encryption_sdk.identifiers.WrappingAlgorithm :param bytes wrapping_key_id: Key ID of wrapping MasterKey :param wrapped_encrypted_key: Raw Wrapped EncryptedKey :type wrapped_encrypted_key: aws_encryption_sdk.structures.EncryptedDataKey :returns: EncryptedData of deserialized Wrapped EncryptedKey :rtype: aws_encryption_sdk.internal.structures.EncryptedData :raises SerializationError: if wrapping_key_id does not match deserialized wrapping key id :raises SerializationError: if wrapping_algorithm IV length does not match deserialized IV length """ if wrapping_key_id == wrapped_encrypted_key.key_provider.key_info: encrypted_wrapped_key = EncryptedData(iv=None, ciphertext=wrapped_encrypted_key.encrypted_data_key, tag=None) else: if not wrapped_encrypted_key.key_provider.key_info.startswith(wrapping_key_id): raise SerializationError("Master Key mismatch for wrapped data key") _key_info = wrapped_encrypted_key.key_provider.key_info[len(wrapping_key_id) :] try: tag_len, iv_len = struct.unpack(">II", _key_info[:8]) except struct.error: raise SerializationError("Malformed key info: key info missing data") tag_len //= 8 # Tag Length is stored in bits, not bytes if iv_len != wrapping_algorithm.algorithm.iv_len: raise SerializationError("Wrapping AlgorithmSuite mismatch for wrapped data key") iv = _key_info[8:] if len(iv) != iv_len: raise SerializationError("Malformed key info: incomplete iv") ciphertext = wrapped_encrypted_key.encrypted_data_key[: -1 * tag_len] tag = wrapped_encrypted_key.encrypted_data_key[-1 * tag_len :] if not ciphertext or len(tag) != tag_len: raise SerializationError("Malformed key info: incomplete ciphertext or tag") encrypted_wrapped_key = EncryptedData(iv=iv, ciphertext=ciphertext, tag=tag) return encrypted_wrapped_key
def encrypt(self, plaintext_data_key, encryption_context): """Encrypts a data key using a direct wrapping key. :param bytes plaintext_data_key: Data key to encrypt :param dict encryption_context: Encryption context to use in encryption :returns: Deserialized object containing encrypted key :rtype: aws_encryption_sdk.internal.structures.EncryptedData """ if self.wrapping_algorithm.encryption_type is EncryptionType.ASYMMETRIC: if self.wrapping_key_type is EncryptionKeyType.PRIVATE: encrypted_key = self._wrapping_key.public_key().encrypt( plaintext=plaintext_data_key, padding=self.wrapping_algorithm.padding) else: encrypted_key = self._wrapping_key.encrypt( plaintext=plaintext_data_key, padding=self.wrapping_algorithm.padding) return EncryptedData(iv=None, ciphertext=encrypted_key, tag=None) serialized_encryption_context = serialize_encryption_context( encryption_context=encryption_context) iv = os.urandom(self.wrapping_algorithm.algorithm.iv_len) return encrypt(algorithm=self.wrapping_algorithm.algorithm, key=self._derived_wrapping_key, plaintext=plaintext_data_key, associated_data=serialized_encryption_context, iv=iv)
def test_decrypt_derived_key(self, mock_derive_key): """Validate that the decrypt function works as expected with a derived key. """ mock_derive_key.return_value = sentinel.derived_key test = aws_encryption_sdk.internal.crypto.decrypt( algorithm=self.mock_algorithm, key=sentinel.key, encrypted_data=EncryptedData(VALUES['iv'], VALUES['ciphertext'], VALUES['tag']), associated_data=sentinel.aad, message_id=sentinel.message_id) mock_derive_key.assert_called_with(source_key=sentinel.key, algorithm=self.mock_algorithm, message_id=sentinel.message_id) self.mock_cryptography_cipher.assert_called_with( sentinel.encryption_algorithm, sentinel.encryption_mode, backend=sentinel.crypto_backend) assert self.mock_cryptography_cipher_instance.decryptor.called self.mock_decryptor.authenticate_additional_data.assert_called_with( sentinel.aad) self.mock_decryptor.update.assert_called_with(VALUES['ciphertext']) assert self.mock_decryptor.finalize.called assert test, VALUES['plaintext']
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_deserialize_wrapped_key_asymmetric(self): test = aws_encryption_sdk.internal.formatting.deserialize.deserialize_wrapped_key( wrapping_algorithm=self.mock_wrapping_algorithm, wrapping_key_id=VALUES["wrapped_keys"]["raw"]["key_info"], wrapped_encrypted_key=VALUES["wrapped_keys"]["structures"]["wrapped_encrypted_data_key_asymmetric"], ) assert test == EncryptedData(iv=None, ciphertext=VALUES["wrapped_keys"]["raw"]["ciphertext"], tag=None)
def validate_header(header, header_auth, stream, header_start, header_end, data_key): """Validates the header using the header authentication data. :param header: Deserialized header :type header: aws_encryption_sdk.structures.MessageHeader :param header_auth: Deserialized header auth :type header_auth: aws_encryption_sdk.internal.structures.MessageHeaderAuthentication :param stream: Stream containing serialized message :type stream: io.BytesIO :param int header_start: Position in stream of start of serialized header :param int header_end: Position in stream of end of serialized header :param bytes data_key: Data key with which to perform validation :raises SerializationError: if header authorization fails """ _LOGGER.debug('Starting header validation') current_position = stream.tell() stream.seek(header_start) try: decrypt(algorithm=header.algorithm, key=data_key, encrypted_data=EncryptedData(header_auth.iv, b'', header_auth.tag), associated_data=stream.read(header_end - header_start)) except InvalidTag: raise SerializationError('Header authorization failed') stream.seek(current_position)
def test_deserialize_wrapped_key_symmetric(self): test = aws_encryption_sdk.internal.formatting.deserialize.deserialize_wrapped_key( wrapping_algorithm=self.mock_wrapping_algorithm, wrapping_key_id=VALUES['wrapped_keys']['raw']['key_info'], wrapped_encrypted_key=VALUES['wrapped_keys']['structures'] ['wrapped_encrypted_data_key_symmetric']) assert test == EncryptedData( iv=VALUES['wrapped_keys']['raw']['iv'], ciphertext=VALUES['wrapped_keys']['raw']['ciphertext'], tag=VALUES['wrapped_keys']['raw']['tag'])
def test_serialize_wrapped_key_asymmetric(self): test = aws_encryption_sdk.internal.formatting.serialize.serialize_wrapped_key( key_provider=self.mock_key_provider, wrapping_algorithm=self.mock_wrapping_algorithm, wrapping_key_id=VALUES['wrapped_keys']['raw']['key_info'], encrypted_wrapped_key=EncryptedData(iv=None, ciphertext=VALUES['data_128'], tag=None)) assert test == EncryptedDataKey(key_provider=MasterKeyInfo( provider_id=VALUES['provider_id'], key_info=VALUES['wrapped_keys']['raw']['key_info']), encrypted_data_key=VALUES['data_128'])
def encrypt(algorithm, key, plaintext, associated_data, iv): """Encrypts a frame body. :param algorithm: Algorithm used to encrypt this body :type algorithm: aws_encryption_sdk.identifiers.Algorithm :param bytes key: Encryption key :param bytes plaintext: Body plaintext :param bytes associated_data: Body AAD Data :param bytes iv: IV to use when encrypting message :returns: Deserialized object containing encrypted body :rtype: aws_encryption_sdk.internal.structures.EncryptedData """ encryptor = Encryptor(algorithm, key, associated_data, iv) ciphertext = encryptor.update(plaintext) + encryptor.finalize() return EncryptedData(encryptor.iv, ciphertext, encryptor.tag)
def test_encrypt(patch_encryptor): patch_encryptor.return_value.update.return_value = b"some data-" patch_encryptor.return_value.finalize.return_value = b"some more data" patch_encryptor.return_value.iv = b"ex iv" patch_encryptor.return_value.tag = b"ex tag" test = encrypt( algorithm=sentinel.algorithm, key=sentinel.key, plaintext=sentinel.plaintext, associated_data=sentinel.aad, iv=sentinel.iv, ) patch_encryptor.assert_called_once_with(sentinel.algorithm, sentinel.key, sentinel.aad, sentinel.iv) patch_encryptor.return_value.update.assert_called_once_with(sentinel.plaintext) patch_encryptor.return_value.finalize.assert_called_once_with() assert test == EncryptedData(iv=b"ex iv", ciphertext=b"some data-some more data", tag=b"ex tag")
def test_wrapping_key_encrypt_public(self, mock_encrypt, mock_serialize_ec): self.mock_wrapping_rsa_public_key.encrypt.return_value = VALUES[ 'ciphertext'] self.mock_wrapping_algorithm.encryption_type = EncryptionType.ASYMMETRIC test_wrapping_key = aws_encryption_sdk.internal.crypto.WrappingKey( wrapping_algorithm=self.mock_wrapping_algorithm, wrapping_key=self.mock_wrapping_key, wrapping_key_type=EncryptionKeyType.PUBLIC) test = test_wrapping_key.encrypt( plaintext_data_key=sentinel.plaintext_data_key, encryption_context=sentinel.encryption_context) assert not self.mock_wrapping_rsa_private_key.public_key.called self.mock_wrapping_rsa_public_key.encrypt.assert_called_once_with( plaintext=sentinel.plaintext_data_key, padding=sentinel.padding) assert not mock_serialize_ec.called assert not mock_encrypt.called assert test == EncryptedData(iv=None, ciphertext=VALUES['ciphertext'], tag=None)
def validate_header(header, header_auth, raw_header, data_key): """Validates the header using the header authentication data. :param header: Deserialized header :type header: aws_encryption_sdk.structures.MessageHeader :param header_auth: Deserialized header auth :type header_auth: aws_encryption_sdk.internal.structures.MessageHeaderAuthentication :type stream: io.BytesIO :param bytes raw_header: Raw header bytes :param bytes data_key: Data key with which to perform validation :raises SerializationError: if header authorization fails """ _LOGGER.debug('Starting header validation') try: decrypt(algorithm=header.algorithm, key=data_key, encrypted_data=EncryptedData(header_auth.iv, b'', header_auth.tag), associated_data=raw_header) except InvalidTag: raise SerializationError('Header authorization failed')
b("\x02\x05x_\xfd\xb3%\xa5}yd\x80}\xe2\x90\xf9\x0e&\x8f_\xfd\xb3%\xa5}yd\x80}\xe2\x90\xf9\x0e&\x8f\x00\x8f\x00" "\x04\x00\x15aws-crypto-public-key\x00DAmZvwV/dN6o9p/usAnJdRcdnE12UbaDHuEFPeyVkw5FC1ULGlSznzDdD3FP8SW1UMg" "==\x00\x05key_a\x00\x07value_a\x00\x05key_b\x00\x07value_b\x00\x05key_c\x00\x07value_c\x00\x01\x00\x07aws" "-kms\x00Karn:aws:kms:us-east-1:248168362296:key/ce78d3b3-f800-4785-a3b9-63e30bb4b183\x00\xcc\n " "\x8b\xc6\xfd\x91\xc7\xd5\xdc+S\x15n\xd9P\x99n\x1d\xb2\xdd\x15\xeaW\xc3\x13k2\xf6\x02\xd0\x0f\x85\xec\x9e\x12" "\xa7\x01\x01\x01\x01\x00x\x8b\xc6\xfd\x91\xc7\xd5\xdc+S\x15n\xd9P\x99n\x1d\xb2\xdd\x15\xeaW\xc3\x13k2\xf6" "\x02\xd0\x0f\x85\xec\x9e\x00\x00\x00~0|\x06\t*\x86H\x86\xf7\r\x01\x07\x06\xa0o0m\x02\x01\x000h\x06\t*\x86H" "\x86\xf7\r\x01\x07\x010\x1e\x06\t`\x86H\x01e\x03\x04\x01.0\x11\x04\x0c\xc9rP\xa1\x08t6{" "\xf2\xfd\xf1\xb3\x02\x01\x10\x80;D\xa4\xed`qP~c\x0f\xa0d\xd5\xa2Kj\xc7\xb2\xc6\x1e\xec\xfb\x0fK\xb2*\xd5\t2" '\x81pR\xee\xd1\x1a\xde<"\x1b\x98\x88\x8b\xf4&\xdaB\x95I\xd2\xff\x10\x13\xfc\x1aX\x08,' "/\x8b\x8b\x02\x00\x00\x00 \x00\xfa\x8c\xdd\x08Au\xc6\x92_4\xc5\xfb\x90\xaf\x8f\xa1D\xaf\xcc\xd25\xa8\x0b\x0b" "\x16\x92\x91W\x01\xb7\x84"), "header_auth_base": EncryptedData( iv=b"s\x15<P\xaa\x94\xb8\x931P\xeb\xa0", ciphertext=b"", tag=b"\x91\xc5\xf7<\x7f\xc9\xb1k\x0e\xe2{\xe4\x97\x9d\xdbU", ), "serialized_header_auth": b"s\x15<P\xaa\x94\xb8\x931P\xeb\xa0\x91\xc5\xf7<\x7f\xc9\xb1k\x0e\xe2{\xe4\x97\x9d\xdbU", "serialized_header_auth_v2": b"\x91\xc5\xf7<\x7f\xc9\xb1k\x0e\xe2{\xe4\x97\x9d\xdbU", "non_framed_aac": six.b("_\xfd\xb3%\xa5}yd\x80}\xe2\x90\xf9\x0e&\x8fAWSKMSEncryptionClient" " Single Block\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00 "), "frame_aac": six.b("_\xfd\xb3%\xa5}yd\x80}\xe2\x90\xf9\x0e&\x8fAWSKMSEncryptionClient" " Frame\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00 "), "final_frame_aac": six.b("_\xfd\xb3%\xa5}yd\x80}\xe2\x90\xf9\x0e&\x8fAWSKMSEncryptionClient" " Final Frame\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00 "),
def mock_encrypted_data(): return EncryptedData(iv=VALUES["iv"], ciphertext=VALUES["ciphertext"], tag=VALUES["tag"])
'\x07aws-kms\x00Karn:aws:kms:us-east-1:248168362296:key/ce78d3b3-f800' '-4785-a3b9-63e30bb4b183\x00\xcc\n \x8b\xc6\xfd\x91\xc7\xd5\xdc+S\x15' 'n\xd9P\x99n\x1d\xb2\xdd\x15\xeaW\xc3\x13k2\xf6\x02\xd0\x0f\x85\xec' '\x9e\x12\xa7\x01\x01\x01\x01\x00x\x8b\xc6\xfd\x91\xc7\xd5\xdc+S\x15' 'n\xd9P\x99n\x1d\xb2\xdd\x15\xeaW\xc3\x13k2\xf6\x02\xd0\x0f\x85\xec' '\x9e\x00\x00\x00~0|\x06\t*\x86H\x86\xf7\r\x01\x07\x06\xa0o0m\x02\x01' '\x000h\x06\t*\x86H\x86\xf7\r\x01\x07\x010\x1e\x06\t`\x86H\x01e\x03' '\x04\x01.0\x11\x04\x0c\xc9rP\xa1\x08t6{\xf2\xfd\xf1\xb3\x02\x01\x10' '\x80;D\xa4\xed`qP~c\x0f\xa0d\xd5\xa2Kj\xc7\xb2\xc6\x1e\xec\xfb\x0fK' '\xb2*\xd5\t2\x81pR\xee\xd1\x1a\xde<"\x1b\x98\x88\x8b\xf4&\xdaB\x95I' '\xd2\xff\x10\x13\xfc\x1aX\x08,/\x8b\x8b\x02\x00\x00\x00\x00\x0c\x00' '\x00\x00 ' ), 'header_auth_base': EncryptedData( iv=b's\x15<P\xaa\x94\xb8\x931P\xeb\xa0', ciphertext=b'', tag=b'\x91\xc5\xf7<\x7f\xc9\xb1k\x0e\xe2{\xe4\x97\x9d\xdbU' ), 'serialized_header_auth': b's\x15<P\xaa\x94\xb8\x931P\xeb\xa0\x91\xc5\xf7<\x7f\xc9\xb1k\x0e\xe2{\xe4\x97\x9d\xdbU', 'non_framed_aac': six.b( '_\xfd\xb3%\xa5}yd\x80}\xe2\x90\xf9\x0e&\x8fAWSKMSEncryptionClient' ' Single Block\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00 ' ), 'frame_aac': six.b( '_\xfd\xb3%\xa5}yd\x80}\xe2\x90\xf9\x0e&\x8fAWSKMSEncryptionClient' ' Frame\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00 ' ), 'final_frame_aac': six.b( '_\xfd\xb3%\xa5}yd\x80}\xe2\x90\xf9\x0e&\x8fAWSKMSEncryptionClient' ' Final Frame\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00 ' ),
def setUp(self): # Set up mock algorithm for tests self.mock_algorithm = MagicMock() self.mock_encryption_algorithm = MagicMock() self.mock_encryption_algorithm.return_value = sentinel.encryption_algorithm self.mock_algorithm.encryption_algorithm = self.mock_encryption_algorithm self.mock_encryption_mode = MagicMock() self.mock_encryption_mode.return_value = sentinel.encryption_mode self.mock_algorithm.encryption_mode = self.mock_encryption_mode self.mock_algorithm.iv_len = sentinel.iv_len self.mock_algorithm.data_key_len = sentinel.data_key_len self.mock_algorithm.algorithm_id = sentinel.algorithm_id self.mock_kdf_hash_type = MagicMock() self.mock_kdf_hash_type.return_value = sentinel.hash_instance self.mock_algorithm.kdf_hash_type = self.mock_kdf_hash_type self.mock_signing_algorithm_info = MagicMock() self.mock_signing_algorithm_info.return_value = sentinel.curve_instance self.mock_algorithm.signing_algorithm_info = self.mock_signing_algorithm_info self.mock_kdf_type_instance = MagicMock() self.mock_kdf_type_instance.derive.return_value = sentinel.derived_key self.mock_kdf_type = MagicMock() self.mock_kdf_type.return_value = self.mock_kdf_type_instance self.mock_algorithm.kdf_type = self.mock_kdf_type self.mock_algorithm.kdf_input_len = sentinel.kdf_input_len # Set up mock wrapping algorithm for tests self.mock_wrapping_algorithm = MagicMock() self.mock_wrapping_algorithm.padding = sentinel.padding self.mock_wrapping_algorithm.algorithm = sentinel.algorithm self.mock_wrapping_key = MagicMock() self.mock_wrapping_rsa_private_key = MagicMock() self.mock_wrapping_rsa_public_key = MagicMock() self.mock_wrapping_rsa_private_key.public_key.return_value = self.mock_wrapping_rsa_public_key self.mock_encrypted_data = EncryptedData( iv=VALUES['iv'], ciphertext=VALUES['ciphertext'], tag=VALUES['tag']) # Set up os.urandom patch self.mock_urandom_patcher = patch( 'aws_encryption_sdk.internal.crypto.os.urandom') self.mock_urandom = self.mock_urandom_patcher.start() self.mock_urandom.return_value = VALUES['random'] # Set up cryptography backend patch self.mock_cryptography_backend_patcher = patch( 'aws_encryption_sdk.internal.crypto.default_backend') self.mock_cryptography_backend = self.mock_cryptography_backend_patcher.start( ) self.mock_cryptography_backend.return_value = sentinel.crypto_backend # Set up cryptography Cipher patch self.mock_cryptography_cipher_patcher = patch( 'aws_encryption_sdk.internal.crypto.Cipher') self.mock_cryptography_cipher = self.mock_cryptography_cipher_patcher.start( ) self.mock_cryptography_cipher_instance = MagicMock() self.mock_cryptography_cipher.return_value = self.mock_cryptography_cipher_instance self.mock_encryptor = MagicMock() self.mock_encryptor.update.return_value = VALUES['encryptor']['update'] self.mock_encryptor.finalize.return_value = VALUES['encryptor'][ 'finalize'] self.mock_encryptor.tag = VALUES['tag'] self.mock_cryptography_cipher_instance.encryptor.return_value = self.mock_encryptor self.mock_decryptor = MagicMock() self.mock_decryptor.update.return_value = VALUES['decryptor']['update'] self.mock_decryptor.finalize.return_value = VALUES['decryptor'][ 'finalize'] self.mock_cryptography_cipher_instance.decryptor.return_value = self.mock_decryptor # Set up mock ec patch self.mock_cryptography_ec_patcher = patch( 'aws_encryption_sdk.internal.crypto.ec') self.mock_cryptography_ec = self.mock_cryptography_ec_patcher.start() self.mock_cryptography_ec.ECDSA.return_value = sentinel.ecdsa_instance self.mock_signer_private_key = MagicMock() self.mock_signer_private_key.signer.return_value = sentinel.signer_instance self.mock_cryptography_ec.generate_private_key.return_value = self.mock_signer_private_key self.mock_signer = MagicMock() self.mock_signer.finalize.return_value = sentinel.signature self.mock_verifier_public_key = MagicMock() self.mock_verifier_instance = MagicMock() self.mock_verifier_public_key.verifier.return_value = self.mock_verifier_instance # Set up mock load cryptography serialization patch self.mock_crypto_serialization_patcher = patch( 'aws_encryption_sdk.internal.crypto.serialization') self.mock_crypto_serialization = self.mock_crypto_serialization_patcher.start( ) self.mock_crypto_serialization.load_pem_private_key.return_value = self.mock_wrapping_rsa_private_key self.mock_crypto_serialization.load_pem_public_key.return_value = self.mock_wrapping_rsa_public_key
def mock_encrypted_data(): return EncryptedData(iv=VALUES['iv'], ciphertext=VALUES['ciphertext'], tag=VALUES['tag'])
def test_encrypted_data_succeeds(iv, ciphertext, tag): EncryptedData(iv=iv, ciphertext=ciphertext, tag=tag)
def test_encrypted_data_fails(): with pytest.raises(TypeError): EncryptedData(ciphertext=None)