azure-storage-blob 12.19.1__py3-none-any.whl → 12.20.0b1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. azure/storage/blob/__init__.py +7 -5
  2. azure/storage/blob/_blob_client.py +12 -4
  3. azure/storage/blob/_blob_service_client.py +4 -3
  4. azure/storage/blob/_container_client.py +28 -12
  5. azure/storage/blob/_download.py +3 -3
  6. azure/storage/blob/_encryption.py +254 -165
  7. azure/storage/blob/_generated/_azure_blob_storage.py +21 -3
  8. azure/storage/blob/_generated/_configuration.py +4 -11
  9. azure/storage/blob/_generated/_serialization.py +41 -49
  10. azure/storage/blob/_generated/aio/_azure_blob_storage.py +23 -3
  11. azure/storage/blob/_generated/aio/_configuration.py +4 -11
  12. azure/storage/blob/_generated/aio/operations/_append_blob_operations.py +24 -58
  13. azure/storage/blob/_generated/aio/operations/_blob_operations.py +123 -306
  14. azure/storage/blob/_generated/aio/operations/_block_blob_operations.py +37 -86
  15. azure/storage/blob/_generated/aio/operations/_container_operations.py +98 -289
  16. azure/storage/blob/_generated/aio/operations/_page_blob_operations.py +51 -150
  17. azure/storage/blob/_generated/aio/operations/_service_operations.py +49 -125
  18. azure/storage/blob/_generated/models/_models_py3.py +31 -31
  19. azure/storage/blob/_generated/operations/_append_blob_operations.py +25 -59
  20. azure/storage/blob/_generated/operations/_blob_operations.py +123 -306
  21. azure/storage/blob/_generated/operations/_block_blob_operations.py +39 -88
  22. azure/storage/blob/_generated/operations/_container_operations.py +100 -291
  23. azure/storage/blob/_generated/operations/_page_blob_operations.py +52 -151
  24. azure/storage/blob/_generated/operations/_service_operations.py +50 -126
  25. azure/storage/blob/_models.py +3 -4
  26. azure/storage/blob/_serialize.py +1 -0
  27. azure/storage/blob/_shared/authentication.py +1 -1
  28. azure/storage/blob/_shared/avro/avro_io.py +0 -6
  29. azure/storage/blob/_shared/avro/avro_io_async.py +0 -6
  30. azure/storage/blob/_shared/avro/datafile.py +0 -4
  31. azure/storage/blob/_shared/avro/datafile_async.py +0 -4
  32. azure/storage/blob/_shared/avro/schema.py +4 -4
  33. azure/storage/blob/_shared/base_client.py +72 -87
  34. azure/storage/blob/_shared/base_client_async.py +115 -27
  35. azure/storage/blob/_shared/models.py +112 -20
  36. azure/storage/blob/_shared/parser.py +7 -6
  37. azure/storage/blob/_shared/policies.py +96 -66
  38. azure/storage/blob/_shared/policies_async.py +48 -21
  39. azure/storage/blob/_shared/response_handlers.py +14 -16
  40. azure/storage/blob/_shared/shared_access_signature.py +2 -3
  41. azure/storage/blob/_shared_access_signature.py +37 -27
  42. azure/storage/blob/_upload_helpers.py +4 -7
  43. azure/storage/blob/_version.py +1 -1
  44. azure/storage/blob/aio/__init__.py +2 -2
  45. azure/storage/blob/aio/_blob_client_async.py +16 -5
  46. azure/storage/blob/aio/_blob_service_client_async.py +3 -1
  47. azure/storage/blob/aio/_container_client_async.py +25 -8
  48. azure/storage/blob/aio/_download_async.py +9 -9
  49. azure/storage/blob/aio/_encryption_async.py +72 -0
  50. azure/storage/blob/aio/_upload_helpers.py +8 -10
  51. {azure_storage_blob-12.19.1.dist-info → azure_storage_blob-12.20.0b1.dist-info}/METADATA +9 -9
  52. azure_storage_blob-12.20.0b1.dist-info/RECORD +81 -0
  53. {azure_storage_blob-12.19.1.dist-info → azure_storage_blob-12.20.0b1.dist-info}/WHEEL +1 -1
  54. azure/storage/blob/_generated/py.typed +0 -1
  55. azure_storage_blob-12.19.1.dist-info/RECORD +0 -81
  56. {azure_storage_blob-12.19.1.dist-info → azure_storage_blob-12.20.0b1.dist-info}/LICENSE +0 -0
  57. {azure_storage_blob-12.19.1.dist-info → azure_storage_blob-12.20.0b1.dist-info}/top_level.txt +0 -0
@@ -5,8 +5,8 @@
5
5
  # license information.
6
6
  # --------------------------------------------------------------------------
7
7
 
8
- import os
9
8
  import math
9
+ import os
10
10
  import sys
11
11
  import warnings
12
12
  from collections import OrderedDict
@@ -15,7 +15,9 @@ from json import (
15
15
  dumps,
16
16
  loads,
17
17
  )
18
- from typing import Any, BinaryIO, Dict, Optional, Tuple
18
+ from typing import Any, Callable, Dict, IO, Optional, Tuple, TYPE_CHECKING
19
+ from typing import OrderedDict as TypedOrderedDict
20
+ from typing_extensions import Protocol
19
21
 
20
22
  from cryptography.hazmat.backends import default_backend
21
23
  from cryptography.hazmat.primitives.ciphers import Cipher
@@ -28,7 +30,12 @@ from azure.core.exceptions import HttpResponseError
28
30
  from azure.core.utils import CaseInsensitiveDict
29
31
 
30
32
  from ._version import VERSION
31
- from ._shared import encode_base64, decode_base64_to_bytes
33
+ from ._shared import decode_base64_to_bytes, encode_base64
34
+
35
+ if TYPE_CHECKING:
36
+ from azure.core.pipeline import PipelineResponse
37
+ from cryptography.hazmat.primitives.ciphers import AEADEncryptionContext
38
+ from cryptography.hazmat.primitives.padding import PaddingContext
32
39
 
33
40
 
34
41
  _ENCRYPTION_PROTOCOL_V1 = '1.0'
@@ -41,12 +48,27 @@ _ERROR_OBJECT_INVALID = \
41
48
  '{0} does not define a complete interface. Value of {1} is either missing or invalid.'
42
49
 
43
50
 
44
- def _validate_not_none(param_name, param):
51
+ class KeyEncryptionKey(Protocol):
52
+
53
+ def wrap_key(self, key: bytes) -> bytes:
54
+ ...
55
+
56
+ def unwrap_key(self, key: bytes, algorithm: str) -> bytes:
57
+ ...
58
+
59
+ def get_kid(self) -> str:
60
+ ...
61
+
62
+ def get_key_wrap_algorithm(self) -> str:
63
+ ...
64
+
65
+
66
+ def _validate_not_none(param_name: str, param: Any):
45
67
  if param is None:
46
68
  raise ValueError(f'{param_name} should not be None.')
47
69
 
48
70
 
49
- def _validate_key_encryption_key_wrap(kek):
71
+ def _validate_key_encryption_key_wrap(kek: KeyEncryptionKey):
50
72
  # Note that None is not callable and so will fail the second clause of each check.
51
73
  if not hasattr(kek, 'wrap_key') or not callable(kek.wrap_key):
52
74
  raise AttributeError(_ERROR_OBJECT_INVALID.format('key encryption key', 'wrap_key'))
@@ -57,7 +79,7 @@ def _validate_key_encryption_key_wrap(kek):
57
79
 
58
80
 
59
81
  class StorageEncryptionMixin(object):
60
- def _configure_encryption(self, kwargs):
82
+ def _configure_encryption(self, kwargs: Dict[str, Any]):
61
83
  self.require_encryption = kwargs.get("require_encryption", False)
62
84
  self.encryption_version = kwargs.get("encryption_version", "1.0")
63
85
  self.key_encryption_key = kwargs.get("key_encryption_key")
@@ -80,15 +102,17 @@ class _EncryptionAlgorithm(object):
80
102
  class _WrappedContentKey:
81
103
  """
82
104
  Represents the envelope key details stored on the service.
83
-
84
- :param str algorithm:
85
- The algorithm used for wrapping.
86
- :param bytes encrypted_key:
87
- The encrypted content-encryption-key.
88
- :param str key_id:
89
- The key-encryption-key identifier string.
90
105
  """
91
- def __init__(self, algorithm, encrypted_key, key_id):
106
+
107
+ def __init__(self, algorithm: str, encrypted_key: bytes, key_id: str) -> None:
108
+ """
109
+ :param str algorithm:
110
+ The algorithm used for wrapping.
111
+ :param bytes encrypted_key:
112
+ The encrypted content-encryption-key.
113
+ :param str key_id:
114
+ The key-encryption-key identifier string.
115
+ """
92
116
  _validate_not_none('algorithm', algorithm)
93
117
  _validate_not_none('encrypted_key', encrypted_key)
94
118
  _validate_not_none('key_id', key_id)
@@ -102,16 +126,17 @@ class _EncryptedRegionInfo:
102
126
  """
103
127
  Represents the length of encryption elements.
104
128
  This is only used for Encryption V2.
105
-
106
- :param int data_length:
107
- The length of the encryption region data (not including nonce + tag).
108
- :param str nonce_length:
109
- The length of nonce used when encrypting.
110
- :param int tag_length:
111
- The length of the encryption tag.
112
129
  """
113
130
 
114
- def __init__(self, data_length, nonce_length, tag_length):
131
+ def __init__(self, data_length: int, nonce_length: int, tag_length: int) -> None:
132
+ """
133
+ :param int data_length:
134
+ The length of the encryption region data (not including nonce + tag).
135
+ :param int nonce_length:
136
+ The length of nonce used when encrypting.
137
+ :param int tag_length:
138
+ The length of the encryption tag.
139
+ """
115
140
  _validate_not_none('data_length', data_length)
116
141
  _validate_not_none('nonce_length', nonce_length)
117
142
  _validate_not_none('tag_length', tag_length)
@@ -125,14 +150,15 @@ class _EncryptionAgent:
125
150
  """
126
151
  Represents the encryption agent stored on the service.
127
152
  It consists of the encryption protocol version and encryption algorithm used.
128
-
129
- :param _EncryptionAlgorithm encryption_algorithm:
130
- The algorithm used for encrypting the message contents.
131
- :param str protocol:
132
- The protocol version used for encryption.
133
153
  """
134
154
 
135
- def __init__(self, encryption_algorithm, protocol):
155
+ def __init__(self, encryption_algorithm: _EncryptionAlgorithm, protocol: str) -> None:
156
+ """
157
+ :param _EncryptionAlgorithm encryption_algorithm:
158
+ The algorithm used for encrypting the message contents.
159
+ :param str protocol:
160
+ The protocol version used for encryption.
161
+ """
136
162
  _validate_not_none('encryption_algorithm', encryption_algorithm)
137
163
  _validate_not_none('protocol', protocol)
138
164
 
@@ -143,30 +169,30 @@ class _EncryptionAgent:
143
169
  class _EncryptionData:
144
170
  """
145
171
  Represents the encryption data that is stored on the service.
146
-
147
- :param Optional[bytes] content_encryption_IV:
148
- The content encryption initialization vector.
149
- Required for AES-CBC (V1).
150
- :param Optional[_EncryptedRegionInfo] encrypted_region_info:
151
- The info about the authenticated block sizes.
152
- Required for AES-GCM (V2).
153
- :param _EncryptionAgent encryption_agent:
154
- The encryption agent.
155
- :param _WrappedContentKey wrapped_content_key:
156
- An object that stores the wrapping algorithm, the key identifier,
157
- and the encrypted key bytes.
158
- :param dict key_wrapping_metadata:
159
- A dict containing metadata related to the key wrapping.
160
172
  """
161
173
 
162
174
  def __init__(
163
- self,
164
- content_encryption_IV,
165
- encrypted_region_info,
166
- encryption_agent,
167
- wrapped_content_key,
168
- key_wrapping_metadata
169
- ):
175
+ self, content_encryption_IV: Optional[bytes],
176
+ encrypted_region_info: Optional[_EncryptedRegionInfo],
177
+ encryption_agent: _EncryptionAgent,
178
+ wrapped_content_key: _WrappedContentKey,
179
+ key_wrapping_metadata: Dict[str, Any]
180
+ ) -> None:
181
+ """
182
+ :param Optional[bytes] content_encryption_IV:
183
+ The content encryption initialization vector.
184
+ Required for AES-CBC (V1).
185
+ :param Optional[_EncryptedRegionInfo] encrypted_region_info:
186
+ The info about the autenticated block sizes.
187
+ Required for AES-GCM (V2).
188
+ :param _EncryptionAgent encryption_agent:
189
+ The encryption agent.
190
+ :param _WrappedContentKey wrapped_content_key:
191
+ An object that stores the wrapping algorithm, the key identifier,
192
+ and the encrypted key bytes.
193
+ :param Dict[str, Any] key_wrapping_metadata:
194
+ A dict containing metadata related to the key wrapping.
195
+ """
170
196
  _validate_not_none('encryption_agent', encryption_agent)
171
197
  _validate_not_none('wrapped_content_key', wrapped_content_key)
172
198
 
@@ -191,15 +217,15 @@ class GCMBlobEncryptionStream:
191
217
  it's streamed. Data is read and encrypted in regions. The stream
192
218
  will use the same encryption key and will generate a guaranteed unique
193
219
  nonce for each encryption region.
194
-
195
- :param bytes content_encryption_key: The encryption key to use.
196
- :param BinaryIO data_stream: The data stream to read data from.
197
220
  """
198
221
  def __init__(
199
- self,
200
- content_encryption_key: bytes,
201
- data_stream: BinaryIO,
202
- ):
222
+ self, content_encryption_key: bytes,
223
+ data_stream: IO[bytes],
224
+ ) -> None:
225
+ """
226
+ :param bytes content_encryption_key: The encryption key to use.
227
+ :param IO[bytes] data_stream: The data stream to read data from.
228
+ """
203
229
  self.content_encryption_key = content_encryption_key
204
230
  self.data_stream = data_stream
205
231
 
@@ -235,28 +261,30 @@ class GCMBlobEncryptionStream:
235
261
  # No more data to read
236
262
  break
237
263
 
238
- self.current = self._encrypt_region(data)
264
+ self.current = encrypt_data_v2(data, self.nonce_counter, self.content_encryption_key)
265
+ # IMPORTANT: Must increment the nonce each time.
266
+ self.nonce_counter += 1
239
267
 
240
268
  return result.getvalue()
241
269
 
242
- def _encrypt_region(self, data: bytes) -> bytes:
243
- """
244
- Encrypt the given region of data using AES-GCM. The result
245
- includes the data in the form: nonce + ciphertext + tag.
246
270
 
247
- :param bytes data: The data to encrypt.
248
- :return: The encrypted bytes.
249
- :rtype: bytes
250
- """
251
- # Each region MUST use a different nonce
252
- nonce = self.nonce_counter.to_bytes(_GCM_NONCE_LENGTH, 'big')
253
- self.nonce_counter += 1
271
+ def encrypt_data_v2(data: bytes, nonce: int, key: bytes) -> bytes:
272
+ """
273
+ Encrypts the given data using the given nonce and key using AES-GCM.
274
+ The result includes the data in the form: nonce + ciphertext + tag.
254
275
 
255
- aesgcm = AESGCM(self.content_encryption_key)
276
+ :param bytes data: The raw data to encrypt.
277
+ :param int nonce: The nonce to use for encryption.
278
+ :param bytes key: The encryption key to use for encryption.
279
+ :return: The encrypted bytes in the form: nonce + ciphertext + tag.
280
+ :rtype: bytes
281
+ """
282
+ nonce_bytes = nonce.to_bytes(_GCM_NONCE_LENGTH, 'big')
283
+ aesgcm = AESGCM(key)
256
284
 
257
- # Returns ciphertext + tag
258
- ciphertext_with_tag = aesgcm.encrypt(nonce, data, None)
259
- return nonce + ciphertext_with_tag
285
+ # Returns ciphertext + tag
286
+ ciphertext_with_tag = aesgcm.encrypt(nonce_bytes, data, None)
287
+ return nonce_bytes + ciphertext_with_tag
260
288
 
261
289
 
262
290
  def is_encryption_v2(encryption_data: Optional[_EncryptionData]) -> bool:
@@ -268,7 +296,7 @@ def is_encryption_v2(encryption_data: Optional[_EncryptionData]) -> bool:
268
296
  :rtype: bool
269
297
  """
270
298
  # If encryption_data is None, assume no encryption
271
- return encryption_data and encryption_data.encryption_agent.protocol == _ENCRYPTION_PROTOCOL_V2
299
+ return bool(encryption_data and (encryption_data.encryption_agent.protocol == _ENCRYPTION_PROTOCOL_V2))
272
300
 
273
301
 
274
302
  def modify_user_agent_for_encryption(
@@ -376,6 +404,9 @@ def get_adjusted_download_range_and_offset(
376
404
  elif encryption_data.encryption_agent.protocol == _ENCRYPTION_PROTOCOL_V2:
377
405
  start_offset, end_offset = 0, end
378
406
 
407
+ if encryption_data.encrypted_region_info is None:
408
+ raise ValueError("Missing required metadata for Encryption V2")
409
+
379
410
  nonce_length = encryption_data.encrypted_region_info.nonce_length
380
411
  data_length = encryption_data.encrypted_region_info.data_length
381
412
  tag_length = encryption_data.encrypted_region_info.tag_length
@@ -420,17 +451,17 @@ def parse_encryption_data(metadata: Dict[str, Any]) -> Optional[_EncryptionData]
420
451
  return None
421
452
 
422
453
 
423
- def adjust_blob_size_for_encryption(size: int, encryption_data: Optional[_EncryptionData]) -> int:
454
+ def adjust_blob_size_for_encryption(size: int, encryption_data: _EncryptionData) -> int:
424
455
  """
425
456
  Adjusts the given blob size for encryption by subtracting the size of
426
457
  the encryption data (nonce + tag). This only has an affect for encryption V2.
427
458
 
428
459
  :param int size: The original blob size.
429
- :param Optional[_EncryptionData] encryption_data: The encryption data to determine version and sizes.
460
+ :param _EncryptionData encryption_data: The encryption data to determine version and sizes.
430
461
  :return: The new blob size.
431
462
  :rtype: int
432
463
  """
433
- if is_encryption_v2(encryption_data):
464
+ if is_encryption_v2(encryption_data) and encryption_data.encrypted_region_info is not None:
434
465
  nonce_length = encryption_data.encrypted_region_info.nonce_length
435
466
  data_length = encryption_data.encrypted_region_info.data_length
436
467
  tag_length = encryption_data.encrypted_region_info.tag_length
@@ -443,17 +474,22 @@ def adjust_blob_size_for_encryption(size: int, encryption_data: Optional[_Encryp
443
474
  return size
444
475
 
445
476
 
446
- def _generate_encryption_data_dict(kek, cek, iv, version):
447
- '''
477
+ def _generate_encryption_data_dict(
478
+ kek: KeyEncryptionKey,
479
+ cek: bytes,
480
+ iv: Optional[bytes],
481
+ version: str
482
+ ) -> TypedOrderedDict[str, Any]:
483
+ """
448
484
  Generates and returns the encryption metadata as a dict.
449
485
 
450
- :param object kek: The key encryption key. See calling functions for more information.
486
+ :param KeyEncryptionKey kek: The key encryption key. See calling functions for more information.
451
487
  :param bytes cek: The content encryption key.
452
488
  :param Optional[bytes] iv: The initialization vector. Only required for AES-CBC.
453
489
  :param str version: The client encryption version used.
454
490
  :return: A dict containing all the encryption metadata.
455
- :rtype: dict
456
- '''
491
+ :rtype: Dict[str, Any]
492
+ """
457
493
  # Encrypt the cek.
458
494
  if version == _ENCRYPTION_PROTOCOL_V1:
459
495
  wrapped_cek = kek.wrap_key(cek)
@@ -483,20 +519,20 @@ def _generate_encryption_data_dict(kek, cek, iv, version):
483
519
  encrypted_region_info['DataLength'] = _GCM_REGION_DATA_LENGTH
484
520
  encrypted_region_info['NonceLength'] = _GCM_NONCE_LENGTH
485
521
 
486
- encryption_data_dict = OrderedDict()
522
+ encryption_data_dict: TypedOrderedDict[str, Any] = OrderedDict()
487
523
  encryption_data_dict['WrappedContentKey'] = wrapped_content_key
488
524
  encryption_data_dict['EncryptionAgent'] = encryption_agent
489
525
  if version == _ENCRYPTION_PROTOCOL_V1:
490
526
  encryption_data_dict['ContentEncryptionIV'] = encode_base64(iv)
491
527
  elif version == _ENCRYPTION_PROTOCOL_V2:
492
528
  encryption_data_dict['EncryptedRegionInfo'] = encrypted_region_info
493
- encryption_data_dict['KeyWrappingMetadata'] = {'EncryptionLibrary': 'Python ' + VERSION}
529
+ encryption_data_dict['KeyWrappingMetadata'] = OrderedDict({'EncryptionLibrary': 'Python ' + VERSION})
494
530
 
495
531
  return encryption_data_dict
496
532
 
497
533
 
498
- def _dict_to_encryption_data(encryption_data_dict):
499
- '''
534
+ def _dict_to_encryption_data(encryption_data_dict: Dict[str, Any]) -> _EncryptionData:
535
+ """
500
536
  Converts the specified dictionary to an EncryptionData object for
501
537
  eventual use in decryption.
502
538
 
@@ -504,7 +540,7 @@ def _dict_to_encryption_data(encryption_data_dict):
504
540
  The dictionary containing the encryption data.
505
541
  :return: an _EncryptionData object built from the dictionary.
506
542
  :rtype: _EncryptionData
507
- '''
543
+ """
508
544
  try:
509
545
  protocol = encryption_data_dict['EncryptionAgent']['Protocol']
510
546
  if protocol not in [_ENCRYPTION_PROTOCOL_V1, _ENCRYPTION_PROTOCOL_V2]:
@@ -547,15 +583,15 @@ def _dict_to_encryption_data(encryption_data_dict):
547
583
  return encryption_data
548
584
 
549
585
 
550
- def _generate_AES_CBC_cipher(cek, iv):
551
- '''
586
+ def _generate_AES_CBC_cipher(cek: bytes, iv: bytes) -> Cipher:
587
+ """
552
588
  Generates and returns an encryption cipher for AES CBC using the given cek and iv.
553
589
 
554
590
  :param bytes[] cek: The content encryption key for the cipher.
555
591
  :param bytes[] iv: The initialization vector for the cipher.
556
592
  :return: A cipher for encrypting in AES256 CBC.
557
593
  :rtype: ~cryptography.hazmat.primitives.ciphers.Cipher
558
- '''
594
+ """
559
595
 
560
596
  backend = default_backend()
561
597
  algorithm = AES(cek)
@@ -563,21 +599,30 @@ def _generate_AES_CBC_cipher(cek, iv):
563
599
  return Cipher(algorithm, mode, backend)
564
600
 
565
601
 
566
- def _validate_and_unwrap_cek(encryption_data, key_encryption_key=None, key_resolver=None):
567
- '''
602
+ def _validate_and_unwrap_cek(
603
+ encryption_data: _EncryptionData,
604
+ key_encryption_key: Optional[KeyEncryptionKey] = None,
605
+ key_resolver: Optional[Callable[[str], KeyEncryptionKey]] = None
606
+ ) -> bytes:
607
+ """
568
608
  Extracts and returns the content_encryption_key stored in the encryption_data object
569
609
  and performs necessary validation on all parameters.
570
610
  :param _EncryptionData encryption_data:
571
611
  The encryption metadata of the retrieved value.
572
- :param obj key_encryption_key:
573
- The key_encryption_key used to unwrap the cek. Please refer to high-level service object
574
- instance variables for more details.
575
- :param func key_resolver:
612
+ :param Optional[KeyEncryptionKey] key_encryption_key:
613
+ The user-provided key-encryption-key. Must implement the following methods:
614
+ wrap_key(key)
615
+ - Wraps the specified key using an algorithm of the user's choice.
616
+ get_key_wrap_algorithm()
617
+ - Returns the algorithm used to wrap the specified symmetric key.
618
+ get_kid()
619
+ - Returns a string key id for this key-encryption-key.
620
+ :param Optional[Callable[[str], KeyEncryptionKey]] key_resolver:
576
621
  A function used that, given a key_id, will return a key_encryption_key. Please refer
577
622
  to high-level service object instance variables for more details.
578
- :return: the content_encryption_key stored in the encryption_data object.
579
- :rtype: bytes[]
580
- '''
623
+ :return: The content_encryption_key stored in the encryption_data object.
624
+ :rtype: bytes
625
+ """
581
626
 
582
627
  _validate_not_none('encrypted_key', encryption_data.wrapped_content_key.encrypted_key)
583
628
 
@@ -589,13 +634,14 @@ def _validate_and_unwrap_cek(encryption_data, key_encryption_key=None, key_resol
589
634
  else:
590
635
  raise ValueError('Specified encryption version is not supported.')
591
636
 
592
- content_encryption_key = None
637
+ content_encryption_key: Optional[bytes] = None
593
638
 
594
639
  # If the resolver exists, give priority to the key it finds.
595
640
  if key_resolver is not None:
596
641
  key_encryption_key = key_resolver(encryption_data.wrapped_content_key.key_id)
597
642
 
598
- _validate_not_none('key_encryption_key', key_encryption_key)
643
+ if key_encryption_key is None:
644
+ raise ValueError("Unable to decrypt. key_resolver and key_encryption_key cannot both be None.")
599
645
  if not hasattr(key_encryption_key, 'get_kid') or not callable(key_encryption_key.get_kid):
600
646
  raise AttributeError(_ERROR_OBJECT_INVALID.format('key encryption key', 'get_kid'))
601
647
  if not hasattr(key_encryption_key, 'unwrap_key') or not callable(key_encryption_key.unwrap_key):
@@ -603,8 +649,9 @@ def _validate_and_unwrap_cek(encryption_data, key_encryption_key=None, key_resol
603
649
  if encryption_data.wrapped_content_key.key_id != key_encryption_key.get_kid():
604
650
  raise ValueError('Provided or resolved key-encryption-key does not match the id of key used to encrypt.')
605
651
  # Will throw an exception if the specified algorithm is not supported.
606
- content_encryption_key = key_encryption_key.unwrap_key(encryption_data.wrapped_content_key.encrypted_key,
607
- encryption_data.wrapped_content_key.algorithm)
652
+ content_encryption_key = key_encryption_key.unwrap_key(
653
+ encryption_data.wrapped_content_key.encrypted_key,
654
+ encryption_data.wrapped_content_key.algorithm)
608
655
 
609
656
  # For V2, the version is included with the cek. We need to validate it
610
657
  # and remove it from the actual cek.
@@ -622,27 +669,34 @@ def _validate_and_unwrap_cek(encryption_data, key_encryption_key=None, key_resol
622
669
  return content_encryption_key
623
670
 
624
671
 
625
- def _decrypt_message(message, encryption_data, key_encryption_key=None, resolver=None):
672
+ def _decrypt_message(
673
+ message: bytes,
674
+ encryption_data: _EncryptionData,
675
+ key_encryption_key: Optional[KeyEncryptionKey] = None,
676
+ resolver: Optional[Callable[[str], KeyEncryptionKey]] = None
677
+ ) -> bytes:
626
678
  """
627
679
  Decrypts the given ciphertext using AES256 in CBC mode with 128 bit padding.
628
680
  Unwraps the content-encryption-key using the user-provided or resolved key-encryption-key (kek).
629
- Returns the original plaintex.
681
+ Returns the original plaintext.
630
682
 
631
- :param str message:
683
+ :param bytes message:
632
684
  The ciphertext to be decrypted.
633
685
  :param _EncryptionData encryption_data:
634
686
  The metadata associated with this ciphertext.
635
- :param object key_encryption_key:
687
+ :param Optional[KeyEncryptionKey] key_encryption_key:
636
688
  The user-provided key-encryption-key. Must implement the following methods:
637
- unwrap_key(key, algorithm)
638
- - returns the unwrapped form of the specified symmetric key using the string-specified algorithm.
689
+ wrap_key(key)
690
+ - Wraps the specified key using an algorithm of the user's choice.
691
+ get_key_wrap_algorithm()
692
+ - Returns the algorithm used to wrap the specified symmetric key.
639
693
  get_kid()
640
- - returns a string key id for this key-encryption-key.
641
- :param Callable resolver:
694
+ - Returns a string key id for this key-encryption-key.
695
+ :param Optional[Callable[[str], KeyEncryptionKey]] resolver:
642
696
  The user-provided key resolver. Uses the kid string to return a key-encryption-key
643
697
  implementing the interface defined above.
644
698
  :return: The decrypted plaintext.
645
- :rtype: str
699
+ :rtype: bytes
646
700
  """
647
701
  _validate_not_none('message', message)
648
702
  content_encryption_key = _validate_and_unwrap_cek(encryption_data, key_encryption_key, resolver)
@@ -654,9 +708,8 @@ def _decrypt_message(message, encryption_data, key_encryption_key=None, resolver
654
708
  cipher = _generate_AES_CBC_cipher(content_encryption_key, encryption_data.content_encryption_IV)
655
709
 
656
710
  # decrypt data
657
- decrypted_data = message
658
711
  decryptor = cipher.decryptor()
659
- decrypted_data = (decryptor.update(decrypted_data) + decryptor.finalize())
712
+ decrypted_data = (decryptor.update(message) + decryptor.finalize())
660
713
 
661
714
  # unpad data
662
715
  unpadder = PKCS7(128).unpadder()
@@ -667,7 +720,10 @@ def _decrypt_message(message, encryption_data, key_encryption_key=None, resolver
667
720
  if not block_info or not block_info.nonce_length:
668
721
  raise ValueError("Missing required metadata for decryption.")
669
722
 
670
- nonce_length = encryption_data.encrypted_region_info.nonce_length
723
+ if encryption_data.encrypted_region_info is None:
724
+ raise ValueError("Missing required metadata for Encryption V2")
725
+
726
+ nonce_length = int(encryption_data.encrypted_region_info.nonce_length)
671
727
 
672
728
  # First bytes are the nonce
673
729
  nonce = message[:nonce_length]
@@ -682,8 +738,8 @@ def _decrypt_message(message, encryption_data, key_encryption_key=None, resolver
682
738
  return decrypted_data
683
739
 
684
740
 
685
- def encrypt_blob(blob, key_encryption_key, version):
686
- '''
741
+ def encrypt_blob(blob: bytes, key_encryption_key: KeyEncryptionKey, version: str) -> Tuple[str, bytes]:
742
+ """
687
743
  Encrypts the given blob using the given encryption protocol version.
688
744
  Wraps the generated content-encryption-key using the user-provided key-encryption-key (kek).
689
745
  Returns a json-formatted string containing the encryption metadata. This method should
@@ -692,15 +748,18 @@ def encrypt_blob(blob, key_encryption_key, version):
692
748
 
693
749
  :param bytes blob:
694
750
  The blob to be encrypted.
695
- :param object key_encryption_key:
751
+ :param KeyEncryptionKey key_encryption_key:
696
752
  The user-provided key-encryption-key. Must implement the following methods:
697
- wrap_key(key)--wraps the specified key using an algorithm of the user's choice.
698
- get_key_wrap_algorithm()--returns the algorithm used to wrap the specified symmetric key.
699
- get_kid()--returns a string key id for this key-encryption-key.
753
+ wrap_key(key)
754
+ - Wraps the specified key using an algorithm of the user's choice.
755
+ get_key_wrap_algorithm()
756
+ - Returns the algorithm used to wrap the specified symmetric key.
757
+ get_kid()
758
+ - Returns a string key id for this key-encryption-key.
700
759
  :param str version: The client encryption version to use.
701
760
  :return: A tuple of json-formatted string containing the encryption metadata and the encrypted blob data.
702
761
  :rtype: (str, bytes)
703
- '''
762
+ """
704
763
 
705
764
  _validate_not_none('blob', blob)
706
765
  _validate_not_none('key_encryption_key', key_encryption_key)
@@ -741,17 +800,21 @@ def encrypt_blob(blob, key_encryption_key, version):
741
800
  return dumps(encryption_data), encrypted_data
742
801
 
743
802
 
744
- def generate_blob_encryption_data(key_encryption_key, version):
745
- '''
803
+ def generate_blob_encryption_data(
804
+ key_encryption_key: Optional[KeyEncryptionKey],
805
+ version: str
806
+ ) -> Tuple[Optional[bytes], Optional[bytes], Optional[str]]:
807
+ """
746
808
  Generates the encryption_metadata for the blob.
747
809
 
748
- :param object key_encryption_key:
810
+ :param Optional[KeyEncryptionKey] key_encryption_key:
749
811
  The key-encryption-key used to wrap the cek associate with this blob.
750
812
  :param str version: The client encryption version to use.
751
813
  :return: A tuple containing the cek and iv for this blob as well as the
752
814
  serialized encryption metadata for the blob.
753
- :rtype: (bytes, Optional[bytes], str)
754
- '''
815
+ :rtype: (Optional[bytes], Optional[bytes], Optional[str])
816
+ """
817
+
755
818
  encryption_data = None
756
819
  content_encryption_key = None
757
820
  initialization_vector = None
@@ -761,37 +824,42 @@ def generate_blob_encryption_data(key_encryption_key, version):
761
824
  # Initialization vector only needed for V1
762
825
  if version == _ENCRYPTION_PROTOCOL_V1:
763
826
  initialization_vector = os.urandom(16)
764
- encryption_data = _generate_encryption_data_dict(key_encryption_key,
827
+ encryption_data_dict = _generate_encryption_data_dict(key_encryption_key,
765
828
  content_encryption_key,
766
829
  initialization_vector,
767
830
  version)
768
- encryption_data['EncryptionMode'] = 'FullBlob'
769
- encryption_data = dumps(encryption_data)
831
+ encryption_data_dict['EncryptionMode'] = 'FullBlob'
832
+ encryption_data = dumps(encryption_data_dict)
770
833
 
771
834
  return content_encryption_key, initialization_vector, encryption_data
772
835
 
773
836
 
774
837
  def decrypt_blob( # pylint: disable=too-many-locals,too-many-statements
775
- require_encryption,
776
- key_encryption_key,
777
- key_resolver,
778
- content,
779
- start_offset,
780
- end_offset,
781
- response_headers):
838
+ require_encryption: bool,
839
+ key_encryption_key: KeyEncryptionKey,
840
+ key_resolver: Optional[Callable[[str], KeyEncryptionKey]],
841
+ content: bytes,
842
+ start_offset: int,
843
+ end_offset: int,
844
+ response_headers: Dict[str, Any]
845
+ ) -> bytes:
782
846
  """
783
847
  Decrypts the given blob contents and returns only the requested range.
784
848
 
785
849
  :param bool require_encryption:
786
850
  Whether the calling blob service requires objects to be decrypted.
787
- :param object key_encryption_key:
851
+ :param KeyEncryptionKey key_encryption_key:
788
852
  The user-provided key-encryption-key. Must implement the following methods:
789
- wrap_key(key)--wraps the specified key using an algorithm of the user's choice.
790
- get_key_wrap_algorithm()--returns the algorithm used to wrap the specified symmetric key.
791
- get_kid()--returns a string key id for this key-encryption-key.
792
- :param object key_resolver:
853
+ wrap_key(key)
854
+ - Wraps the specified key using an algorithm of the user's choice.
855
+ get_key_wrap_algorithm()
856
+ - Returns the algorithm used to wrap the specified symmetric key.
857
+ get_kid()
858
+ - Returns a string key id for this key-encryption-key.
859
+ :param key_resolver:
793
860
  The user-provided key resolver. Uses the kid string to return a key-encryption-key
794
861
  implementing the interface defined above.
862
+ :type key_resolver: Optional[Callable[[str], KeyEncryptionKey]]
795
863
  :param bytes content:
796
864
  The encrypted blob content.
797
865
  :param int start_offset:
@@ -827,7 +895,7 @@ def decrypt_blob( # pylint: disable=too-many-locals,too-many-statements
827
895
  if version == _ENCRYPTION_PROTOCOL_V1:
828
896
  blob_type = response_headers['x-ms-blob-type']
829
897
 
830
- iv = None
898
+ iv: Optional[bytes] = None
831
899
  unpad = False
832
900
  if 'content-range' in response_headers:
833
901
  content_range = response_headers['content-range']
@@ -857,6 +925,9 @@ def decrypt_blob( # pylint: disable=too-many-locals,too-many-statements
857
925
  if blob_type == 'PageBlob':
858
926
  unpad = False
859
927
 
928
+ if iv is None:
929
+ raise ValueError("Missing required metadata for Encryption V1")
930
+
860
931
  cipher = _generate_AES_CBC_cipher(content_encryption_key, iv)
861
932
  decryptor = cipher.decryptor()
862
933
 
@@ -872,6 +943,9 @@ def decrypt_blob( # pylint: disable=too-many-locals,too-many-statements
872
943
  total_size = len(content)
873
944
  offset = 0
874
945
 
946
+ if encryption_data.encrypted_region_info is None:
947
+ raise ValueError("Missing required metadata for Encryption V2")
948
+
875
949
  nonce_length = encryption_data.encrypted_region_info.nonce_length
876
950
  data_length = encryption_data.encrypted_region_info.data_length
877
951
  tag_length = encryption_data.encrypted_region_info.tag_length
@@ -899,7 +973,11 @@ def decrypt_blob( # pylint: disable=too-many-locals,too-many-statements
899
973
  raise ValueError('Specified encryption version is not supported.')
900
974
 
901
975
 
902
- def get_blob_encryptor_and_padder(cek, iv, should_pad):
976
+ def get_blob_encryptor_and_padder(
977
+ cek: Optional[bytes],
978
+ iv: Optional[bytes],
979
+ should_pad: bool
980
+ ) -> Tuple[Optional["AEADEncryptionContext"], Optional["PaddingContext"]]:
903
981
  encryptor = None
904
982
  padder = None
905
983
 
@@ -911,23 +989,26 @@ def get_blob_encryptor_and_padder(cek, iv, should_pad):
911
989
  return encryptor, padder
912
990
 
913
991
 
914
- def encrypt_queue_message(message, key_encryption_key, version):
915
- '''
992
+ def encrypt_queue_message(message: str, key_encryption_key: KeyEncryptionKey, version: str) -> str:
993
+ """
916
994
  Encrypts the given plain text message using the given protocol version.
917
995
  Wraps the generated content-encryption-key using the user-provided key-encryption-key (kek).
918
996
  Returns a json-formatted string containing the encrypted message and the encryption metadata.
919
997
 
920
- :param object message:
998
+ :param str message:
921
999
  The plain text message to be encrypted.
922
- :param object key_encryption_key:
1000
+ :param KeyEncryptionKey key_encryption_key:
923
1001
  The user-provided key-encryption-key. Must implement the following methods:
924
- wrap_key(key)--wraps the specified key using an algorithm of the user's choice.
925
- get_key_wrap_algorithm()--returns the algorithm used to wrap the specified symmetric key.
926
- get_kid()--returns a string key id for this key-encryption-key.
1002
+ wrap_key(key)
1003
+ - Wraps the specified key using an algorithm of the user's choice.
1004
+ get_key_wrap_algorithm()
1005
+ - Returns the algorithm used to wrap the specified symmetric key.
1006
+ get_kid()
1007
+ - Returns a string key id for this key-encryption-key.
927
1008
  :param str version: The client encryption version to use.
928
1009
  :return: A json-formatted string containing the encrypted message and the encryption metadata.
929
1010
  :rtype: str
930
- '''
1011
+ """
931
1012
 
932
1013
  _validate_not_none('message', message)
933
1014
  _validate_not_none('key_encryption_key', key_encryption_key)
@@ -935,7 +1016,7 @@ def encrypt_queue_message(message, key_encryption_key, version):
935
1016
 
936
1017
  # Queue encoding functions all return unicode strings, and encryption should
937
1018
  # operate on binary strings.
938
- message = message.encode('utf-8')
1019
+ message_as_bytes: bytes = message.encode('utf-8')
939
1020
 
940
1021
  if version == _ENCRYPTION_PROTOCOL_V1:
941
1022
  # AES256 CBC uses 256 bit (32 byte) keys and always with 16 byte blocks
@@ -946,7 +1027,7 @@ def encrypt_queue_message(message, key_encryption_key, version):
946
1027
 
947
1028
  # PKCS7 with 16 byte blocks ensures compatibility with AES.
948
1029
  padder = PKCS7(128).padder()
949
- padded_data = padder.update(message) + padder.finalize()
1030
+ padded_data = padder.update(message_as_bytes) + padder.finalize()
950
1031
 
951
1032
  # Encrypt the data.
952
1033
  encryptor = cipher.encryptor()
@@ -962,8 +1043,8 @@ def encrypt_queue_message(message, key_encryption_key, version):
962
1043
  aesgcm = AESGCM(content_encryption_key)
963
1044
 
964
1045
  # Returns ciphertext + tag
965
- ciphertext_with_tag = aesgcm.encrypt(nonce, message, None)
966
- encrypted_data = nonce + ciphertext_with_tag
1046
+ cipertext_with_tag = aesgcm.encrypt(nonce, message_as_bytes, None)
1047
+ encrypted_data = nonce + cipertext_with_tag
967
1048
 
968
1049
  else:
969
1050
  raise ValueError("Invalid encryption version specified.")
@@ -978,7 +1059,13 @@ def encrypt_queue_message(message, key_encryption_key, version):
978
1059
  return dumps(queue_message)
979
1060
 
980
1061
 
981
- def decrypt_queue_message(message, response, require_encryption, key_encryption_key, resolver):
1062
+ def decrypt_queue_message(
1063
+ message: str,
1064
+ response: "PipelineResponse",
1065
+ require_encryption: bool,
1066
+ key_encryption_key: Optional[KeyEncryptionKey],
1067
+ resolver: Optional[Callable[[str], KeyEncryptionKey]]
1068
+ ) -> str:
982
1069
  """
983
1070
  Returns the decrypted message contents from an EncryptedQueueMessage.
984
1071
  If no encryption metadata is present, will return the unaltered message.
@@ -988,13 +1075,15 @@ def decrypt_queue_message(message, response, require_encryption, key_encryption_
988
1075
  The pipeline response used to generate an error with.
989
1076
  :param bool require_encryption:
990
1077
  If set, will enforce that the retrieved messages are encrypted and decrypt them.
991
- :param object key_encryption_key:
1078
+ :param Optional[KeyEncryptionKey] key_encryption_key:
992
1079
  The user-provided key-encryption-key. Must implement the following methods:
993
- unwrap_key(key, algorithm)
994
- - returns the unwrapped form of the specified symmetric key usingthe string-specified algorithm.
1080
+ wrap_key(key)
1081
+ - Wraps the specified key using an algorithm of the user's choice.
1082
+ get_key_wrap_algorithm()
1083
+ - Returns the algorithm used to wrap the specified symmetric key.
995
1084
  get_kid()
996
- - returns a string key id for this key-encryption-key.
997
- :param Callable resolver:
1085
+ - Returns a string key id for this key-encryption-key.
1086
+ :param Optional[Callable[[str], KeyEncryptionKey]] resolver:
998
1087
  The user-provided key resolver. Uses the kid string to return a key-encryption-key
999
1088
  implementing the interface defined above.
1000
1089
  :return: The plain text message from the queue message.
@@ -1003,10 +1092,10 @@ def decrypt_queue_message(message, response, require_encryption, key_encryption_
1003
1092
  response = response.http_response
1004
1093
 
1005
1094
  try:
1006
- message = loads(message)
1095
+ deserialized_message: Dict[str, Any] = loads(message)
1007
1096
 
1008
- encryption_data = _dict_to_encryption_data(message['EncryptionData'])
1009
- decoded_data = decode_base64_to_bytes(message['EncryptedMessageContents'])
1097
+ encryption_data = _dict_to_encryption_data(deserialized_message['EncryptionData'])
1098
+ decoded_data = decode_base64_to_bytes(deserialized_message['EncryptedMessageContents'])
1010
1099
  except (KeyError, ValueError) as exc:
1011
1100
  # Message was not json formatted and so was not encrypted
1012
1101
  # or the user provided a json formatted message
@@ -1022,5 +1111,5 @@ def decrypt_queue_message(message, response, require_encryption, key_encryption_
1022
1111
  except Exception as error:
1023
1112
  raise HttpResponseError(
1024
1113
  message="Decryption failed.",
1025
- response=response,
1114
+ response=response, #type: ignore [arg-type]
1026
1115
  error=error) from error