6
6
"""
7
7
8
8
import abc
9
- import copy
9
+ import logging
10
10
import os
11
11
from typing import Any , Callable , Dict , Mapping , Optional
12
12
from urllib import parse
13
13
14
14
import securesystemslib .gpg .functions as gpg
15
15
import securesystemslib .keys as sslib_keys
16
- from securesystemslib import formats
16
+ from securesystemslib .exceptions import FormatError
17
+
18
+ logger = logging .getLogger (__name__ )
17
19
18
20
# NOTE This dictionary is initialized here so it's available to Signer, but
19
21
# filled at end of file when Signer subclass definitions are available.
@@ -146,6 +148,137 @@ def to_dict(self) -> Dict:
146
148
}
147
149
148
150
151
+ class Key :
152
+ """A container class representing the public portion of a key.
153
+
154
+ *All parameters named below are not just constructor arguments but also
155
+ instance attributes.*
156
+
157
+ Args:
158
+ keyid: Key identifier that is unique within the metadata it is used in.
159
+ Keyid is not verified to be the hash of a specific representation
160
+ of the key.
161
+ keytype: Key type, e.g. "rsa", "ed25519" or "ecdsa-sha2-nistp256".
162
+ scheme: Signature scheme. For example:
163
+ "rsassa-pss-sha256", "ed25519", and "ecdsa-sha2-nistp256".
164
+ keyval: Opaque key content
165
+ unrecognized_fields: Dictionary of all attributes that are not managed
166
+ by Securesystemslib
167
+
168
+ Raises:
169
+ TypeError: Invalid type for an argument.
170
+ """
171
+
172
+ def __init__ (
173
+ self ,
174
+ keyid : str ,
175
+ keytype : str ,
176
+ scheme : str ,
177
+ keyval : Dict [str , Any ],
178
+ unrecognized_fields : Optional [Dict [str , Any ]] = None ,
179
+ ):
180
+ if not all (
181
+ isinstance (at , str ) for at in [keyid , keytype , scheme ]
182
+ ) or not isinstance (keyval , dict ):
183
+ raise TypeError ("Unexpected Key attributes types!" )
184
+ self .keyid = keyid
185
+ self .keytype = keytype
186
+ self .scheme = scheme
187
+ self .keyval = keyval
188
+ if unrecognized_fields is None :
189
+ unrecognized_fields = {}
190
+
191
+ self .unrecognized_fields = unrecognized_fields
192
+
193
+ def __eq__ (self , other : Any ) -> bool :
194
+ if not isinstance (other , Key ):
195
+ return False
196
+
197
+ return (
198
+ self .keyid == other .keyid
199
+ and self .keytype == other .keytype
200
+ and self .scheme == other .scheme
201
+ and self .keyval == other .keyval
202
+ and self .unrecognized_fields == other .unrecognized_fields
203
+ )
204
+
205
+
206
+ @classmethod
207
+ def from_dict (cls , keyid : str , key_dict : Dict [str , Any ]) -> "Key" :
208
+ """Creates ``Key`` object from TUF serialization dict.
209
+
210
+ Raises:
211
+ KeyError, TypeError: Invalid arguments.
212
+ """
213
+ keytype = key_dict .pop ("keytype" )
214
+ scheme = key_dict .pop ("scheme" )
215
+ keyval = key_dict .pop ("keyval" )
216
+ # All fields left in the key_dict are unrecognized.
217
+ return cls (keyid , keytype , scheme , keyval , key_dict )
218
+
219
+ def to_dict (self ) -> Dict [str , Any ]:
220
+ """Returns a dict for TUF serialization."""
221
+ return {
222
+ "keytype" : self .keytype ,
223
+ "scheme" : self .scheme ,
224
+ "keyval" : self .keyval ,
225
+ ** self .unrecognized_fields ,
226
+ }
227
+
228
+ def to_securesystemslib_key (self ) -> Dict [str , Any ]:
229
+ """Returns a classic Securesystemslib keydict"""
230
+ return {
231
+ "keyid" : self .keyid ,
232
+ "keytype" : self .keytype ,
233
+ "scheme" : self .scheme ,
234
+ "keyval" : self .keyval ,
235
+ }
236
+
237
+ @classmethod
238
+ def from_securesystemslib_key (cls , key_dict : Dict [str , Any ]) -> "Key" :
239
+ """Creates a ``Key`` object from a classic securesystemlib keydict.
240
+
241
+ Args:
242
+ key_dict: Key in securesystemlib dict representation.
243
+
244
+ Raises:
245
+ ValueError: ``key_dict`` value is not in securesystemslib format.
246
+ """
247
+ try :
248
+ key_meta = sslib_keys .format_keyval_to_metadata (
249
+ key_dict ["keytype" ],
250
+ key_dict ["scheme" ],
251
+ key_dict ["keyval" ],
252
+ )
253
+ except FormatError as e :
254
+ raise ValueError ("keydict not in securesystemslib format" ) from e
255
+
256
+ return cls (
257
+ key_dict ["keyid" ],
258
+ key_meta ["keytype" ],
259
+ key_meta ["scheme" ],
260
+ key_meta ["keyval" ],
261
+ )
262
+
263
+ def is_verified (self , signature : Signature , data : bytes ) -> bool :
264
+ """Verifies the signature over data.
265
+
266
+ Args:
267
+ signature: Signature object.
268
+ data: Payload bytes.
269
+
270
+ Raises:
271
+ CryptoError, FormatError, UnsupportedAlgorithmError.
272
+
273
+ Returns True if signature is valid for this key for given data.
274
+ """
275
+ return sslib_keys .verify_signature (
276
+ self .to_securesystemslib_key (),
277
+ signature .to_dict (),
278
+ data ,
279
+ )
280
+
281
+
149
282
# SecretsHandler is a function the calling code can provide to Signer:
150
283
# If Signer needs secrets from user, the function will be called
151
284
SecretsHandler = Callable [[str ], str ]
@@ -184,7 +317,7 @@ def sign(self, payload: bytes) -> Signature:
184
317
def new_from_uri (
185
318
cls ,
186
319
priv_key_uri : str ,
187
- public_key : Dict [ str , Any ] ,
320
+ public_key : Key ,
188
321
secrets_handler : SecretsHandler ,
189
322
) -> "Signer" :
190
323
"""Constructor for given private key URI
@@ -195,7 +328,7 @@ def new_from_uri(
195
328
196
329
Arguments:
197
330
priv_key_uri: URI that identifies the private key and signer
198
- public_key: Public key metadata conforming to PUBLIC_KEY_SCHEMA
331
+ public_key: Key object
199
332
secrets_handler: Optional function that may be called if the
200
333
signer needs additional secrets (like a PIN or passphrase)
201
334
"""
@@ -204,17 +337,16 @@ def new_from_uri(
204
337
@staticmethod
205
338
def from_priv_key_uri (
206
339
priv_key_uri : str ,
207
- public_key : Dict [ str , Any ] ,
340
+ public_key : Key ,
208
341
secrets_handler : Optional [SecretsHandler ] = None ,
209
342
):
210
343
"""Returns a concrete Signer implementation based on private key URI
211
344
212
345
Args:
213
346
priv_key_uri: URI that identifies the private key location and signer
214
- public_key: Public key metadata conforming to PUBLIC_KEY_SCHEMA
347
+ public_key: Key object
215
348
"""
216
349
217
- formats .PUBLIC_KEY_SCHEMA .check_match (public_key )
218
350
scheme , _ , _ = priv_key_uri .partition (":" )
219
351
if scheme not in SIGNER_FOR_URI_SCHEME :
220
352
raise ValueError (f"Unsupported private key scheme { scheme } " )
@@ -246,8 +378,8 @@ class SSlibSigner(Signer):
246
378
247
379
Attributes:
248
380
key_dict:
249
- A securesystemslib-style key dictionary, which includes a keyid,
250
- key type, scheme, and keyval with both private and public parts.
381
+ A securesystemslib-style key dictionary. This is an implementation
382
+ detail, not part of public API
251
383
"""
252
384
253
385
ENVVAR_URI_SCHEME = "envvar"
@@ -261,15 +393,14 @@ def __init__(self, key_dict: Dict):
261
393
def new_from_uri (
262
394
cls ,
263
395
priv_key_uri : str ,
264
- public_key : Dict [ str , Any ] ,
396
+ public_key : Key ,
265
397
secrets_handler : SecretsHandler ,
266
398
) -> "SSlibSigner" :
267
399
"""Semi-private Constructor for Signer to call
268
400
269
401
Arguments:
270
402
priv_key_uri: private key URI described in class doc
271
- public_key: securesystemslib-style key dict, which includes keyid,
272
- type, scheme, and keyval the public key.
403
+ public_key: Key object.
273
404
274
405
Raises:
275
406
OSError: Reading the file failed with "file:" URI
@@ -279,7 +410,6 @@ def new_from_uri(
279
410
Returns:
280
411
SSlibSigner for the given private key URI.
281
412
"""
282
- keydict = copy .deepcopy (public_key )
283
413
uri = parse .urlparse (priv_key_uri )
284
414
285
415
if uri .scheme == cls .ENVVAR_URI_SCHEME :
@@ -308,6 +438,7 @@ def new_from_uri(
308
438
f"SSlibSigner does not support priv key uri { priv_key_uri } "
309
439
)
310
440
441
+ keydict = public_key .to_securesystemslib_key ()
311
442
keydict ["keyval" ]["private" ] = private
312
443
return cls (keydict )
313
444
@@ -355,7 +486,7 @@ def __init__(
355
486
def new_from_uri (
356
487
cls ,
357
488
priv_key_uri : str ,
358
- public_key : Dict [ str , Any ] ,
489
+ public_key : Key ,
359
490
secrets_handler : SecretsHandler ,
360
491
) -> Signer :
361
492
# GPGSigner uses keys and produces signature dicts that are not
0 commit comments