18
18
import six
19
19
from attr .validators import deep_iterable , deep_mapping , instance_of , optional
20
20
21
- from ..identifiers import Algorithm , KeyringTraceFlag
22
- from ..internal .utils .streams import ROStream
23
- from ..structures import DataKey , EncryptedDataKey , KeyringTrace
21
+ from aws_encryption_sdk .exceptions import InvalidDataKeyError , InvalidKeyringTraceError , SignatureKeyError
22
+ from aws_encryption_sdk .identifiers import Algorithm , KeyringTraceFlag
23
+ from aws_encryption_sdk .internal .utils .streams import ROStream
24
+ from aws_encryption_sdk .structures import DataKey , EncryptedDataKey , KeyringTrace , RawDataKey
25
+
26
+ try : # Python 3.5.0 and 3.5.1 have incompatible typing modules
27
+ from typing import Iterable , Union # noqa pylint: disable=unused-import
28
+ except ImportError : # pragma: no cover
29
+ # We only actually need these imports when running the mypy checks
30
+ pass
24
31
25
32
26
33
@attr .s (hash = False )
@@ -41,15 +48,24 @@ class EncryptionMaterialsRequest(object):
41
48
:param int plaintext_length: Length of source plaintext (optional)
42
49
"""
43
50
44
- encryption_context = attr .ib (validator = attr .validators .instance_of (dict ))
45
- frame_length = attr .ib (validator = attr .validators .instance_of (six .integer_types ))
46
- plaintext_rostream = attr .ib (
47
- default = None , validator = attr .validators .optional (attr .validators .instance_of (ROStream ))
48
- )
49
- algorithm = attr .ib (default = None , validator = attr .validators .optional (attr .validators .instance_of (Algorithm )))
50
- plaintext_length = attr .ib (
51
- default = None , validator = attr .validators .optional (attr .validators .instance_of (six .integer_types ))
51
+ encryption_context = attr .ib (
52
+ validator = deep_mapping (
53
+ key_validator = instance_of (six .string_types ), value_validator = instance_of (six .string_types )
54
+ )
52
55
)
56
+ frame_length = attr .ib (validator = instance_of (six .integer_types ))
57
+ plaintext_rostream = attr .ib (default = None , validator = optional (instance_of (ROStream )))
58
+ algorithm = attr .ib (default = None , validator = optional (instance_of (Algorithm )))
59
+ plaintext_length = attr .ib (default = None , validator = optional (instance_of (six .integer_types )))
60
+
61
+
62
+ def _data_key_to_raw_data_key (data_key ):
63
+ # type: (Union[DataKey, RawDataKey, None]) -> Union[RawDataKey, None]
64
+ """Convert a :class:`DataKey` into a :class:`RawDataKey`."""
65
+ if isinstance (data_key , RawDataKey ) or data_key is None :
66
+ return data_key
67
+
68
+ return RawDataKey .from_data_key (data_key = data_key )
53
69
54
70
55
71
@attr .s
@@ -73,13 +89,87 @@ class CryptographicMaterials(object):
73
89
deep_mapping (key_validator = instance_of (six .string_types ), value_validator = instance_of (six .string_types ))
74
90
)
75
91
)
76
- data_encryption_key = attr .ib (default = None , validator = optional (instance_of (DataKey )))
77
- encrypted_data_keys = attr .ib (
78
- default = attr .Factory (list ), validator = optional (deep_iterable (member_validator = instance_of (EncryptedDataKey )))
92
+ data_encryption_key = attr .ib (
93
+ default = None , validator = optional (instance_of (RawDataKey )), converter = _data_key_to_raw_data_key
79
94
)
80
- keyring_trace = attr .ib (
95
+ _keyring_trace = attr .ib (
81
96
default = attr .Factory (list ), validator = optional (deep_iterable (member_validator = instance_of (KeyringTrace )))
82
97
)
98
+ _initialized = False
99
+
100
+ def __attrs_post_init__ (self ):
101
+ """Freeze attributes after initialization."""
102
+ self ._initialized = True
103
+
104
+ def __setattr__ (self , key , value ):
105
+ """Do not allow attributes to be changed once an instance is initialized."""
106
+ if self ._initialized :
107
+ raise AttributeError ("can't set attribute" )
108
+
109
+ self ._setattr (key , value )
110
+
111
+ def _setattr (self , key , value ):
112
+ """Special __setattr__ to avoid having to perform multi-level super calls."""
113
+ super (CryptographicMaterials , self ).__setattr__ (key , value )
114
+
115
+ def _validate_data_encryption_key (self , data_encryption_key , keyring_trace , required_flags ):
116
+ # type: (Union[DataKey, RawDataKey], KeyringTrace, Iterable[KeyringTraceFlag]) -> None
117
+ """Validate that the provided data encryption key and keyring trace match for each other and the materials.
118
+
119
+ :param RawDataKey data_encryption_key: Data encryption key
120
+ :param KeyringTrace keyring_trace: Keyring trace corresponding to data_encryption_key
121
+ :param required_flags: Iterable of required flags
122
+ :type required_flags: iterable of :class:`KeyringTraceFlag`
123
+ :raises AttributeError: if data encryption key is already set
124
+ :raises InvalidKeyringTraceError: if keyring trace does not match decrypt action
125
+ :raises InvalidKeyringTraceError: if keyring trace does not match data key provider
126
+ :raises InvalidDataKeyError: if data key length does not match algorithm suite
127
+ """
128
+ if self .data_encryption_key is not None :
129
+ raise AttributeError ("Data encryption key is already set." )
130
+
131
+ for flag in required_flags :
132
+ if flag not in keyring_trace .flags :
133
+ raise InvalidKeyringTraceError ("Keyring flags do not match action." )
134
+
135
+ if keyring_trace .wrapping_key != data_encryption_key .key_provider :
136
+ raise InvalidKeyringTraceError ("Keyring trace does not match data key provider." )
137
+
138
+ if len (data_encryption_key .data_key ) != self .algorithm .kdf_input_len :
139
+ raise InvalidDataKeyError (
140
+ "Invalid data key length {actual} must be {expected}." .format (
141
+ actual = len (data_encryption_key .data_key ), expected = self .algorithm .kdf_input_len
142
+ )
143
+ )
144
+
145
+ def _add_data_encryption_key (self , data_encryption_key , keyring_trace , required_flags ):
146
+ # type: (Union[DataKey, RawDataKey], KeyringTrace, Iterable[KeyringTraceFlag]) -> None
147
+ """Add a plaintext data encryption key.
148
+
149
+ :param RawDataKey data_encryption_key: Data encryption key
150
+ :param KeyringTrace keyring_trace: Trace of actions that a keyring performed
151
+ while getting this data encryption key
152
+ :raises AttributeError: if data encryption key is already set
153
+ :raises InvalidKeyringTraceError: if keyring trace does not match required actions
154
+ :raises InvalidKeyringTraceError: if keyring trace does not match data key provider
155
+ :raises InvalidDataKeyError: if data key length does not match algorithm suite
156
+ """
157
+ self ._validate_data_encryption_key (
158
+ data_encryption_key = data_encryption_key , keyring_trace = keyring_trace , required_flags = required_flags
159
+ )
160
+
161
+ data_key = _data_key_to_raw_data_key (data_key = data_encryption_key )
162
+
163
+ super (CryptographicMaterials , self ).__setattr__ ("data_encryption_key" , data_key )
164
+ self ._keyring_trace .append (keyring_trace )
165
+
166
+ @property
167
+ def keyring_trace (self ):
168
+ """Return a read-only version of the keyring trace.
169
+
170
+ :rtype: tuple
171
+ """
172
+ return tuple (self ._keyring_trace )
83
173
84
174
85
175
@attr .s (hash = False , init = False )
@@ -106,7 +196,10 @@ class EncryptionMaterials(CryptographicMaterials):
106
196
:type keyring_trace: list of :class:`KeyringTrace`
107
197
"""
108
198
109
- signing_key = attr .ib (default = None , validator = attr .validators .optional (attr .validators .instance_of (bytes )))
199
+ _encrypted_data_keys = attr .ib (
200
+ default = attr .Factory (list ), validator = optional (deep_iterable (member_validator = instance_of (EncryptedDataKey )))
201
+ )
202
+ signing_key = attr .ib (default = None , repr = False , validator = optional (instance_of (bytes )))
110
203
111
204
def __init__ (
112
205
self ,
@@ -116,7 +209,7 @@ def __init__(
116
209
encryption_context = None ,
117
210
signing_key = None ,
118
211
** kwargs
119
- ):
212
+ ): # noqa we define this in the class docstring
120
213
if algorithm is None :
121
214
raise TypeError ("algorithm must not be None" )
122
215
@@ -127,12 +220,79 @@ def __init__(
127
220
algorithm = algorithm ,
128
221
encryption_context = encryption_context ,
129
222
data_encryption_key = data_encryption_key ,
130
- encrypted_data_keys = encrypted_data_keys ,
131
223
** kwargs
132
224
)
133
- self .signing_key = signing_key
225
+ self ._setattr ("signing_key" , signing_key )
226
+ self ._setattr ("_encrypted_data_keys" , encrypted_data_keys )
134
227
attr .validate (self )
135
228
229
+ @property
230
+ def encrypted_data_keys (self ):
231
+ """Return a read-only version of the encrypted data keys.
232
+
233
+ :rtype: frozenset
234
+ """
235
+ return frozenset (self ._encrypted_data_keys )
236
+
237
+ def add_data_encryption_key (self , data_encryption_key , keyring_trace ):
238
+ # type: (Union[DataKey, RawDataKey], KeyringTrace) -> None
239
+ """Add a plaintext data encryption key.
240
+
241
+ .. versionadded:: 1.5.0
242
+
243
+ :param RawDataKey data_encryption_key: Data encryption key
244
+ :param KeyringTrace keyring_trace: Trace of actions that a keyring performed
245
+ while getting this data encryption key
246
+ :raises AttributeError: if data encryption key is already set
247
+ :raises InvalidKeyringTraceError: if keyring trace does not match generate action
248
+ :raises InvalidKeyringTraceError: if keyring trace does not match data key provider
249
+ :raises InvalidDataKeyError: if data key length does not match algorithm suite
250
+ """
251
+ self ._add_data_encryption_key (
252
+ data_encryption_key = data_encryption_key ,
253
+ keyring_trace = keyring_trace ,
254
+ required_flags = {KeyringTraceFlag .WRAPPING_KEY_GENERATED_DATA_KEY },
255
+ )
256
+
257
+ def add_encrypted_data_key (self , encrypted_data_key , keyring_trace ):
258
+ # type: (EncryptedDataKey, KeyringTrace) -> None
259
+ """Add an encrypted data key with corresponding keyring trace.
260
+
261
+ .. versionadded:: 1.5.0
262
+
263
+ :param EncryptedDataKey encrypted_data_key: Encrypted data key to add
264
+ :param KeyringTrace keyring_trace: Trace of actions that a keyring performed
265
+ while getting this encrypted data key
266
+ :raises InvalidKeyringTraceError: if keyring trace does not match generate action
267
+ :raises InvalidKeyringTraceError: if keyring trace does not match data key encryptor
268
+ """
269
+ if KeyringTraceFlag .WRAPPING_KEY_ENCRYPTED_DATA_KEY not in keyring_trace .flags :
270
+ raise InvalidKeyringTraceError ("Keyring flags do not match action." )
271
+
272
+ if keyring_trace .wrapping_key != encrypted_data_key .key_provider :
273
+ raise InvalidKeyringTraceError ("Keyring trace does not match data key encryptor." )
274
+
275
+ self ._encrypted_data_keys .add (encrypted_data_key )
276
+ self ._keyring_trace .append (keyring_trace )
277
+
278
+ def add_signing_key (self , signing_key ):
279
+ # type: (bytes) -> None
280
+ """Add a signing key.
281
+
282
+ .. versionadded:: 1.5.0
283
+
284
+ :param bytes signing_key: Signing key
285
+ :raises AttributeError: if signing key is already set
286
+ :raises SignatureKeyError: if algorithm suite does not support signing keys
287
+ """
288
+ if self .signing_key is not None :
289
+ raise AttributeError ("Signing key is already set." )
290
+
291
+ if self .algorithm .signing_algorithm_info is None :
292
+ raise SignatureKeyError ("Algorithm suite does not support signing keys." )
293
+
294
+ self ._setattr ("signing_key" , signing_key )
295
+
136
296
137
297
@attr .s (hash = False )
138
298
class DecryptionMaterialsRequest (object ):
@@ -147,9 +307,14 @@ class DecryptionMaterialsRequest(object):
147
307
:param dict encryption_context: Encryption context to provide to master keys for underlying decrypt requests
148
308
"""
149
309
150
- algorithm = attr .ib (validator = attr .validators .instance_of (Algorithm ))
151
- encrypted_data_keys = attr .ib (validator = attr .validators .instance_of (set ))
152
- encryption_context = attr .ib (validator = attr .validators .instance_of (dict ))
310
+ algorithm = attr .ib (validator = instance_of (Algorithm ))
311
+ # TODO: Restrict this to only EncryptedDataKeys
312
+ encrypted_data_keys = attr .ib (validator = deep_iterable (member_validator = instance_of ((EncryptedDataKey , DataKey ))))
313
+ encryption_context = attr .ib (
314
+ validator = deep_mapping (
315
+ key_validator = instance_of (six .string_types ), value_validator = instance_of (six .string_types )
316
+ )
317
+ )
153
318
154
319
155
320
_DEFAULT_SENTINEL = object ()
@@ -163,35 +328,33 @@ class DecryptionMaterials(CryptographicMaterials):
163
328
164
329
.. versionadded:: 1.5.0
165
330
166
- The **algorithm**, **data_encryption_key**, **encrypted_data_keys**,
167
- **encryption_context**, and **keyring_trace** parameters.
331
+ The **algorithm**, **data_encryption_key**, **encryption_context**, and **keyring_trace** parameters.
168
332
169
333
.. versionadded:: 1.5.0
170
334
171
335
All parameters are now optional.
172
336
173
337
:param Algorithm algorithm: Algorithm to use for encrypting message (optional)
174
338
:param DataKey data_encryption_key: Plaintext data key to use for encrypting message (optional)
175
- :param encrypted_data_keys: List of encrypted data keys (optional)
176
- :type encrypted_data_keys: list of :class:`EncryptedDataKey`
177
339
:param dict encryption_context: Encryption context tied to `encrypted_data_keys` (optional)
178
340
:param bytes verification_key: Raw signature verification key (optional)
179
341
:param keyring_trace: Any KeyRing trace entries (optional)
180
342
:type keyring_trace: list of :class:`KeyringTrace`
181
343
"""
182
344
183
- verification_key = attr .ib (default = None , validator = attr . validators . optional (attr . validators . instance_of (bytes )))
345
+ verification_key = attr .ib (default = None , repr = False , validator = optional (instance_of (bytes )))
184
346
185
- def __init__ (self , data_key = _DEFAULT_SENTINEL , verification_key = None , ** kwargs ):
186
- if any (
187
- (
188
- data_key is _DEFAULT_SENTINEL and "data_encryption_key" not in kwargs ,
189
- data_key is not _DEFAULT_SENTINEL and "data_encryption_key" in kwargs ,
190
- )
191
- ):
192
- raise TypeError ("Exactly one of data_key or data_encryption_key must be set" )
347
+ def __init__ (
348
+ self , data_key = _DEFAULT_SENTINEL , verification_key = None , ** kwargs
349
+ ): # noqa we define this in the class docstring
193
350
194
- if data_key is not _DEFAULT_SENTINEL and "data_encryption_key" not in kwargs :
351
+ legacy_data_key_set = data_key is not _DEFAULT_SENTINEL
352
+ data_encryption_key_set = "data_encryption_key" in kwargs
353
+
354
+ if legacy_data_key_set and data_encryption_key_set :
355
+ raise TypeError ("Either data_key or data_encryption_key can be used but not both" )
356
+
357
+ if legacy_data_key_set and not data_encryption_key_set :
195
358
kwargs ["data_encryption_key" ] = data_key
196
359
197
360
for legacy_missing in ("algorithm" , "encryption_context" ):
@@ -200,16 +363,46 @@ def __init__(self, data_key=_DEFAULT_SENTINEL, verification_key=None, **kwargs):
200
363
201
364
super (DecryptionMaterials , self ).__init__ (** kwargs )
202
365
203
- self .verification_key = verification_key
366
+ self ._setattr ( " verification_key" , verification_key )
204
367
attr .validate (self )
205
368
206
369
@property
207
370
def data_key (self ):
208
- """Backwards-compatible shim."""
371
+ """Backwards-compatible shim for access to data key ."""
209
372
return self .data_encryption_key
210
373
211
- @data_key .setter
212
- def data_key (self , value ):
213
- # type: (DataKey) -> None
214
- """Backwards-compatible shim."""
215
- self .data_encryption_key = value
374
+ def add_data_encryption_key (self , data_encryption_key , keyring_trace ):
375
+ # type: (Union[DataKey, RawDataKey], KeyringTrace) -> None
376
+ """Add a plaintext data encryption key.
377
+
378
+ .. versionadded:: 1.5.0
379
+
380
+ :param RawDataKey data_encryption_key: Data encryption key
381
+ :param KeyringTrace keyring_trace: Trace of actions that a keyring performed
382
+ while getting this data encryption key
383
+ :raises AttributeError: if data encryption key is already set
384
+ :raises InvalidKeyringTraceError: if keyring trace does not match decrypt action
385
+ :raises InvalidKeyringTraceError: if keyring trace does not match data key provider
386
+ :raises InvalidDataKeyError: if data key length does not match algorithm suite
387
+ """
388
+ self ._add_data_encryption_key (
389
+ data_encryption_key = data_encryption_key ,
390
+ keyring_trace = keyring_trace ,
391
+ required_flags = {KeyringTraceFlag .WRAPPING_KEY_DECRYPTED_DATA_KEY },
392
+ )
393
+
394
+ def add_verification_key (self , verification_key ):
395
+ # type: (bytes) -> None
396
+ """Add a verification key.
397
+
398
+ .. versionadded:: 1.5.0
399
+
400
+ :param bytes verification_key: Verification key
401
+ """
402
+ if self .verification_key is not None :
403
+ raise AttributeError ("Verification key is already set." )
404
+
405
+ if self .algorithm .signing_algorithm_info is None :
406
+ raise SignatureKeyError ("Algorithm suite does not support signing keys." )
407
+
408
+ self ._setattr ("verification_key" , verification_key )
0 commit comments