diff --git a/tests/test_api.py b/tests/test_api.py index ca45f8ed92..1646747a0f 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -28,6 +28,7 @@ def setUpModule(): # Since setUpModule is called after imports we need to import conditionally. if IS_PY_VERSION_SUPPORTED: + import tuf.exceptions from tuf.api.metadata import ( Metadata, Snapshot, @@ -152,12 +153,9 @@ def test_sign_verify(self): # ... it has a single existing signature, self.assertTrue(len(metadata_obj.signatures) == 1) - # ... valid for the correct key, but + # ... which is valid for the correct key. self.assertTrue(metadata_obj.verify( self.keystore['targets']['public'])) - # ... invalid for an unrelated key. - self.assertFalse(metadata_obj.verify( - self.keystore['snapshot']['public'])) # Append a new signature with the unrelated key and assert that ... metadata_obj.sign(self.keystore['snapshot']['private'], append=True) @@ -177,15 +175,20 @@ def test_sign_verify(self): self.assertTrue(metadata_obj.verify( self.keystore['timestamp']['public'])) - - # Update the metadata, invalidating the existing signature, append - # a new signature with the same key, and assert that ... - metadata_obj.signed.bump_version() + # Assert exception if there are more than one signatures for a key metadata_obj.sign(self.keystore['timestamp']['private'], append=True) - # ... verify returns False, because all signatures identified by a - # keyid must be valid - self.assertFalse(metadata_obj.verify( - self.keystore['timestamp']['public'])) + with self.assertRaises(tuf.exceptions.Error) as ctx: + metadata_obj.verify(self.keystore['timestamp']['public']) + self.assertTrue( + '2 signatures for key' in str(ctx.exception), + str(ctx.exception)) + + # Assert exception if there is no signature for a key + with self.assertRaises(tuf.exceptions.Error) as ctx: + metadata_obj.verify(self.keystore['targets']['public']) + self.assertTrue( + 'no signature for' in str(ctx.exception), + str(ctx.exception)) def test_metadata_base(self): diff --git a/tuf/api/metadata.py b/tuf/api/metadata.py index 95f5779764..cd85bcd5b4 100644 --- a/tuf/api/metadata.py +++ b/tuf/api/metadata.py @@ -45,6 +45,7 @@ import iso8601 import tuf.formats +import tuf.exceptions # Types @@ -242,33 +243,33 @@ def verify(self, key: JsonDict) -> bool: key: A securesystemslib-style public key object. Raises: + # TODO: Revise exception taxonomy + tuf.exceptions.Error: None or multiple signatures found for key. securesystemslib.exceptions.FormatError: Key argument is malformed. securesystemslib.exceptions.CryptoError, \ securesystemslib.exceptions.UnsupportedAlgorithmError: Signing errors. Returns: - A boolean indicating if all identified signatures are valid. False - if no signature was found for the keyid or any of the found - signatures is invalid. - - FIXME: Is this behavior expected? An alternative approach would be - to raise an exception if no signature is found for the keyid, - and/or if more than one sigantures are found for the keyid. + A boolean indicating if the signature is valid for the passed key. """ signatures_for_keyid = list(filter( lambda sig: sig['keyid'] == key['keyid'], self.signatures)) if not signatures_for_keyid: - return False + raise tuf.exceptions.Error( + f'no signature for key {key["keyid"]}.') - for signature in signatures_for_keyid: - if not verify_signature( - key, signature, self.signed.to_canonical_bytes()): - return False + elif len(signatures_for_keyid) > 1: + raise tuf.exceptions.Error( + f'{len(signatures_for_keyid)} signatures for key ' + f'{key["keyid"]}, not sure which one to verify.') + else: + return verify_signature( + key, signatures_for_keyid[0], + self.signed.to_canonical_bytes()) - return True class Signed: