From 2fc25adfadc04df0bfad520f2f4f2a6b6f0d28fe Mon Sep 17 00:00:00 2001 From: Joshua Lock Date: Mon, 3 Aug 2020 17:18:39 +0100 Subject: [PATCH] updater: verify newly downloaded root metadata with its signatures Per the detailed client workflow in the specification step 1.2 "Version N+1 of the root metadata file MUST have been signed by: (1) a threshold of keys specified in the trusted root metadata file (version N), and (2) a threshold of keys specified in the new root metadata file being validated (version N+1)." Number 2 is implemented here as this step was not being performed by the Updater. Unfortunately we can't use existing signature verification methods in tuf.sig, because tuf.sig.signature_status() does not verify signatures for keys which are not listed in keydb (and tuf.sig.verify uses tuf.sig.signature_status) Therefore this patch introduces a method for verifying signatures with root keys listed in the signable being verified. Signed-off-by: Joshua Lock --- tuf/client/updater.py | 53 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/tuf/client/updater.py b/tuf/client/updater.py index 150bf376..b5514e35 100755 --- a/tuf/client/updater.py +++ b/tuf/client/updater.py @@ -1370,6 +1370,45 @@ def verify_target_file(target_file_object): + def _verify_root_self_signed(self, signable): + """ + Verify the root metadata in signable is signed by a threshold of keys, + where the threshold and valid keys are defined by itself + """ + threshold = signable['signed']['roles']['root']['threshold'] + keyids = signable['signed']['roles']['root']['keyids'] + keys = signable['signed']['keys'] + signatures = signable['signatures'] + signed = securesystemslib.formats.encode_canonical( + signable['signed']).encode('utf-8') + validated = 0 + + for signature in signatures: + keyid = signature['keyid'] + + # At this point we are verifying that the root metadata is signed by a + # threshold of keys listed in the current root role, therefore skip + # keys with a keyid that is not listed in the current root role. + if keyid not in keyids: + continue + + key = keys[keyid] + # The ANYKEY_SCHEMA check in verify_signature expects the keydict to + # include a keyid + key['keyid'] = keyid + valid_sig = securesystemslib.keys.verify_signature(key, signature, signed) + + if valid_sig: + validated = validated + 1 + + if validated >= threshold: + return True + return False + + + + + def _verify_metadata_file(self, metadata_file_object, metadata_role): """ @@ -1439,6 +1478,20 @@ def _verify_metadata_file(self, metadata_file_object, if not valid: raise securesystemslib.exceptions.BadSignatureError(metadata_role) + # For root metadata, verify the downloaded root metadata object with the + # new threshold of new signatures contained within the downloaded root + # metadata object + # NOTE: we perform the checks on root metadata here because this enables + # us to perform the check before the tempfile is persisted. Furthermore, + # by checking here we can easily perform the check for each download + # mirror. Whereas if we check after _verify_metadata_file we may be + # persisting invalid files and we cannot try copies of the file from other + # mirrors. + if valid and metadata_role == 'root': + valid = self._verify_root_self_signed(metadata_signable) + if not valid: + raise securesystemslib.exceptions.BadSignatureError(metadata_role) +