def verify(self, key: JsonDict) -> bool: """Verifies 'signatures' over 'signed' that match the passed key by id. Arguments: 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 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: raise tuf.exceptions.Error(f'no signature for key {key["keyid"]}.') if 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.') return verify_signature(key, signatures_for_keyid[0], self.signed.to_canonical_bytes())
def test_sslib_sign(self): dicts = [self.rsakey_dict, self.ecdsakey_dict, self.ed25519key_dict] for scheme_dict in dicts: # Test generation of signatures. sslib_signer = SSlibSigner(scheme_dict) sig_obj = sslib_signer.sign(self.DATA) # Verify signature verified = KEYS.verify_signature(scheme_dict, sig_obj.to_dict(), self.DATA) self.assertTrue(verified, "Incorrect signature.") # Removing private key from "scheme_dict". private = scheme_dict["keyval"]["private"] scheme_dict["keyval"]["private"] = "" sslib_signer.key_dict = scheme_dict with self.assertRaises((ValueError, FormatError)): sslib_signer.sign(self.DATA) scheme_dict["keyval"]["private"] = private # Test for invalid signature scheme. valid_scheme = scheme_dict["scheme"] scheme_dict["scheme"] = "invalid_scheme" sslib_signer = SSlibSigner(scheme_dict) with self.assertRaises((UnsupportedAlgorithmError, FormatError)): sslib_signer.sign(self.DATA) scheme_dict["scheme"] = valid_scheme
def verify( self, key: Mapping[str, Any], signed_serializer: Optional[SignedSerializer] = None, ) -> bool: """Verifies 'signatures' over 'signed' that match the passed key by id. Arguments: key: A securesystemslib-style public key object. signed_serializer: A SignedSerializer subclass instance that implements the desired canonicalization format. Per default a CanonicalJSONSerializer is used. Raises: # TODO: Revise exception taxonomy tuf.exceptions.Error: None or multiple signatures found for key. securesystemslib.exceptions.FormatError: Key argument is malformed. tuf.api.serialization.SerializationError: 'signed' cannot be serialized. securesystemslib.exceptions.CryptoError, \ securesystemslib.exceptions.UnsupportedAlgorithmError: Signing errors. Returns: 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: raise exceptions.Error(f"no signature for key {key['keyid']}.") if len(signatures_for_keyid) > 1: raise exceptions.Error( f"{len(signatures_for_keyid)} signatures for key " f"{key['keyid']}, not sure which one to verify." ) if signed_serializer is None: # Use local scope import to avoid circular import errors # pylint: disable=import-outside-toplevel from tuf.api.serialization.json import CanonicalJSONSerializer signed_serializer = CanonicalJSONSerializer() return verify_signature( key, signatures_for_keyid[0].to_dict(), signed_serializer.serialize(self.signed), )
def verify_signature( self, metadata: Metadata, signed_serializer: Optional[SignedSerializer] = None, ) -> None: """Verifies that the 'metadata.signatures' contains a signature made with this key, correctly signing 'metadata.signed'. Arguments: metadata: Metadata to verify signed_serializer: Optional; SignedSerializer to serialize 'metadata.signed' with. Default is CanonicalJSONSerializer. Raises: UnsignedMetadataError: The signature could not be verified for a variety of possible reasons: see error message. """ try: signature = metadata.signatures[self.keyid] except KeyError: raise exceptions.UnsignedMetadataError( f"no signature for key {self.keyid} found in metadata", metadata.signed, ) from None if signed_serializer is None: # pylint: disable=import-outside-toplevel from tuf.api.serialization.json import CanonicalJSONSerializer signed_serializer = CanonicalJSONSerializer() try: if not sslib_keys.verify_signature( self.to_securesystemslib_key(), signature.to_dict(), signed_serializer.serialize(metadata.signed), ): raise exceptions.UnsignedMetadataError( f"Failed to verify {self.keyid} signature", metadata.signed, ) except ( sslib_exceptions.CryptoError, sslib_exceptions.FormatError, sslib_exceptions.UnsupportedAlgorithmError, ) as e: raise exceptions.UnsignedMetadataError( f"Failed to verify {self.keyid} signature", metadata.signed, ) from e
def get_signature_status(signable, role=None, repository_name='default', threshold=None, keyids=None): """ <Purpose> Return a dictionary representing the status of the signatures listed in 'signable'. Signatures in the returned dictionary are identified by the signature keyid and can have a status of either: * bad -- Invalid signature * good -- Valid signature from key that is available in 'tuf.keydb', and is authorized for the passed role as per 'roledb' (authorization may be overwritten by passed 'keyids'). * unknown -- Signature from key that is not available in 'tuf.keydb', or if 'role' is None. * unknown signing schemes -- Signature from key with unknown signing scheme. * untrusted -- Valid signature from key that is available in 'tuf.keydb', but is not trusted for the passed role as per 'roledb' or the passed 'keyids'. NOTE: The result may contain duplicate keyids or keyids that reference the same key, if 'signable' lists multiple signatures from the same key. <Arguments> signable: A dictionary containing a list of signatures and a 'signed' identifier. signable = {'signed': 'signer', 'signatures': [{'keyid': keyid, 'sig': sig}]} Conformant to tuf.formats.SIGNABLE_SCHEMA. role: TUF role string (e.g. 'root', 'targets', 'snapshot' or timestamp). threshold: Rather than reference the role's threshold as set in roledb, use the given 'threshold' to calculate the signature status of 'signable'. 'threshold' is an integer value that sets the role's threshold value, or the minimum number of signatures needed for metadata to be considered fully signed. keyids: Similar to the 'threshold' argument, use the supplied list of 'keyids' to calculate the signature status, instead of referencing the keyids in roledb for 'role'. <Exceptions> securesystemslib.exceptions.FormatError, if 'signable' does not have the correct format. tuf.exceptions.UnknownRoleError, if 'role' is not recognized. <Side Effects> None. <Returns> A dictionary representing the status of the signatures in 'signable'. Conformant to tuf.formats.SIGNATURESTATUS_SCHEMA. """ # Do the arguments have the correct format? This check will ensure that # arguments have the appropriate number of objects and object types, and that # all dict keys are properly named. Raise # 'securesystemslib.exceptions.FormatError' if the check fails. formats.SIGNABLE_SCHEMA.check_match(signable) sslib_formats.NAME_SCHEMA.check_match(repository_name) if role is not None: formats.ROLENAME_SCHEMA.check_match(role) if threshold is not None: formats.THRESHOLD_SCHEMA.check_match(threshold) if keyids is not None: sslib_formats.KEYIDS_SCHEMA.check_match(keyids) # The signature status dictionary returned. signature_status = {} good_sigs = [] bad_sigs = [] unknown_sigs = [] untrusted_sigs = [] unknown_signing_schemes = [] # Extract the relevant fields from 'signable' that will allow us to identify # the different classes of keys (i.e., good_sigs, bad_sigs, etc.). signed = sslib_formats.encode_canonical(signable['signed']).encode('utf-8') signatures = signable['signatures'] # Iterate the signatures and enumerate the signature_status fields. # (i.e., good_sigs, bad_sigs, etc.). for signature in signatures: keyid = signature['keyid'] # Does the signature use an unrecognized key? try: key = keydb.get_key(keyid, repository_name) except exceptions.UnknownKeyError: unknown_sigs.append(keyid) continue # Does the signature use an unknown/unsupported signing scheme? try: valid_sig = sslib_keys.verify_signature(key, signature, signed) except sslib_exceptions.UnsupportedAlgorithmError: unknown_signing_schemes.append(keyid) continue # We are now dealing with either a trusted or untrusted key... if valid_sig: if role is not None: # Is this an unauthorized key? (a keyid associated with 'role') # Note that if the role is not known, tuf.exceptions.UnknownRoleError # is raised here. if keyids is None: keyids = roledb.get_role_keyids(role, repository_name) if keyid not in keyids: untrusted_sigs.append(keyid) continue # This is an unset role, thus an unknown signature. else: unknown_sigs.append(keyid) continue # Identify good/authorized key. good_sigs.append(keyid) else: # This is a bad signature for a trusted key. bad_sigs.append(keyid) # Retrieve the threshold value for 'role'. Raise # tuf.exceptions.UnknownRoleError if we were given an invalid role. if role is not None: if threshold is None: # Note that if the role is not known, tuf.exceptions.UnknownRoleError is # raised here. threshold = roledb.get_role_threshold( role, repository_name=repository_name) else: logger.debug('Not using roledb.py\'s threshold for ' + repr(role)) else: threshold = 0 # Build the signature_status dict. signature_status['threshold'] = threshold signature_status['good_sigs'] = good_sigs signature_status['bad_sigs'] = bad_sigs signature_status['unknown_sigs'] = unknown_sigs signature_status['untrusted_sigs'] = untrusted_sigs signature_status['unknown_signing_schemes'] = unknown_signing_schemes return signature_status
def verify_data(signature, data): return verify_signature(get_private_key(), signature, data)