20
20
from datetime import datetime , timedelta
21
21
from typing import Any , ClassVar , Dict , List , Mapping , Optional , Tuple , Type
22
22
23
- from securesystemslib . keys import verify_signature
23
+ from securesystemslib import keys as sslib_keys
24
24
from securesystemslib .signer import Signature , Signer
25
25
from securesystemslib .storage import FilesystemBackend , StorageBackendInterface
26
26
from securesystemslib .util import persist_temp_file
@@ -251,59 +251,6 @@ def sign(
251
251
252
252
return signature
253
253
254
- def verify (
255
- self ,
256
- key : Mapping [str , Any ],
257
- signed_serializer : Optional [SignedSerializer ] = None ,
258
- ) -> bool :
259
- """Verifies 'signatures' over 'signed' that match the passed key by id.
260
-
261
- Arguments:
262
- key: A securesystemslib-style public key object.
263
- signed_serializer: A SignedSerializer subclass instance that
264
- implements the desired canonicalization format. Per default a
265
- CanonicalJSONSerializer is used.
266
-
267
- Raises:
268
- # TODO: Revise exception taxonomy
269
- tuf.exceptions.Error: None or multiple signatures found for key.
270
- securesystemslib.exceptions.FormatError: Key argument is malformed.
271
- tuf.api.serialization.SerializationError:
272
- 'signed' cannot be serialized.
273
- securesystemslib.exceptions.CryptoError, \
274
- securesystemslib.exceptions.UnsupportedAlgorithmError:
275
- Signing errors.
276
-
277
- Returns:
278
- A boolean indicating if the signature is valid for the passed key.
279
-
280
- """
281
- signatures_for_keyid = list (
282
- filter (lambda sig : sig .keyid == key ["keyid" ], self .signatures )
283
- )
284
-
285
- if not signatures_for_keyid :
286
- raise exceptions .Error (f"no signature for key { key ['keyid' ]} ." )
287
-
288
- if len (signatures_for_keyid ) > 1 :
289
- raise exceptions .Error (
290
- f"{ len (signatures_for_keyid )} signatures for key "
291
- f"{ key ['keyid' ]} , not sure which one to verify."
292
- )
293
-
294
- if signed_serializer is None :
295
- # Use local scope import to avoid circular import errors
296
- # pylint: disable=import-outside-toplevel
297
- from tuf .api .serialization .json import CanonicalJSONSerializer
298
-
299
- signed_serializer = CanonicalJSONSerializer ()
300
-
301
- return verify_signature (
302
- key ,
303
- signatures_for_keyid [0 ].to_dict (),
304
- signed_serializer .serialize (self .signed ),
305
- )
306
-
307
254
308
255
class Signed (metaclass = abc .ABCMeta ):
309
256
"""A base class for the signed part of TUF metadata.
@@ -431,6 +378,9 @@ class Key:
431
378
"""A container class representing the public portion of a Key.
432
379
433
380
Attributes:
381
+ keyid: An identifier string that must uniquely identify a key within
382
+ the metadata it is used in. This implementation does not verify
383
+ that keyid is the hash of a specific representation of the key.
434
384
keytype: A string denoting a public key signature system,
435
385
such as "rsa", "ed25519", and "ecdsa-sha2-nistp256".
436
386
scheme: A string denoting a corresponding signature scheme. For example:
@@ -442,26 +392,28 @@ class Key:
442
392
443
393
def __init__ (
444
394
self ,
395
+ keyid : str ,
445
396
keytype : str ,
446
397
scheme : str ,
447
398
keyval : Dict [str , str ],
448
399
unrecognized_fields : Optional [Mapping [str , Any ]] = None ,
449
400
) -> None :
450
401
if not keyval .get ("public" ):
451
402
raise ValueError ("keyval doesn't follow the specification format!" )
403
+ self .keyid = keyid
452
404
self .keytype = keytype
453
405
self .scheme = scheme
454
406
self .keyval = keyval
455
407
self .unrecognized_fields : Mapping [str , Any ] = unrecognized_fields or {}
456
408
457
409
@classmethod
458
- def from_dict (cls , key_dict : Dict [str , Any ]) -> "Key" :
410
+ def from_dict (cls , keyid : str , key_dict : Dict [str , Any ]) -> "Key" :
459
411
"""Creates Key object from its dict representation."""
460
412
keytype = key_dict .pop ("keytype" )
461
413
scheme = key_dict .pop ("scheme" )
462
414
keyval = key_dict .pop ("keyval" )
463
415
# All fields left in the key_dict are unrecognized.
464
- return cls (keytype , scheme , keyval , key_dict )
416
+ return cls (keyid , keytype , scheme , keyval , key_dict )
465
417
466
418
def to_dict (self ) -> Dict [str , Any ]:
467
419
"""Returns the dictionary representation of self."""
@@ -472,6 +424,59 @@ def to_dict(self) -> Dict[str, Any]:
472
424
** self .unrecognized_fields ,
473
425
}
474
426
427
+ def to_securesystemslib_key (self ) -> Dict [str , Any ]:
428
+ """Returns a Securesystemslib compatible representation of self."""
429
+ return {
430
+ "keyid" : self .keyid ,
431
+ "keytype" : self .keytype ,
432
+ "scheme" : self .scheme ,
433
+ "keyval" : self .keyval ,
434
+ }
435
+
436
+ def verify_signature (
437
+ self ,
438
+ metadata : Metadata ,
439
+ signed_serializer : Optional [SignedSerializer ] = None ,
440
+ ):
441
+ """Verifies that the 'metadata.signatures' contains a signature made
442
+ with this key, correctly signing 'metadata.signed'.
443
+
444
+ Arguments:
445
+ metadata: Metadata to verify
446
+ signed_serializer: Optional; SignedSerializer to serialize
447
+ 'metadata.signed' with. Default is CanonicalJSONSerializer.
448
+
449
+ Raises:
450
+ UnsignedMetadataError: The signature could not be verified for a
451
+ variety of possible reasons: see error message.
452
+ TODO: Various other errors currently bleed through from lower
453
+ level components: Issue #1351
454
+ """
455
+ try :
456
+ sigs = metadata .signatures
457
+ signature = next (sig for sig in sigs if sig .keyid == self .keyid )
458
+ except StopIteration :
459
+ raise exceptions .UnsignedMetadataError (
460
+ f"no signature for key { self .keyid } found in metadata" ,
461
+ metadata .signed ,
462
+ ) from None
463
+
464
+ if signed_serializer is None :
465
+ # pylint: disable=import-outside-toplevel
466
+ from tuf .api .serialization .json import CanonicalJSONSerializer
467
+
468
+ signed_serializer = CanonicalJSONSerializer ()
469
+
470
+ if not sslib_keys .verify_signature (
471
+ self .to_securesystemslib_key (),
472
+ signature .to_dict (),
473
+ signed_serializer .serialize (metadata .signed ),
474
+ ):
475
+ raise exceptions .UnsignedMetadataError (
476
+ f"Failed to verify { self .keyid } signature for metadata" ,
477
+ metadata .signed ,
478
+ )
479
+
475
480
476
481
class Role :
477
482
"""A container class containing the set of keyids and threshold associated
@@ -572,7 +577,7 @@ def from_dict(cls, signed_dict: Dict[str, Any]) -> "Root":
572
577
roles = signed_dict .pop ("roles" )
573
578
574
579
for keyid , key_dict in keys .items ():
575
- keys [keyid ] = Key .from_dict (key_dict )
580
+ keys [keyid ] = Key .from_dict (keyid , key_dict )
576
581
for role_name , role_dict in roles .items ():
577
582
roles [role_name ] = Role .from_dict (role_dict )
578
583
@@ -598,10 +603,10 @@ def to_dict(self) -> Dict[str, Any]:
598
603
return root_dict
599
604
600
605
# Update key for a role.
601
- def add_key (self , role : str , keyid : str , key_metadata : Key ) -> None :
602
- """Adds new key for 'role' and updates the key store ."""
603
- self .roles [role ].keyids .add (keyid )
604
- self .keys [keyid ] = key_metadata
606
+ def add_key (self , role : str , key : Key ) -> None :
607
+ """Adds new signing key for delegated role 'role'."""
608
+ self .roles [role ].keyids .add (key . keyid )
609
+ self .keys [key . keyid ] = key
605
610
606
611
def remove_key (self , role : str , keyid : str ) -> None :
607
612
"""Removes key from 'role' and updates the key store.
@@ -880,7 +885,7 @@ def from_dict(cls, delegations_dict: Dict[str, Any]) -> "Delegations":
880
885
keys = delegations_dict .pop ("keys" )
881
886
keys_res = {}
882
887
for keyid , key_dict in keys .items ():
883
- keys_res [keyid ] = Key .from_dict (key_dict )
888
+ keys_res [keyid ] = Key .from_dict (keyid , key_dict )
884
889
roles = delegations_dict .pop ("roles" )
885
890
roles_res = []
886
891
for role_dict in roles :
0 commit comments