From 15677d2afd0f3ca4026078e1e4265bf9c964e169 Mon Sep 17 00:00:00 2001 From: Martin Vrachev Date: Tue, 10 Nov 2020 17:31:43 +0200 Subject: [PATCH 1/4] Refactor _get_metadata_file() from updater.py Currently, the `_get_metadata_file()` from `tuf/client/updater.py` is really large and does too many things and some of them partially. Right now, it validates the specification version and the metadata version in it, but doesn't validate if the metadata has expired or if the signature could be trusted. So, it's partially validating the metadata file and at the same time calling `_verify_metadata_file()` from updater.py for the rest of the work which doesn't make sense. It's logically better if we move the validation of the specification and the metadata version in separate functions which would be then called in ` _verify_metadata_file()` and that way all of the validation will be done in ` _verify_metadata_file()`. Signed-off-by: Martin Vrachev --- tuf/client/updater.py | 167 ++++++++++++++++++++++-------------------- 1 file changed, 86 insertions(+), 81 deletions(-) diff --git a/tuf/client/updater.py b/tuf/client/updater.py index ccab75305b..3941dc6c8d 100755 --- a/tuf/client/updater.py +++ b/tuf/client/updater.py @@ -1412,9 +1412,81 @@ def _verify_root_self_signed(self, signable): + def _validate_metadata_version(self, expected_version, metadata_role, + version_downloaded): + """ + Validates the metadata version number. + If the version number is unspecified, ensure that the version number + downloaded is greater than the currently trusted version number for + 'metadata_role'. + """ + + if expected_version is not None: + if version_downloaded != expected_version: + raise tuf.exceptions.BadVersionNumberError('Downloaded' + ' version number: ' + repr(version_downloaded) + '. Version' + ' number MUST be: ' + repr(expected_version)) + + # The caller does not know which version to download. + # Verify that the downloaded version is at least greater + # than the one locally available. + else: + try: + current_version = self.metadata['current'][metadata_role]['version'] + + if version_downloaded < current_version: + raise tuf.exceptions.ReplayedMetadataError(metadata_role, + version_downloaded, current_version) + + except KeyError: + logger.info(metadata_role + ' not available locally.') + + + + def _validate_spec_version(self, metadata_spec_version): + """ + Validates if the specification version number is supported. + It is assumed that "spec_version" is in (major.minor.fix) format, + (for example: "1.4.3") and that releases with the same major version + number maintain backward compatibility. + Consequently, if the major version number of new metadata equals our + expected major version number, the new metadata is safe to parse. + """ + + try: + metadata_spec_version_split = metadata_spec_version.split('.') + metadata_spec_major_version = int(metadata_spec_version_split[0]) + metadata_spec_minor_version = int(metadata_spec_version_split[1]) + + code_spec_version_split = tuf.SPECIFICATION_VERSION.split('.') + code_spec_major_version = int(code_spec_version_split[0]) + code_spec_minor_version = int(code_spec_version_split[1]) + + if metadata_spec_major_version != code_spec_major_version: + raise tuf.exceptions.UnsupportedSpecificationError( + 'Downloaded metadata that specifies an unsupported spec_version. ' + 'This code supports major version number: ' + + repr(code_spec_major_version) + '; however,' + 'metadata spec version is: ' + str(metadata_spec_version)) + + # report to user if minor versions do not match, continue with update + if metadata_spec_minor_version != code_spec_minor_version: + logger.info("Downloaded metadata that specifies a different minor " + + "spec_version.") + logger.info("This code has version " + tuf.SPECIFICATION_VERSION + + " and the metadata lists version number " + + str(metadata_spec_version) + ".") + logger.info("The update will continue as the major versions match.") + + except (ValueError, TypeError) as error: + six.raise_from(securesystemslib.exceptions.FormatError('Improperly' + ' formatted spec_version, which must be in major.minor.fix format'), + error) + + def _verify_metadata_file(self, metadata_file_object, - metadata_role): + metadata_role, expected_version): """ Non-public method that verifies a metadata file. An exception is @@ -1429,6 +1501,10 @@ def _verify_metadata_file(self, metadata_file_object, The role name of the metadata (e.g., 'root', 'targets', 'unclaimed'). + expected_version: + An integer representing the expected and required version number + of the 'metadata_role' file downloaded. + securesystemslib.exceptions.FormatError: In case the metadata file is valid JSON, but not valid TUF metadata. @@ -1468,6 +1544,11 @@ def _verify_metadata_file(self, metadata_file_object, # 'securesystemslib.exceptions.FormatError' if not. tuf.formats.check_signable_object_format(metadata_signable) + self._validate_spec_version(metadata_signable['signed']['spec_version']) + + self._validate_metadata_version(expected_version, metadata_role, + metadata_signable['signed']['version']) + # Is 'metadata_signable' expired? self._ensure_not_expired(metadata_signable['signed'], metadata_role) @@ -1521,8 +1602,8 @@ def _get_metadata_file(self, metadata_role, remote_filename, downloaded. expected_version: - The expected and required version number of the 'metadata_role' file - downloaded. 'expected_version' is an integer. + An integer representing the expected and required version number + of the 'metadata_role' file downloaded. tuf.exceptions.NoWorkingMirrorError: @@ -1549,85 +1630,9 @@ def _get_metadata_file(self, metadata_role, remote_filename, try: file_object = tuf.download.unsafe_download(file_mirror, upperbound_filelength) - file_object.seek(0) - - # Verify 'file_object' according to the callable function. - # 'file_object' is also verified if decompressed above (i.e., the - # uncompressed version). - metadata_signable = \ - securesystemslib.util.load_json_string(file_object.read().decode('utf-8')) - - # Determine if the specification version number is supported. It is - # assumed that "spec_version" is in (major.minor.fix) format, (for - # example: "1.4.3") and that releases with the same major version - # number maintain backwards compatibility. Consequently, if the major - # version number of new metadata equals our expected major version - # number, the new metadata is safe to parse. - try: - metadata_spec_version = metadata_signable['signed']['spec_version'] - metadata_spec_version_split = metadata_spec_version.split('.') - metadata_spec_major_version = int(metadata_spec_version_split[0]) - metadata_spec_minor_version = int(metadata_spec_version_split[1]) - - code_spec_version_split = tuf.SPECIFICATION_VERSION.split('.') - code_spec_major_version = int(code_spec_version_split[0]) - code_spec_minor_version = int(code_spec_version_split[1]) - - if metadata_spec_major_version != code_spec_major_version: - raise tuf.exceptions.UnsupportedSpecificationError( - 'Downloaded metadata that specifies an unsupported ' - 'spec_version. This code supports major version number: ' + - repr(code_spec_major_version) + '; however, the obtained ' - 'metadata lists version number: ' + str(metadata_spec_version)) - - #report to user if minor versions do not match, continue with update - if metadata_spec_minor_version != code_spec_minor_version: - logger.info("Downloaded metadata that specifies a different minor " + - "spec_version. This code has version " + - str(tuf.SPECIFICATION_VERSION) + - " and the metadata lists version number " + - str(metadata_spec_version) + - ". The update will continue as the major versions match.") - - except (ValueError, TypeError) as error: - six.raise_from(securesystemslib.exceptions.FormatError('Improperly' - ' formatted spec_version, which must be in major.minor.fix format'), - error) - - # If the version number is unspecified, ensure that the version number - # downloaded is greater than the currently trusted version number for - # 'metadata_role'. - version_downloaded = metadata_signable['signed']['version'] - - if expected_version is not None: - # Verify that the downloaded version matches the version expected by - # the caller. - if version_downloaded != expected_version: - raise tuf.exceptions.BadVersionNumberError('Downloaded' - ' version number: ' + repr(version_downloaded) + '. Version' - ' number MUST be: ' + repr(expected_version)) - - # The caller does not know which version to download. Verify that the - # downloaded version is at least greater than the one locally - # available. - else: - # Verify that the version number of the locally stored - # 'timestamp.json', if available, is less than what was downloaded. - # Otherwise, accept the new timestamp with version number - # 'version_downloaded'. - - try: - current_version = \ - self.metadata['current'][metadata_role]['version'] - - if version_downloaded < current_version: - raise tuf.exceptions.ReplayedMetadataError(metadata_role, - version_downloaded, current_version) - - except KeyError: - logger.info(metadata_role + ' not available locally.') - self._verify_metadata_file(file_object, metadata_role) + self._verify_metadata_file(file_object, metadata_role, + expected_version) except Exception as exception: # Remember the error from this mirror, and "reset" the target file. From 99f6c9c2fe87099e3b664960a720ef6be62708e1 Mon Sep 17 00:00:00 2001 From: Martin Vrachev Date: Tue, 10 Nov 2020 17:41:17 +0200 Subject: [PATCH 2/4] Tests for new validation functions in updater.py Signed-off-by: Martin Vrachev --- tests/test_endless_data_attack.py | 4 +-- tests/test_updater.py | 56 +++++++++++++++++++++++++++++-- 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/tests/test_endless_data_attack.py b/tests/test_endless_data_attack.py index 34928a93ef..e9cad94377 100755 --- a/tests/test_endless_data_attack.py +++ b/tests/test_endless_data_attack.py @@ -269,8 +269,8 @@ def test_with_tuf(self): self.repository_updater.refresh() except tuf.exceptions.NoWorkingMirrorError as exception: - for mirror_url, mirror_error in six.iteritems(exception.mirror_errors): - self.assertTrue(isinstance(mirror_error, securesystemslib.exceptions.Error)) + for _, mirror_error in six.iteritems(exception.mirror_errors): + self.assertTrue(isinstance(mirror_error, tuf.exceptions.InvalidMetadataJSONError)) else: self.fail('TUF did not prevent an endless data attack.') diff --git a/tests/test_updater.py b/tests/test_updater.py index a9cf90b384..78a9770cf4 100755 --- a/tests/test_updater.py +++ b/tests/test_updater.py @@ -1760,10 +1760,58 @@ def test_11__verify_metadata_file(self): self.assertRaises(tuf.exceptions.InvalidMetadataJSONError, self.repository_updater._verify_metadata_file, - metadata_file_object, 'root') + metadata_file_object, 'root', None) - def test_12__get_file(self): + + def test_12__validate_metadata_version(self): + # Test for valid metadata version with expected_version. + self.repository_updater._validate_metadata_version( + expected_version=1, metadata_role='root', version_downloaded=1) + + # Test for valid metadata version without expected_version. + self.repository_updater._validate_metadata_version( + expected_version=None, metadata_role='root', version_downloaded=1) + + # Test for expected_version different than version downloaded. + self.assertRaises(tuf.exceptions.BadVersionNumberError, + self.repository_updater._validate_metadata_version, + expected_version=2, metadata_role='root', version_downloaded=1) + + # Test without expected_version and version_downloaded < current_version. + self.assertRaises(tuf.exceptions.ReplayedMetadataError, + self.repository_updater._validate_metadata_version, + expected_version=None, metadata_role='root', version_downloaded=0) + + + + def test_13__validate_spec_version(self): + # Tests when metadata spec ver is compatible with tuf.SPECIFICATION_VERSION + + # metadata spec ver = tuf.SPECIFICATION_VERSION + self.repository_updater._validate_spec_version(tuf.SPECIFICATION_VERSION) + + code_spec_ver_split = tuf.SPECIFICATION_VERSION.split('.') + code_spec_major = int(code_spec_ver_split[0]) + code_spec_minor= int(code_spec_ver_split[1]) + + # metadata major ver is the same as tuf.SPECIFICATION_VERSION major ver + # but metadata minor ver != tuf.SPECIFICATION_VERSION minor ver + metadata_spec = [str(code_spec_major), str(code_spec_minor + 1), '0'] + separator = '.' + metadata_spec = separator.join(metadata_spec) + self.repository_updater._validate_spec_version(metadata_spec) + + # Test when metadata spec ver is NOT compatible + # with tuf.SPECIFICATION_VERSION + metadata_spec = [str(code_spec_major + 1), str(code_spec_minor), '0'] + metadata_spec = separator.join(metadata_spec) + self.assertRaises(tuf.exceptions.UnsupportedSpecificationError, + self.repository_updater._validate_spec_version, metadata_spec) + + + + def test_14__get_file(self): # Test for an "unsafe" download, where the file is downloaded up to # a required length (and no more). The "safe" download approach # downloads an exact required length. @@ -1783,7 +1831,9 @@ def verify_target_file(targets_path): self.repository_updater._get_file('targets.json', verify_target_file, file_type, file_size, download_safely=False).close() - def test_13__targets_of_role(self): + + + def test_15__targets_of_role(self): # Test case where a list of targets is given. By default, the 'targets' # parameter is None. targets = [{'filepath': 'file1.txt', 'fileinfo': {'length': 1, 'hashes': {'sha256': 'abc'}}}] From 7393e04c6c1d74e18d70c3cbc01637dc71c34b54 Mon Sep 17 00:00:00 2001 From: Martin Vrachev Date: Tue, 17 Nov 2020 15:21:31 +0200 Subject: [PATCH 3/4] Add a chain of root trust Add an optional feature to establish a chain of trust between a bootstrap root metadata file and the trusted current root metadata. If bootstrap_root_path is provided, the folder of the bootstrap root will be expected to contain all intermediate root files between the bootstrap root version and the current root version. Also, if this feature is enabled, when updating a root.json file with _update_metadata call (from updater.py) the root metadata files marked as "previous" won't be deleted, but instead, they will be moved to the provided bootstrap root folder. That way after updating to a new version of the root metadata, the chain of trust will be preserved. Signed-off-by: Martin Vrachev --- tuf/client/updater.py | 106 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 100 insertions(+), 6 deletions(-) diff --git a/tuf/client/updater.py b/tuf/client/updater.py index 3941dc6c8d..cbdf710872 100755 --- a/tuf/client/updater.py +++ b/tuf/client/updater.py @@ -619,7 +619,8 @@ class Updater(object): http://www.python.org/dev/peps/pep-0008/#method-names-and-instance-variables """ - def __init__(self, repository_name, repository_mirrors): + def __init__(self, repository_name, repository_mirrors, + bootstrap_root_path=None): """ Constructor. Instantiating an updater object causes all the metadata @@ -658,6 +659,13 @@ def __init__(self, repository_name, repository_mirrors): 'metadata_path': 'metadata', 'targets_path': 'targets', 'confined_target_dirs': ['']}} + bootstrap_root_path: + Optional path to the bootstrap root metadata file. + If provided, on each refresh() call a chain of trust will be + established from the bootstrap root up to the current root file. + The bootstrap_root_path parent folder will be used to store + all root versions marked as "previous" on _update_metadata() call. + Default is None. securesystemslib.exceptions.FormatError: @@ -668,8 +676,8 @@ def __init__(self, repository_name, repository_mirrors): as a missing 'root.json' file. - Th metadata files (e.g., 'root.json', 'targets.json') for the top- level - roles are read from disk and stored in dictionaries. In addition, the + The metadata files (e.g., 'root.json', 'targets.json') for the top-level + roles are read from disk and stored in dictionaries. In addition, the key and roledb modules are populated with 'repository_name' entries. @@ -763,9 +771,39 @@ def __init__(self, repository_name, repository_mirrors): raise tuf.exceptions.RepositoryError('No root of trust!' ' Could not find the "root.json" file.') + self._set_boostrap_root(bootstrap_root_path) + def _set_boostrap_root(self, bootstrap_root_path): + """ + Internal function. Setup boostrap_root in a separate function + to provide flexibilty needed for testing. + """ + + self.bootstrap_root = bootstrap_root_path + self.bootstrap_root_folder = None + if bootstrap_root_path: + path = bootstrap_root_path + # Open as byte file, so it could be decoded in _verify_metadata_file. + with open(bootstrap_root_path, "rb") as bootstrap_root: + try: + self._verify_metadata_file(bootstrap_root, "root", + expected_version=None, ensure_not_expired=False) + + bootstrap_root.seek(0) + bootstrap_metadata = bootstrap_root.read().decode('utf-8') + + self.bootstap_signable = securesystemslib.util.load_json_string( + bootstrap_metadata) + except Exception: + filename = os.path.basename(path) + logger.warning("Failed to setup " + filename + " as boostrap root!") + raise + + self.bootstrap_root_folder = os.path.dirname(path) + + def __str__(self): """ @@ -1062,6 +1100,9 @@ def refresh(self, unsafely_update_root_if_necessary=True): logger.info('An expired Root metadata was loaded and must be updated.') raise + if self.bootstrap_root: + self._chain_of_root_trust(self.metadata['current']['root']['version']) + # Update the root metadata and verify it by building a chain of trusted root # keys from the current trusted root metadata file self._update_root_metadata(root_metadata) @@ -1087,6 +1128,47 @@ def refresh(self, unsafely_update_root_if_necessary=True): + def _chain_of_root_trust(self, current_root_version): + """ + Establishes a chain of trust between the bootstrap root + and the current root. + Raises an exception if the current_root_version is lower than + the bootstrap root version or the validation of one of + the intermediate root files failed somewhere. + """ + + low = self.bootstap_signable['signed']['version'] + up = current_root_version + + if low > up: + raise tuf.exceptions.BadVersionNumberError('Bootstrap root ver ' \ + + str(low) + ' is higher than the curr root ver ' + str(up) + '!') + + # We don't need to verify self.bootstrap_root again. + # It was verified in __init__. + # We don't need to verify the current_root. + # It was verified when _update_root_metadata was called. + for prev_version in range(low + 1, up): + prev_root_filename = str(prev_version) + ".root.json" + prev_root_path = os.path.join(self.bootstrap_root_folder, + prev_root_filename) + logger.debug('Verifying root file: ' + prev_root_filename) + + try: + # Open as byte file, so it could be decoded in _verify_metadata_file. + with open(prev_root_path, 'rb') as prev_root: + self._verify_metadata_file(prev_root, 'root', prev_version, + ensure_not_expired=False) + + except Exception: + logger.warning("Failed to verify " + prev_root_filename) + raise + + logger.info('The chain of root trust between ' + str(low) + ' and ' \ + + str(up) + ' root versions has been successfully established!') + + + def _update_root_metadata(self, current_root_metadata): """ @@ -1486,7 +1568,7 @@ def _validate_spec_version(self, metadata_spec_version): def _verify_metadata_file(self, metadata_file_object, - metadata_role, expected_version): + metadata_role, expected_version, ensure_not_expired=True): """ Non-public method that verifies a metadata file. An exception is @@ -1505,6 +1587,11 @@ def _verify_metadata_file(self, metadata_file_object, An integer representing the expected and required version number of the 'metadata_role' file downloaded. + ensure_not_expired: + A boolean flag indicating do we need to ensure + that the metadata has not_expired. + Default is True. + securesystemslib.exceptions.FormatError: In case the metadata file is valid JSON, but not valid TUF metadata. @@ -1549,8 +1636,9 @@ def _verify_metadata_file(self, metadata_file_object, self._validate_metadata_version(expected_version, metadata_role, metadata_signable['signed']['version']) - # Is 'metadata_signable' expired? - self._ensure_not_expired(metadata_signable['signed'], metadata_role) + if ensure_not_expired: + # Is 'metadata_signable' expired? + self._ensure_not_expired(metadata_signable['signed'], metadata_role) # We previously verified version numbers in this function, but have since # moved version number verification to the functions that retrieve @@ -1834,6 +1922,12 @@ def _update_metadata(self, metadata_role, upperbound_filelength, version=None): if os.path.exists(current_filepath): # Previous metadata might not exist, say when delegations are added. securesystemslib.util.ensure_parent_dir(previous_filepath) + + if metadata_role == "root" and self.bootstrap_root_folder: + # Make sure we don't delete the old root metadata files. + # We will need them to establish chain of root trust. + previous_filepath = self.bootstrap_root_folder + shutil.move(current_filepath, previous_filepath) # Next, move the verified updated metadata file to the 'current' directory. From e21a885ca7872d88755a83cd272a441587a715ad Mon Sep 17 00:00:00 2001 From: Martin Vrachev Date: Tue, 17 Nov 2020 15:28:21 +0200 Subject: [PATCH 4/4] Add tests for the "chain of root trust" feature New root.json metadata files had to be generated in order to tests properly the establishment of a chain of trust between a bootstrap root file and the current trustworthy root file. The sequence "1.root.json", "2.root.json" is generated by marking the older one "X.root.json" as "dirty" and using "writeall()" function from repository_tool.py. Signed-off-by: Martin Vrachev --- .../metadata/root_sequence/1.root.json | 87 +++++++++++++++++++ .../metadata/root_sequence/2.root.json | 87 +++++++++++++++++++ .../metadata/root_sequence/3.root.json | 87 +++++++++++++++++++ .../metadata/root_sequence/4.root.json | 87 +++++++++++++++++++ .../metadata/root_sequence/broken-root.json | 87 +++++++++++++++++++ tests/test_repository_tool.py | 7 ++ tests/test_updater.py | 35 ++++++++ 7 files changed, 477 insertions(+) create mode 100644 tests/repository_data/repository/metadata/root_sequence/1.root.json create mode 100644 tests/repository_data/repository/metadata/root_sequence/2.root.json create mode 100644 tests/repository_data/repository/metadata/root_sequence/3.root.json create mode 100644 tests/repository_data/repository/metadata/root_sequence/4.root.json create mode 100644 tests/repository_data/repository/metadata/root_sequence/broken-root.json diff --git a/tests/repository_data/repository/metadata/root_sequence/1.root.json b/tests/repository_data/repository/metadata/root_sequence/1.root.json new file mode 100644 index 0000000000..214d8db01b --- /dev/null +++ b/tests/repository_data/repository/metadata/root_sequence/1.root.json @@ -0,0 +1,87 @@ +{ + "signatures": [ + { + "keyid": "4e777de0d275f9d28588dd9a1606cc748e548f9e22b6795b7cb3f63f98035fcb", + "sig": "a337d6375fedd2eabfcd6c2ef6c8a9c3bb85dc5a857715f6a6bd41123e7670c4972d8548bcd7248154f3d864bf25f1823af59d74c459f41ea09a02db057ca1245612ebbdb97e782c501dc3e094f7fa8aa1402b03c6ed0635f565e2a26f9f543a89237e15a2faf0c267e2b34c3c38f2a43a28ddcdaf8308a12ead8c6dc47d1b762de313e9ddda8cc5bc25aea1b69d0e5b9199ca02f5dda48c3bff615fd12a7136d00634b9abc6e75c3256106c4d6f12e6c43f6195071355b2857bbe377ce028619b58837696b805040ce144b393d50a472531f430fadfb68d3081b6a8b5e49337e328c9a0a3f11e80b0bc8eb2dc6e78d1451dd857e6e6e6363c3fd14c590aa95e083c9bfc77724d78af86eb7a7ef635eeddaa353030c79f66b3ba9ea11fab456cfe896a826fdfb50a43cd444f762821aada9bcd7b022c0ee85b8768f960343d5a1d3d76374cc0ac9e12a500de0bf5d48569e5398cadadadab045931c398e3bcb6cec88af2437ba91959f956079cbed159fed3938016e6c3b5e446131f81cc5981" + } + ], + "signed": { + "_type": "root", + "consistent_snapshot": false, + "expires": "2030-01-01T00:00:00Z", + "keys": { + "4e777de0d275f9d28588dd9a1606cc748e548f9e22b6795b7cb3f63f98035fcb": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "rsa", + "keyval": { + "public": "-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA0GjPoVrjS9eCqzoQ8VRe\nPkC0cI6ktiEgqPfHESFzyxyjC490Cuy19nuxPcJuZfN64MC48oOkR+W2mq4pM51i\nxmdG5xjvNOBRkJ5wUCc8fDCltMUTBlqt9y5eLsf/4/EoBU+zC4SW1iPU++mCsity\nfQQ7U6LOn3EYCyrkH51hZ/dvKC4o9TPYMVxNecJ3CL1q02Q145JlyjBTuM3Xdqsa\nndTHoXSRPmmzgB/1dL/c4QjMnCowrKW06mFLq9RAYGIaJWfM/0CbrOJpVDkATmEc\nMdpGJYDfW/sRQvRdlHNPo24ZW7vkQUCqdRxvnTWkK5U81y7RtjLt1yskbWXBIbOV\nz94GXsgyzANyCT9qRjHXDDz2mkLq+9I2iKtEqaEePcWRu3H6RLahpM/TxFzw684Y\nR47weXdDecPNxWyiWiyMGStRFP4Cg9trcwAGnEm1w8R2ggmWphznCd5dXGhPNjfA\na82yNFY8ubnOUVJOf0nXGg3Edw9iY3xyjJb2+nrsk5f3AgMBAAE=\n-----END PUBLIC KEY-----" + }, + "scheme": "rsassa-pss-sha256" + }, + "59a4df8af818e9ed7abe0764c0b47b4240952aa0d179b5b78346c470ac30278d": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "edcd0a32a07dce33f7c7873aaffbff36d20ea30787574ead335eefd337e4dacd" + }, + "scheme": "ed25519" + }, + "65171251a9aff5a8b3143a813481cb07f6e0de4eb197c767837fe4491b739093": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "89f28bd4ede5ec3786ab923fd154f39588d20881903e69c7b08fb504c6750815" + }, + "scheme": "ed25519" + }, + "8a1c4a3ac2d515dec982ba9910c5fd79b91ae57f625b9cff25d06bf0a61c1758": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "82ccf6ac47298ff43bfa0cd639868894e305a99c723ff0515ae2e9856eb5bbf4" + }, + "scheme": "ed25519" + } + }, + "roles": { + "root": { + "keyids": [ + "4e777de0d275f9d28588dd9a1606cc748e548f9e22b6795b7cb3f63f98035fcb" + ], + "threshold": 1 + }, + "snapshot": { + "keyids": [ + "59a4df8af818e9ed7abe0764c0b47b4240952aa0d179b5b78346c470ac30278d" + ], + "threshold": 1 + }, + "targets": { + "keyids": [ + "65171251a9aff5a8b3143a813481cb07f6e0de4eb197c767837fe4491b739093" + ], + "threshold": 1 + }, + "timestamp": { + "keyids": [ + "8a1c4a3ac2d515dec982ba9910c5fd79b91ae57f625b9cff25d06bf0a61c1758" + ], + "threshold": 1 + } + }, + "spec_version": "1.0.0", + "version": 1 + } +} \ No newline at end of file diff --git a/tests/repository_data/repository/metadata/root_sequence/2.root.json b/tests/repository_data/repository/metadata/root_sequence/2.root.json new file mode 100644 index 0000000000..0f16a65ac9 --- /dev/null +++ b/tests/repository_data/repository/metadata/root_sequence/2.root.json @@ -0,0 +1,87 @@ +{ + "signatures": [ + { + "keyid": "4e777de0d275f9d28588dd9a1606cc748e548f9e22b6795b7cb3f63f98035fcb", + "sig": "932485fe7318ab48317dde790ef6cb22cce3e44525b1e8e06a5f8e2c61ec2517ae745993df97c2dfe0024a61bb762e0f5405b2125f49005ea17596706957bf504582f3abf5187662c27fb475aaedcfa84eea31709ec325e17023a66bd6cc5ec32b5f669d677516804dbbf9f8599f166f2699af8f2b6ba1f21bf3ddbbfefa5e16266c5b6ef9d4d72926d5a7cf35b8da1fba4bc057df32127d7058254f6c45f5ca2fb6486c1737d0bc9d698e8f00edb9ed5b2abcc091c9909f61cedc69dca4df4a3ba6e2b8958725eac519c486dfe1dde4928d47cf826ee0065a2169ad4d511b11c6c454b73165e6f1ad904f445a6e9c3e502a0d785b34c4ad52a99733a81ab80664618cd500ee87832e5185b2f08c278603a5d56114cfdc9a6e171df73b8f074745d97851ee0a55be11265b9480aa830583ca3cfed70bf6371976fbc48d77bcef40f2a6aacdc3c4b9fabace5835a85904e921eb1a5cae4f2409e3b15f693497cbd1a82c6cd3fc6d22c159e3af0e742d4da417dd54347ec189eede78e08664beb1" + } + ], + "signed": { + "_type": "root", + "consistent_snapshot": false, + "expires": "2030-01-01T00:00:00Z", + "keys": { + "4e777de0d275f9d28588dd9a1606cc748e548f9e22b6795b7cb3f63f98035fcb": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "rsa", + "keyval": { + "public": "-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA0GjPoVrjS9eCqzoQ8VRe\nPkC0cI6ktiEgqPfHESFzyxyjC490Cuy19nuxPcJuZfN64MC48oOkR+W2mq4pM51i\nxmdG5xjvNOBRkJ5wUCc8fDCltMUTBlqt9y5eLsf/4/EoBU+zC4SW1iPU++mCsity\nfQQ7U6LOn3EYCyrkH51hZ/dvKC4o9TPYMVxNecJ3CL1q02Q145JlyjBTuM3Xdqsa\nndTHoXSRPmmzgB/1dL/c4QjMnCowrKW06mFLq9RAYGIaJWfM/0CbrOJpVDkATmEc\nMdpGJYDfW/sRQvRdlHNPo24ZW7vkQUCqdRxvnTWkK5U81y7RtjLt1yskbWXBIbOV\nz94GXsgyzANyCT9qRjHXDDz2mkLq+9I2iKtEqaEePcWRu3H6RLahpM/TxFzw684Y\nR47weXdDecPNxWyiWiyMGStRFP4Cg9trcwAGnEm1w8R2ggmWphznCd5dXGhPNjfA\na82yNFY8ubnOUVJOf0nXGg3Edw9iY3xyjJb2+nrsk5f3AgMBAAE=\n-----END PUBLIC KEY-----" + }, + "scheme": "rsassa-pss-sha256" + }, + "59a4df8af818e9ed7abe0764c0b47b4240952aa0d179b5b78346c470ac30278d": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "edcd0a32a07dce33f7c7873aaffbff36d20ea30787574ead335eefd337e4dacd" + }, + "scheme": "ed25519" + }, + "65171251a9aff5a8b3143a813481cb07f6e0de4eb197c767837fe4491b739093": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "89f28bd4ede5ec3786ab923fd154f39588d20881903e69c7b08fb504c6750815" + }, + "scheme": "ed25519" + }, + "8a1c4a3ac2d515dec982ba9910c5fd79b91ae57f625b9cff25d06bf0a61c1758": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "82ccf6ac47298ff43bfa0cd639868894e305a99c723ff0515ae2e9856eb5bbf4" + }, + "scheme": "ed25519" + } + }, + "roles": { + "root": { + "keyids": [ + "4e777de0d275f9d28588dd9a1606cc748e548f9e22b6795b7cb3f63f98035fcb" + ], + "threshold": 1 + }, + "snapshot": { + "keyids": [ + "59a4df8af818e9ed7abe0764c0b47b4240952aa0d179b5b78346c470ac30278d" + ], + "threshold": 1 + }, + "targets": { + "keyids": [ + "65171251a9aff5a8b3143a813481cb07f6e0de4eb197c767837fe4491b739093" + ], + "threshold": 1 + }, + "timestamp": { + "keyids": [ + "8a1c4a3ac2d515dec982ba9910c5fd79b91ae57f625b9cff25d06bf0a61c1758" + ], + "threshold": 1 + } + }, + "spec_version": "1.0.0", + "version": 2 + } +} \ No newline at end of file diff --git a/tests/repository_data/repository/metadata/root_sequence/3.root.json b/tests/repository_data/repository/metadata/root_sequence/3.root.json new file mode 100644 index 0000000000..42ea1c5bd8 --- /dev/null +++ b/tests/repository_data/repository/metadata/root_sequence/3.root.json @@ -0,0 +1,87 @@ +{ + "signatures": [ + { + "keyid": "4e777de0d275f9d28588dd9a1606cc748e548f9e22b6795b7cb3f63f98035fcb", + "sig": "bc0c34f49f4c6fa7a0c5f5760ef55b1b16c768701189d3376843657f339c39bb587d2d48d6a1d6c973dfcaf4a53e2fad6fc0a74147527662856aac738c2c2ecbe8794d0ed175bea94546c563957274e07a07fc5c11e0b193a711dd6ee9f7f288aca720194137b99acf145a2e5ab4daf8fb0e575098c09614a9a4ea500c20f31ab062e4c622578d82a4f874dbb9ca8a39314a575883adbd6a8cf548754fc720b3353d835af525c887335480edf84b6c9210438f97140432dfe099a9a6b5ab4e134c13979f74b9c38899aa5e9624b040a3a96a1298b2ef8c75996e7ce5ffefb9396bd8be90deb4ad0508af8806212b8316161c44d6f117b98506d73116399a278075b3271e106ba7854dc5ef759f3b7823e3b015f72a65cbab22ec818cf7f784cbaffdbeecc6c0e1e6f5c5b8dd60fb10e2760208ba6786ddb685b26fa505ab5ce58df00cfb4aa5db9e96cd964c80ebc59c3be9f6cc0e24a79288918ff1c4527db724c35170df416d385008ac67b452bda49b04530a5aebbcecd8a7e0f550f2d436" + } + ], + "signed": { + "_type": "root", + "consistent_snapshot": false, + "expires": "2030-01-01T00:00:00Z", + "keys": { + "4e777de0d275f9d28588dd9a1606cc748e548f9e22b6795b7cb3f63f98035fcb": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "rsa", + "keyval": { + "public": "-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA0GjPoVrjS9eCqzoQ8VRe\nPkC0cI6ktiEgqPfHESFzyxyjC490Cuy19nuxPcJuZfN64MC48oOkR+W2mq4pM51i\nxmdG5xjvNOBRkJ5wUCc8fDCltMUTBlqt9y5eLsf/4/EoBU+zC4SW1iPU++mCsity\nfQQ7U6LOn3EYCyrkH51hZ/dvKC4o9TPYMVxNecJ3CL1q02Q145JlyjBTuM3Xdqsa\nndTHoXSRPmmzgB/1dL/c4QjMnCowrKW06mFLq9RAYGIaJWfM/0CbrOJpVDkATmEc\nMdpGJYDfW/sRQvRdlHNPo24ZW7vkQUCqdRxvnTWkK5U81y7RtjLt1yskbWXBIbOV\nz94GXsgyzANyCT9qRjHXDDz2mkLq+9I2iKtEqaEePcWRu3H6RLahpM/TxFzw684Y\nR47weXdDecPNxWyiWiyMGStRFP4Cg9trcwAGnEm1w8R2ggmWphznCd5dXGhPNjfA\na82yNFY8ubnOUVJOf0nXGg3Edw9iY3xyjJb2+nrsk5f3AgMBAAE=\n-----END PUBLIC KEY-----" + }, + "scheme": "rsassa-pss-sha256" + }, + "59a4df8af818e9ed7abe0764c0b47b4240952aa0d179b5b78346c470ac30278d": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "edcd0a32a07dce33f7c7873aaffbff36d20ea30787574ead335eefd337e4dacd" + }, + "scheme": "ed25519" + }, + "65171251a9aff5a8b3143a813481cb07f6e0de4eb197c767837fe4491b739093": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "89f28bd4ede5ec3786ab923fd154f39588d20881903e69c7b08fb504c6750815" + }, + "scheme": "ed25519" + }, + "8a1c4a3ac2d515dec982ba9910c5fd79b91ae57f625b9cff25d06bf0a61c1758": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "82ccf6ac47298ff43bfa0cd639868894e305a99c723ff0515ae2e9856eb5bbf4" + }, + "scheme": "ed25519" + } + }, + "roles": { + "root": { + "keyids": [ + "4e777de0d275f9d28588dd9a1606cc748e548f9e22b6795b7cb3f63f98035fcb" + ], + "threshold": 1 + }, + "snapshot": { + "keyids": [ + "59a4df8af818e9ed7abe0764c0b47b4240952aa0d179b5b78346c470ac30278d" + ], + "threshold": 1 + }, + "targets": { + "keyids": [ + "65171251a9aff5a8b3143a813481cb07f6e0de4eb197c767837fe4491b739093" + ], + "threshold": 1 + }, + "timestamp": { + "keyids": [ + "8a1c4a3ac2d515dec982ba9910c5fd79b91ae57f625b9cff25d06bf0a61c1758" + ], + "threshold": 1 + } + }, + "spec_version": "1.0.0", + "version": 3 + } +} \ No newline at end of file diff --git a/tests/repository_data/repository/metadata/root_sequence/4.root.json b/tests/repository_data/repository/metadata/root_sequence/4.root.json new file mode 100644 index 0000000000..9db9511791 --- /dev/null +++ b/tests/repository_data/repository/metadata/root_sequence/4.root.json @@ -0,0 +1,87 @@ +{ + "signatures": [ + { + "keyid": "4e777de0d275f9d28588dd9a1606cc748e548f9e22b6795b7cb3f63f98035fcb", + "sig": "3e7d8139549f3ceb25d9eebc870df1eaf47114b098aa1f3e916a9a173278e2ba5e400e228fc6aedb6f346cbdb29ce4246869bc146863bb223d95a3fcc4fe6793ef851ee8d50458b286c1b1b95868e08cd6f5be4b588b6fc45ffc22971326ff47b7a7d32ad42f19ed31e127e3c6f1f477acc29711f68ba774c05aeb436539148e3d5a022234f21d397dccccc336edda1cf36c40101e17509fd655f83a704b0afbf851c56db717965148943684894d0908faf427e800c8e88b0dc7eaa956cd0ca4bd047d4b6e09edc66fd090f520bd56abd14ffc6e0a101d0febb8ce84a9d621bb84a816b2a0f9b0544ba9bdd91e96f314397966624dfe2e2dbb26ff3693c782ea5b81df7868a81db12cf0e968e61096fc1e66ad4b0a9552ce2e16d837da7f69d488bee291520c135d9d22eb3d4b0936a9a7fd2f87dce632b8368a2fdf79308c01085ac97abe7dc57b5d65d05529fb780d9cf3784583acc6aa72b3fa308dff9da1314747cd09be78f43014c8fd2efbd0916daa81f012224c4b80e6adfcea1cf9ef" + } + ], + "signed": { + "_type": "root", + "consistent_snapshot": false, + "expires": "2030-01-01T00:00:00Z", + "keys": { + "4e777de0d275f9d28588dd9a1606cc748e548f9e22b6795b7cb3f63f98035fcb": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "rsa", + "keyval": { + "public": "-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA0GjPoVrjS9eCqzoQ8VRe\nPkC0cI6ktiEgqPfHESFzyxyjC490Cuy19nuxPcJuZfN64MC48oOkR+W2mq4pM51i\nxmdG5xjvNOBRkJ5wUCc8fDCltMUTBlqt9y5eLsf/4/EoBU+zC4SW1iPU++mCsity\nfQQ7U6LOn3EYCyrkH51hZ/dvKC4o9TPYMVxNecJ3CL1q02Q145JlyjBTuM3Xdqsa\nndTHoXSRPmmzgB/1dL/c4QjMnCowrKW06mFLq9RAYGIaJWfM/0CbrOJpVDkATmEc\nMdpGJYDfW/sRQvRdlHNPo24ZW7vkQUCqdRxvnTWkK5U81y7RtjLt1yskbWXBIbOV\nz94GXsgyzANyCT9qRjHXDDz2mkLq+9I2iKtEqaEePcWRu3H6RLahpM/TxFzw684Y\nR47weXdDecPNxWyiWiyMGStRFP4Cg9trcwAGnEm1w8R2ggmWphznCd5dXGhPNjfA\na82yNFY8ubnOUVJOf0nXGg3Edw9iY3xyjJb2+nrsk5f3AgMBAAE=\n-----END PUBLIC KEY-----" + }, + "scheme": "rsassa-pss-sha256" + }, + "59a4df8af818e9ed7abe0764c0b47b4240952aa0d179b5b78346c470ac30278d": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "edcd0a32a07dce33f7c7873aaffbff36d20ea30787574ead335eefd337e4dacd" + }, + "scheme": "ed25519" + }, + "65171251a9aff5a8b3143a813481cb07f6e0de4eb197c767837fe4491b739093": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "89f28bd4ede5ec3786ab923fd154f39588d20881903e69c7b08fb504c6750815" + }, + "scheme": "ed25519" + }, + "8a1c4a3ac2d515dec982ba9910c5fd79b91ae57f625b9cff25d06bf0a61c1758": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "82ccf6ac47298ff43bfa0cd639868894e305a99c723ff0515ae2e9856eb5bbf4" + }, + "scheme": "ed25519" + } + }, + "roles": { + "root": { + "keyids": [ + "4e777de0d275f9d28588dd9a1606cc748e548f9e22b6795b7cb3f63f98035fcb" + ], + "threshold": 1 + }, + "snapshot": { + "keyids": [ + "59a4df8af818e9ed7abe0764c0b47b4240952aa0d179b5b78346c470ac30278d" + ], + "threshold": 1 + }, + "targets": { + "keyids": [ + "65171251a9aff5a8b3143a813481cb07f6e0de4eb197c767837fe4491b739093" + ], + "threshold": 1 + }, + "timestamp": { + "keyids": [ + "8a1c4a3ac2d515dec982ba9910c5fd79b91ae57f625b9cff25d06bf0a61c1758" + ], + "threshold": 1 + } + }, + "spec_version": "1.0.0", + "version": 4 + } +} \ No newline at end of file diff --git a/tests/repository_data/repository/metadata/root_sequence/broken-root.json b/tests/repository_data/repository/metadata/root_sequence/broken-root.json new file mode 100644 index 0000000000..2ff92755ad --- /dev/null +++ b/tests/repository_data/repository/metadata/root_sequence/broken-root.json @@ -0,0 +1,87 @@ +{ + "signatures": [ + { + "keyid": "4e777de0d275f9d28588dd9a1606cc748e548f9e22b6795b7cb3f63f98035fcb", + "sig": "3e7d8139549f3ceb25d9eebc870df1eaf47114b098aa1f3e916a9a173278e2ba5e400e228fc6aedb6f346cbdb29ce4246869bc146863bb223d95a3fcc4fe6793ef851ee8d50458b286c1b1b95868e08cd6f5be4b588b6fc45ffc22971326ff47b7a7d32ad42f19ed31e127e3c6f1f477acc29711f68ba774c05aeb436539148e3d5a022234f21d397dccccc336edda1cf36c40101e17509fd655f83a704b0afbf851c56db717965148943684894d0908faf427e800c8e88b0dc7eaa956cd0ca4bd047d4b6e09edc66fd090f520bd56abd14ffc6e0a101d0febb8ce84a9d621bb84a816b2a0f9b0544ba9bdd91e96f314397966624dfe2e2dbb26ff3693c782ea5b81df7868a81db12cf0e968e61096fc1e66ad4b0a9552ce2e16d837da7f69d488bee291520c135d9d22eb3d4b0936a9a7fd2f87dce632b8368a2fdf79308c01085ac97abe7dc57b5d65d05529fb780d9cf3784583acc6aa72b3fa308dff9da1314747cd09be78f43014c8fd2efbd0916daa81f012224c4b80e6adfcea1cf9ef" + } + ], + "signed": { + "_type": "root", + "consistent_snapshot": false, + "expires": "2030-01-01T00:00:00Z", + "keys": { + "4e777de0d275f9d28588dd9a1606cc748e548f9e22b6795b7cb3f63f98035fcb": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "rsa", + "keyval": { + "public": "-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA0GjPoVrjS9eCqzoQ8VRe\nPkC0cI6ktiEgqPfHESFzyxyjC490Cuy19nuxPcJuZfN64MC48oOkR+W2mq4pM51i\nxmdG5xjvNOBRkJ5wUCc8fDCltMUTBlqt9y5eLsf/4/EoBU+zC4SW1iPU++mCsity\nfQQ7U6LOn3EYCyrkH51hZ/dvKC4o9TPYMVxNecJ3CL1q02Q145JlyjBTuM3Xdqsa\nndTHoXSRPmmzgB/1dL/c4QjMnCowrKW06mFLq9RAYGIaJWfM/0CbrOJpVDkATmEc\nMdpGJYDfW/sRQvRdlHNPo24ZW7vkQUCqdRxvnTWkK5U81y7RtjLt1yskbWXBIbOV\nz94GXsgyzANyCT9qRjHXDDz2mkLq+9I2iKtEqaEePcWRu3H6RLahpM/TxFzw684Y\nR47weXdDecPNxWyiWiyMGStRFP4Cg9trcwAGnEm1w8R2ggmWphznCd5dXGhPNjfA\na82yNFY8ubnOUVJOf0nXGg3Edw9iY3xyjJb2+nrsk5f3AgMBAAE=\n-----END PUBLIC KEY-----" + }, + "scheme": "rsassa-pss-sha256" + }, + "59a4df8af818e9ed7abe0764c0b47b4240952aa0d179b5b78346c470ac30278d": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "edcd0a32a07dce33f7c7873aaffbff36d20ea30787574ead335eefd337e4dacd" + }, + "scheme": "ed25519" + }, + "65171251a9aff5a8b3143a813481cb07f6e0de4eb197c767837fe4491b739093": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "89f28bd4ede5ec3786ab923fd154f39588d20881903e69c7b08fb504c6750815" + }, + "scheme": "ed25519" + }, + "8a1c4a3ac2d515dec982ba9910c5fd79b91ae57f625b9cff25d06bf0a61c1758": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "82ccf6ac47298ff43bfa0cd639868894e305a99c723ff0515ae2e9856eb5bbf4" + }, + "scheme": "ed25519" + } + }, + "roles": { + "root": { + "keyids": [ + "4e777de0d275f9d28588dd9a1606cc748e548f9e22b6795b7cb3f63f98035fcb" + ], + "threshold": 1 + }, + "snapshot": { + "keyids": [ + "59a4df8af818e9ed7abe0764c0b47b4240952aa0d179b5b78346c470ac30278d" + ], + "threshold": 1 + }, + "targets": { + "keyids": [ + "65171251a9aff5a8b3143a813481cb07f6e0de4eb197c767837fe4491b739093" + ], + "threshold": 1 + }, + "timestamp": { + "keyids": [ + "8a1c4a3ac2d515dec982ba9910c5fd79b91ae57f625b9cff25d06bf0a61c1758" + ], + "threshold": 1 + } + }, + "spec_version": "0.0.0", + "version": 4 + } + } \ No newline at end of file diff --git a/tests/test_repository_tool.py b/tests/test_repository_tool.py index 9f955f72a6..5bb26a2f80 100755 --- a/tests/test_repository_tool.py +++ b/tests/test_repository_tool.py @@ -502,6 +502,13 @@ def test_get_filepaths_in_directory(self): self.assertEqual(sorted(expected_files), sorted(metadata_files)) + for i in range(1, 5): + root_filename = str(i) + ".root.json" + expected_files.append(os.path.abspath(os.path.join('repository_data', + 'repository', 'metadata', 'root_sequence', root_filename))) + + expected_files.append(os.path.abspath(os.path.join('repository_data', + 'repository', 'metadata', 'root_sequence', 'broken-root.json'))) # Test when the 'recursive_walk' argument is True. # In this case, recursive walk should yield the same results as the diff --git a/tests/test_updater.py b/tests/test_updater.py index 78a9770cf4..19b93e99ad 100755 --- a/tests/test_updater.py +++ b/tests/test_updater.py @@ -1027,6 +1027,41 @@ def test_5_all_targets(self): + def test_5__chain_of_root_trust(self): + # Test normal cases. + bootstrap_root_path = os.path.join(self.repository_directory, 'metadata', + 'root_sequence', '1.root.json') + self.repository_updater._set_boostrap_root(bootstrap_root_path) + + self.repository_updater._chain_of_root_trust(current_root_version=1) + self.repository_updater._chain_of_root_trust(current_root_version=4) + + # Broken cases + + # Test with a bootstrap root file which cannot be verified. + broken_root = os.path.join(self.repository_directory, 'metadata', + 'root_sequence', 'broken-root.json') + # Give root with unsupported specification version. + self.assertRaises(tuf.exceptions.UnsupportedSpecificationError, + self.repository_updater._set_boostrap_root, broken_root) + + # Test with a current root version lower than bootstrap root version. + self.assertRaises(tuf.exceptions.BadVersionNumberError, + self.repository_updater._chain_of_root_trust, 0) + + # Test with a bootstrap root file which is not a json file. + self.assertRaises(tuf.exceptions.InvalidMetadataJSONError, + self.repository_updater._set_boostrap_root, __file__) + + # Test with a nonexistent path. + # FileNotFoundError is not available on Python2, + # instead use one of its base classes - EnvironmentError. + self.assertRaises(EnvironmentError, + self.repository_updater._set_boostrap_root, 'nonexistent_path') + + # Unset the booststrap root, so it won't be used for other tests. + self.repository_updater._set_boostrap_root(bootstrap_root_path=None) + def test_5_targets_of_role(self):