Skip to content

Commit b989e00

Browse files
[Storage] Changes to decryption codepath to allow v2.1 (#37455)
1 parent 200f57e commit b989e00

File tree

2 files changed

+38
-25
lines changed

2 files changed

+38
-25
lines changed

sdk/storage/azure-storage-blob/azure/storage/blob/_encryption.py

+13-10
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@
4040

4141
_ENCRYPTION_PROTOCOL_V1 = '1.0'
4242
_ENCRYPTION_PROTOCOL_V2 = '2.0'
43+
_ENCRYPTION_PROTOCOL_V2_1 = '2.1'
44+
_VALID_ENCRYPTION_PROTOCOLS = [_ENCRYPTION_PROTOCOL_V1, _ENCRYPTION_PROTOCOL_V2, _ENCRYPTION_PROTOCOL_V2_1]
45+
_ENCRYPTION_V2_PROTOCOLS = [_ENCRYPTION_PROTOCOL_V2, _ENCRYPTION_PROTOCOL_V2_1]
4346
_GCM_REGION_DATA_LENGTH = 4 * 1024 * 1024
4447
_GCM_NONCE_LENGTH = 12
4548
_GCM_TAG_LENGTH = 16
@@ -293,14 +296,14 @@ def encrypt_data_v2(data: bytes, nonce: int, key: bytes) -> bytes:
293296

294297
def is_encryption_v2(encryption_data: Optional[_EncryptionData]) -> bool:
295298
"""
296-
Determine whether the given encryption data signifies version 2.0.
299+
Determine whether the given encryption data signifies version 2.0 or 2.1.
297300
298301
:param Optional[_EncryptionData] encryption_data: The encryption data. Will return False if this is None.
299302
:return: True, if the encryption data indicates encryption V2, false otherwise.
300303
:rtype: bool
301304
"""
302305
# If encryption_data is None, assume no encryption
303-
return bool(encryption_data and (encryption_data.encryption_agent.protocol == _ENCRYPTION_PROTOCOL_V2))
306+
return bool(encryption_data and (encryption_data.encryption_agent.protocol in _ENCRYPTION_V2_PROTOCOLS))
304307

305308

306309
def modify_user_agent_for_encryption(
@@ -405,7 +408,7 @@ def get_adjusted_download_range_and_offset(
405408
end_offset = 15 - (end % 16)
406409
end += end_offset
407410

408-
elif encryption_data.encryption_agent.protocol == _ENCRYPTION_PROTOCOL_V2:
411+
elif encryption_data.encryption_agent.protocol in _ENCRYPTION_V2_PROTOCOLS:
409412
start_offset, end_offset = 0, end
410413

411414
if encryption_data.encrypted_region_info is None:
@@ -550,7 +553,7 @@ def _dict_to_encryption_data(encryption_data_dict: Dict[str, Any]) -> _Encryptio
550553
"""
551554
try:
552555
protocol = encryption_data_dict['EncryptionAgent']['Protocol']
553-
if protocol not in [_ENCRYPTION_PROTOCOL_V1, _ENCRYPTION_PROTOCOL_V2]:
556+
if protocol not in _VALID_ENCRYPTION_PROTOCOLS:
554557
raise ValueError("Unsupported encryption version.")
555558
except KeyError as exc:
556559
raise ValueError("Unsupported encryption version.") from exc
@@ -636,7 +639,7 @@ def _validate_and_unwrap_cek(
636639
# Validate we have the right info for the specified version
637640
if encryption_data.encryption_agent.protocol == _ENCRYPTION_PROTOCOL_V1:
638641
_validate_not_none('content_encryption_IV', encryption_data.content_encryption_IV)
639-
elif encryption_data.encryption_agent.protocol == _ENCRYPTION_PROTOCOL_V2:
642+
elif encryption_data.encryption_agent.protocol in _ENCRYPTION_V2_PROTOCOLS:
640643
_validate_not_none('encrypted_region_info', encryption_data.encrypted_region_info)
641644
else:
642645
raise ValueError('Specified encryption version is not supported.')
@@ -662,8 +665,8 @@ def _validate_and_unwrap_cek(
662665

663666
# For V2, the version is included with the cek. We need to validate it
664667
# and remove it from the actual cek.
665-
if encryption_data.encryption_agent.protocol == _ENCRYPTION_PROTOCOL_V2:
666-
version_2_bytes = _ENCRYPTION_PROTOCOL_V2.encode().ljust(8, b'\0')
668+
if encryption_data.encryption_agent.protocol in _ENCRYPTION_V2_PROTOCOLS:
669+
version_2_bytes = encryption_data.encryption_agent.protocol.encode().ljust(8, b'\0')
667670
cek_version_bytes = content_encryption_key[:len(version_2_bytes)]
668671
if cek_version_bytes != version_2_bytes:
669672
raise ValueError('The encryption metadata is not valid and may have been modified.')
@@ -722,7 +725,7 @@ def _decrypt_message(
722725
unpadder = PKCS7(128).unpadder()
723726
decrypted_data = (unpadder.update(decrypted_data) + unpadder.finalize())
724727

725-
elif encryption_data.encryption_agent.protocol == _ENCRYPTION_PROTOCOL_V2:
728+
elif encryption_data.encryption_agent.protocol in _ENCRYPTION_V2_PROTOCOLS:
726729
block_info = encryption_data.encrypted_region_info
727730
if not block_info or not block_info.nonce_length:
728731
raise ValueError("Missing required metadata for decryption.")
@@ -894,7 +897,7 @@ def decrypt_blob( # pylint: disable=too-many-locals,too-many-statements
894897
raise ValueError('Specified encryption algorithm is not supported.')
895898

896899
version = encryption_data.encryption_agent.protocol
897-
if version not in (_ENCRYPTION_PROTOCOL_V1, _ENCRYPTION_PROTOCOL_V2):
900+
if version not in _VALID_ENCRYPTION_PROTOCOLS:
898901
raise ValueError('Specified encryption version is not supported.')
899902

900903
content_encryption_key = _validate_and_unwrap_cek(encryption_data, key_encryption_key, key_resolver)
@@ -945,7 +948,7 @@ def decrypt_blob( # pylint: disable=too-many-locals,too-many-statements
945948

946949
return content[start_offset: len(content) - end_offset]
947950

948-
if version == _ENCRYPTION_PROTOCOL_V2:
951+
if version in _ENCRYPTION_V2_PROTOCOLS:
949952
# We assume the content contains only full encryption regions
950953
total_size = len(content)
951954
offset = 0

sdk/storage/azure-storage-queue/azure/storage/queue/_encryption.py

+25-15
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,20 @@
4040

4141
_ENCRYPTION_PROTOCOL_V1 = '1.0'
4242
_ENCRYPTION_PROTOCOL_V2 = '2.0'
43+
_ENCRYPTION_PROTOCOL_V2_1 = '2.1'
44+
_VALID_ENCRYPTION_PROTOCOLS = [_ENCRYPTION_PROTOCOL_V1, _ENCRYPTION_PROTOCOL_V2, _ENCRYPTION_PROTOCOL_V2_1]
45+
_ENCRYPTION_V2_PROTOCOLS = [_ENCRYPTION_PROTOCOL_V2, _ENCRYPTION_PROTOCOL_V2_1]
4346
_GCM_REGION_DATA_LENGTH = 4 * 1024 * 1024
4447
_GCM_NONCE_LENGTH = 12
4548
_GCM_TAG_LENGTH = 16
4649

4750
_ERROR_OBJECT_INVALID = \
4851
'{0} does not define a complete interface. Value of {1} is either missing or invalid.'
4952

53+
_ERROR_UNSUPPORTED_METHOD_FOR_ENCRYPTION = (
54+
'The require_encryption flag is set, but encryption is not supported'
55+
' for this method.')
56+
5057

5158
class KeyEncryptionKey(Protocol):
5259

@@ -289,14 +296,14 @@ def encrypt_data_v2(data: bytes, nonce: int, key: bytes) -> bytes:
289296

290297
def is_encryption_v2(encryption_data: Optional[_EncryptionData]) -> bool:
291298
"""
292-
Determine whether the given encryption data signifies version 2.0.
299+
Determine whether the given encryption data signifies version 2.0 or 2.1.
293300
294301
:param Optional[_EncryptionData] encryption_data: The encryption data. Will return False if this is None.
295302
:return: True, if the encryption data indicates encryption V2, false otherwise.
296303
:rtype: bool
297304
"""
298305
# If encryption_data is None, assume no encryption
299-
return bool(encryption_data and (encryption_data.encryption_agent.protocol == _ENCRYPTION_PROTOCOL_V2))
306+
return bool(encryption_data and (encryption_data.encryption_agent.protocol in _ENCRYPTION_V2_PROTOCOLS))
300307

301308

302309
def modify_user_agent_for_encryption(
@@ -357,7 +364,7 @@ def get_adjusted_upload_size(length: int, encryption_version: str) -> int:
357364
def get_adjusted_download_range_and_offset(
358365
start: int,
359366
end: int,
360-
length: int,
367+
length: Optional[int],
361368
encryption_data: Optional[_EncryptionData]) -> Tuple[Tuple[int, int], Tuple[int, int]]:
362369
"""
363370
Gets the new download range and offsets into the decrypted data for
@@ -374,7 +381,7 @@ def get_adjusted_download_range_and_offset(
374381
375382
:param int start: The user-requested start index.
376383
:param int end: The user-requested end index.
377-
:param int length: The user-requested length. Only used for V1.
384+
:param Optional[int] length: The user-requested length. Only used for V1.
378385
:param Optional[_EncryptionData] encryption_data: The encryption data to determine version and sizes.
379386
:return: (new start, new end), (start offset, end offset)
380387
:rtype: Tuple[Tuple[int, int], Tuple[int, int]]
@@ -401,7 +408,7 @@ def get_adjusted_download_range_and_offset(
401408
end_offset = 15 - (end % 16)
402409
end += end_offset
403410

404-
elif encryption_data.encryption_agent.protocol == _ENCRYPTION_PROTOCOL_V2:
411+
elif encryption_data.encryption_agent.protocol in _ENCRYPTION_V2_PROTOCOLS:
405412
start_offset, end_offset = 0, end
406413

407414
if encryption_data.encrypted_region_info is None:
@@ -451,17 +458,20 @@ def parse_encryption_data(metadata: Dict[str, Any]) -> Optional[_EncryptionData]
451458
return None
452459

453460

454-
def adjust_blob_size_for_encryption(size: int, encryption_data: _EncryptionData) -> int:
461+
def adjust_blob_size_for_encryption(size: int, encryption_data: Optional[_EncryptionData]) -> int:
455462
"""
456463
Adjusts the given blob size for encryption by subtracting the size of
457464
the encryption data (nonce + tag). This only has an affect for encryption V2.
458465
459466
:param int size: The original blob size.
460-
:param _EncryptionData encryption_data: The encryption data to determine version and sizes.
467+
:param Optional[_EncryptionData] encryption_data: The encryption data to determine version and sizes.
461468
:return: The new blob size.
462469
:rtype: int
463470
"""
464-
if is_encryption_v2(encryption_data) and encryption_data.encrypted_region_info is not None:
471+
if (encryption_data is not None and
472+
encryption_data.encrypted_region_info is not None and
473+
is_encryption_v2(encryption_data)):
474+
465475
nonce_length = encryption_data.encrypted_region_info.nonce_length
466476
data_length = encryption_data.encrypted_region_info.data_length
467477
tag_length = encryption_data.encrypted_region_info.tag_length
@@ -543,7 +553,7 @@ def _dict_to_encryption_data(encryption_data_dict: Dict[str, Any]) -> _Encryptio
543553
"""
544554
try:
545555
protocol = encryption_data_dict['EncryptionAgent']['Protocol']
546-
if protocol not in [_ENCRYPTION_PROTOCOL_V1, _ENCRYPTION_PROTOCOL_V2]:
556+
if protocol not in _VALID_ENCRYPTION_PROTOCOLS:
547557
raise ValueError("Unsupported encryption version.")
548558
except KeyError as exc:
549559
raise ValueError("Unsupported encryption version.") from exc
@@ -629,7 +639,7 @@ def _validate_and_unwrap_cek(
629639
# Validate we have the right info for the specified version
630640
if encryption_data.encryption_agent.protocol == _ENCRYPTION_PROTOCOL_V1:
631641
_validate_not_none('content_encryption_IV', encryption_data.content_encryption_IV)
632-
elif encryption_data.encryption_agent.protocol == _ENCRYPTION_PROTOCOL_V2:
642+
elif encryption_data.encryption_agent.protocol in _ENCRYPTION_V2_PROTOCOLS:
633643
_validate_not_none('encrypted_region_info', encryption_data.encrypted_region_info)
634644
else:
635645
raise ValueError('Specified encryption version is not supported.')
@@ -655,8 +665,8 @@ def _validate_and_unwrap_cek(
655665

656666
# For V2, the version is included with the cek. We need to validate it
657667
# and remove it from the actual cek.
658-
if encryption_data.encryption_agent.protocol == _ENCRYPTION_PROTOCOL_V2:
659-
version_2_bytes = _ENCRYPTION_PROTOCOL_V2.encode().ljust(8, b'\0')
668+
if encryption_data.encryption_agent.protocol in _ENCRYPTION_V2_PROTOCOLS:
669+
version_2_bytes = encryption_data.encryption_agent.protocol.encode().ljust(8, b'\0')
660670
cek_version_bytes = content_encryption_key[:len(version_2_bytes)]
661671
if cek_version_bytes != version_2_bytes:
662672
raise ValueError('The encryption metadata is not valid and may have been modified.')
@@ -715,7 +725,7 @@ def _decrypt_message(
715725
unpadder = PKCS7(128).unpadder()
716726
decrypted_data = (unpadder.update(decrypted_data) + unpadder.finalize())
717727

718-
elif encryption_data.encryption_agent.protocol == _ENCRYPTION_PROTOCOL_V2:
728+
elif encryption_data.encryption_agent.protocol in _ENCRYPTION_V2_PROTOCOLS:
719729
block_info = encryption_data.encrypted_region_info
720730
if not block_info or not block_info.nonce_length:
721731
raise ValueError("Missing required metadata for decryption.")
@@ -887,7 +897,7 @@ def decrypt_blob( # pylint: disable=too-many-locals,too-many-statements
887897
raise ValueError('Specified encryption algorithm is not supported.')
888898

889899
version = encryption_data.encryption_agent.protocol
890-
if version not in (_ENCRYPTION_PROTOCOL_V1, _ENCRYPTION_PROTOCOL_V2):
900+
if version not in _VALID_ENCRYPTION_PROTOCOLS:
891901
raise ValueError('Specified encryption version is not supported.')
892902

893903
content_encryption_key = _validate_and_unwrap_cek(encryption_data, key_encryption_key, key_resolver)
@@ -938,7 +948,7 @@ def decrypt_blob( # pylint: disable=too-many-locals,too-many-statements
938948

939949
return content[start_offset: len(content) - end_offset]
940950

941-
if version == _ENCRYPTION_PROTOCOL_V2:
951+
if version in _ENCRYPTION_V2_PROTOCOLS:
942952
# We assume the content contains only full encryption regions
943953
total_size = len(content)
944954
offset = 0

0 commit comments

Comments
 (0)