34
34
from dynamodb_encryption_sdk .encrypted .table import EncryptedTable
35
35
from dynamodb_encryption_sdk .identifiers import CryptoAction
36
36
from dynamodb_encryption_sdk .internal .identifiers import ReservedAttributes
37
+ from dynamodb_encryption_sdk .material_providers import CryptographicMaterialsProvider
37
38
from dynamodb_encryption_sdk .material_providers .most_recent import MostRecentProvider
38
39
from dynamodb_encryption_sdk .material_providers .static import StaticCryptographicMaterialsProvider
39
40
from dynamodb_encryption_sdk .material_providers .store .meta import MetaStore
40
41
from dynamodb_encryption_sdk .material_providers .wrapped import WrappedCryptographicMaterialsProvider
42
+ from dynamodb_encryption_sdk .materials import CryptographicMaterials
41
43
from dynamodb_encryption_sdk .materials .raw import RawDecryptionMaterials , RawEncryptionMaterials
42
- from dynamodb_encryption_sdk .structures import AttributeActions
44
+ from dynamodb_encryption_sdk .structures import AttributeActions , EncryptionContext
43
45
from dynamodb_encryption_sdk .transform import ddb_to_dict , dict_to_ddb
44
46
45
47
RUNNING_IN_TRAVIS = "TRAVIS" in os .environ
@@ -164,6 +166,38 @@ def table_with_global_secondary_indexes():
164
166
mock_dynamodb2 ().stop ()
165
167
166
168
169
+ class PassThroughCryptographicMaterialsProviderThatRequiresAttributes (CryptographicMaterialsProvider ):
170
+ """Cryptographic materials provider that passes through to another, but requires that attributes are set.
171
+
172
+ If the EncryptionContext passed to decryption_materials or encryption_materials
173
+ ever does not have attributes set,
174
+ a ValueError is raised.
175
+ Otherwise, it passes through to the passthrough CMP normally.
176
+ """
177
+
178
+ def __init__ (self , passthrough_cmp ):
179
+ self ._passthrough_cmp = passthrough_cmp
180
+
181
+ def _assert_attributes_set (self , encryption_context ):
182
+ # type: (EncryptionContext) -> None
183
+ if not encryption_context .attributes :
184
+ raise ValueError ("Encryption context attributes MUST be set!" )
185
+
186
+ def decryption_materials (self , encryption_context ):
187
+ # type: (EncryptionContext) -> CryptographicMaterials
188
+ self ._assert_attributes_set (encryption_context )
189
+ return self ._passthrough_cmp .decryption_materials (encryption_context )
190
+
191
+ def encryption_materials (self , encryption_context ):
192
+ # type: (EncryptionContext) -> CryptographicMaterials
193
+ self ._assert_attributes_set (encryption_context )
194
+ return self ._passthrough_cmp .encryption_materials (encryption_context )
195
+
196
+ def refresh (self ):
197
+ # type: () -> None
198
+ self ._passthrough_cmp .refresh ()
199
+
200
+
167
201
def _get_from_cache (dk_class , algorithm , key_length ):
168
202
"""Don't generate new keys every time. All we care about is that they are valid keys, not that they are unique."""
169
203
try :
@@ -221,8 +255,15 @@ def _some_algorithm_pairs():
221
255
_cmp_builders = {"static" : build_static_jce_cmp , "wrapped" : _build_wrapped_jce_cmp }
222
256
223
257
224
- def _all_possible_cmps (algorithm_generator ):
225
- """Generate all possible cryptographic materials providers based on the supplied generator."""
258
+ def _all_possible_cmps (algorithm_generator , require_attributes ):
259
+ """Generate all possible cryptographic materials providers based on the supplied generator.
260
+
261
+ require_attributes determines whether the CMP will be wrapped in
262
+ PassThroughCryptographicMaterialsProviderThatRequiresAttributes
263
+ to require that attributes are set on every request.
264
+ This should ONLY be disabled on the item encryptor tests.
265
+ All high-level helper clients MUST set the attributes before passing the encryption context down.
266
+ """
226
267
# The AES combinations do the same thing, but this makes sure that the AESWrap name works as expected.
227
268
yield _build_wrapped_jce_cmp ("AESWrap" , 256 , "HmacSHA256" , 256 )
228
269
@@ -242,17 +283,31 @@ def _all_possible_cmps(algorithm_generator):
242
283
sig_key_length = signing_key_length ,
243
284
)
244
285
286
+ inner_cmp = builder_func (encryption_algorithm , encryption_key_length , signing_algorithm , signing_key_length )
287
+
288
+ if require_attributes :
289
+ outer_cmp = PassThroughCryptographicMaterialsProviderThatRequiresAttributes (inner_cmp )
290
+ else :
291
+ outer_cmp = inner_cmp
292
+
245
293
yield pytest .param (
246
- builder_func ( encryption_algorithm , encryption_key_length , signing_algorithm , signing_key_length ) ,
294
+ outer_cmp ,
247
295
id = id_string ,
248
296
)
249
297
250
298
251
- def set_parametrized_cmp (metafunc ):
252
- """Set paramatrized values for cryptographic materials providers."""
299
+ def set_parametrized_cmp (metafunc , require_attributes = True ):
300
+ """Set paramatrized values for cryptographic materials providers.
301
+
302
+ require_attributes determines whether the CMP will be wrapped in
303
+ PassThroughCryptographicMaterialsProviderThatRequiresAttributes
304
+ to require that attributes are set on every request.
305
+ This should ONLY be disabled on the item encryptor tests.
306
+ All high-level helper clients MUST set the attributes before passing the encryption context down.
307
+ """
253
308
for name , algorithm_generator in (("all_the_cmps" , _all_algorithm_pairs ), ("some_cmps" , _some_algorithm_pairs )):
254
309
if name in metafunc .fixturenames :
255
- metafunc .parametrize (name , _all_possible_cmps (algorithm_generator ))
310
+ metafunc .parametrize (name , _all_possible_cmps (algorithm_generator , require_attributes ))
256
311
257
312
258
313
_ACTIONS = {
0 commit comments