def test_slow_retrieval_attack(using_tuf=False, mode=None): WAIT_TIME = 60 # Number of seconds to wait until download completes. ERROR_MSG = 'Slow retrieval attack succeeded (using_tuf: '+str(using_tuf)+', mode: '+\ str(mode)+').' # Launch the server. port = random.randint(30000, 45000) command = ['python', 'slow_retrieval_server.py', str(port), mode] server_process = subprocess.Popen(command, stderr=subprocess.PIPE) time.sleep(1) try: # Setup. root_repo, url, server_proc, keyids = \ util_test_tools.init_repo(using_tuf, port=port) reg_repo = os.path.join(root_repo, 'reg_repo') downloads = os.path.join(root_repo, 'downloads') # Add file to 'repo' directory: {root_repo} filepath = util_test_tools.add_file_to_repository(reg_repo, 'A'*30) file_basename = os.path.basename(filepath) url_to_file = url+'reg_repo/'+file_basename downloaded_file = os.path.join(downloads, file_basename) if using_tuf: tuf_repo = os.path.join(root_repo, 'tuf_repo') # Update TUF metadata before attacker modifies anything. util_test_tools.tuf_refresh_repo(root_repo, keyids) # Modify the url. Remember that the interposition will intercept # urls that have 'localhost:9999' hostname, which was specified in # the json interposition configuration file. Look for 'hostname' # in 'util_test_tools.py'. Further, the 'file_basename' is the target # path relative to 'targets_dir'. url_to_file = 'http://localhost:9999/'+file_basename # Client tries to download. # NOTE: if TUF is enabled the metadata files will be downloaded first. proc = Process(target=_download, args=(url_to_file, downloaded_file, using_tuf)) proc.start() proc.join(WAIT_TIME) # In case the process did not exit or successfully exited, we failed. if not proc.exitcode: proc.terminate() raise SlowRetrievalAttackAlert(ERROR_MSG) finally: server_process.kill() util_test_tools.cleanup(root_repo, server_proc)
def setUp(self): unittest.TestCase.setUp(self) # Ensure the keystore is empty prior to initializing the repository # generated by 'util_test_tools'. tuf.repo.keystore.clear_keystore() # Unpacking necessary parameters returned from init_repo() essential_params = util_test_tools.init_repo(using_tuf=True) self.root_repo = essential_params[0] self.url = essential_params[1] self.server_proc = essential_params[2] self.keyids = essential_params[3]
def test_endless_data_attack(using_tuf=False, TIMESTAMP=False): """ <Purpose> Illustrate endless data attack vulnerability. <Arguments> using_tuf: If set to 'False' all directories that start with 'tuf_' are ignored, indicating that tuf is not implemented. """ ERROR_MSG = 'Endless Data Attack was Successful!\n' try: # Setup. root_repo, url, server_proc, keyids = util_test_tools.init_repo( using_tuf) reg_repo = os.path.join(root_repo, 'reg_repo') tuf_repo = os.path.join(root_repo, 'tuf_repo') downloads = os.path.join(root_repo, 'downloads') tuf_targets = os.path.join(tuf_repo, 'targets') # Original data. INTENDED_DATA = 'Test A' # Add a file to 'repo' directory: {root_repo} filepath = util_test_tools.add_file_to_repository( reg_repo, INTENDED_DATA) file_basename = os.path.basename(filepath) url_to_repo = url + 'reg_repo/' + file_basename downloaded_file = os.path.join(downloads, file_basename) # We do not deliver truly endless data, but we will extend the original # file by many bytes. noisy_data = 'X' * 100000 if using_tuf: # Update TUF metadata before attacker modifies anything. util_test_tools.tuf_refresh_repo(root_repo, keyids) # Modify the url. Remember that the interposition will intercept # urls that have 'localhost:9999' hostname, which was specified in # the json interposition configuration file. Look for 'hostname' # in 'util_test_tools.py'. Further, the 'file_basename' is the target # path relative to 'targets_dir'. url_to_repo = 'http://localhost:9999/' + file_basename # Attacker modifies the file at the targets repository. target = os.path.join(tuf_targets, file_basename) original_data = util_test_tools.read_file_content(target) larger_original_data = original_data + noisy_data util_test_tools.modify_file_at_repository(target, larger_original_data) # Attacker modifies the timestamp.txt metadata. if TIMESTAMP: metadata = os.path.join(tuf_repo, 'metadata') timestamp = os.path.join(metadata, 'timestamp.txt') original_data = util_test_tools.read_file_content(timestamp) larger_original_data = original_data + noisy_data util_test_tools.modify_file_at_repository( timestamp, larger_original_data) # Attacker modifies the file at the regular repository. original_data = util_test_tools.read_file_content(filepath) larger_original_data = original_data + noisy_data util_test_tools.modify_file_at_repository(filepath, larger_original_data) # End Setup. # Client downloads (tries to download) the file. try: _download(url_to_repo, downloaded_file, using_tuf) except Exception, exception: # Because we are extending the true timestamp TUF metadata with invalid # JSON, we except to catch an error about invalid metadata JSON. if using_tuf and TIMESTAMP: endless_data_attack = False for mirror_url, mirror_error in exception.mirror_errors.iteritems( ): if isinstance(mirror_error, tuf.InvalidMetadataJSONError): endless_data_attack = True break # In case we did not detect what was likely an endless data attack, we # reraise the exception to indicate that endless data attack detection # failed. if not endless_data_attack: raise else: raise # When we test downloading "endless" timestamp with TUF, we want to skip # the following test because downloading the timestamp should have failed. if not (using_tuf and TIMESTAMP): # Check whether the attack succeeded by inspecting the content of the # update. The update should contain 'Test A'. Technically it suffices # to check whether the file was downloaded or not. downloaded_content = util_test_tools.read_file_content( downloaded_file) if downloaded_content != INTENDED_DATA: raise EndlessDataAttack(ERROR_MSG)
def test_arbitrary_package_attack(using_tuf=False): """ <Purpose> Illustrate arbitrary package attack vulnerability. <Arguments> using_tuf: If set to 'False' all directories that start with 'tuf_' are ignored, indicating that tuf is not implemented. """ ERROR_MSG = 'Arbitrary Package Attack was Successful!' try: # Setup. root_repo, url, server_proc, keyids = util_test_tools.init_repo(using_tuf) reg_repo = os.path.join(root_repo, 'reg_repo') tuf_repo = os.path.join(root_repo, 'tuf_repo') downloads = os.path.join(root_repo, 'downloads') targets_dir = os.path.join(tuf_repo, 'targets') # Add a file to 'repo' directory: {root_repo} filepath = util_test_tools.add_file_to_repository(reg_repo, 'Test A') file_basename = os.path.basename(filepath) url_to_repo = url+'reg_repo/'+file_basename downloaded_file = os.path.join(downloads, file_basename) if using_tuf: # Update TUF metadata before attacker modifies anything. util_test_tools.tuf_refresh_repo(root_repo, keyids) # Modify the url. Remember that the interposition will intercept # urls that have 'localhost:9999' hostname, which was specified in # the json interposition configuration file. Look for 'hostname' # in 'util_test_tools.py'. Further, the 'file_basename' is the target # path relative to 'targets_dir'. url_to_repo = 'http://localhost:9999/'+file_basename # Attacker modifies the file at the targets repository. target = os.path.join(targets_dir, file_basename) util_test_tools.modify_file_at_repository(target, 'Evil A') # Attacker modifies the file at the regular repository. util_test_tools.modify_file_at_repository(filepath, 'Evil A') # End of Setup. try: # Client downloads (tries to download) the file. _download(url_to_repo, downloaded_file, using_tuf) except tuf.NoWorkingMirrorError, error: # We only set up one mirror, so if it fails, we expect a # NoWorkingMirrorError. If TUF has worked as intended, the mirror error # contained within should be a BadHashError. mirror_error = error.mirror_errors[url+'tuf_repo/targets/'+file_basename] assert isinstance(mirror_error, tuf.BadHashError) else:
def test_indefinite_freeze_attack(using_tuf=False): """ <Arguments> using_tuf: If set to 'False' all directories that start with 'tuf_' are ignored, indicating that tuf is not implemented. The idea here is to expire timestamp metadata so that the attacker """ ERROR_MSG = '\tIndefinite Freeze Attack was Successful!\n\n' try: # Setup. root_repo, url, server_proc, keyids = util_test_tools.init_repo( using_tuf) reg_repo = os.path.join(root_repo, 'reg_repo') tuf_repo = os.path.join(root_repo, 'tuf_repo') metadata_dir = os.path.join(tuf_repo, 'metadata') downloads = os.path.join(root_repo, 'downloads') # Add file to 'repo' directory: {root_repo} filepath = util_test_tools.add_file_to_repository(reg_repo, 'Test A') file_basename = os.path.basename(filepath) url_to_repo = url + 'reg_repo/' + file_basename downloaded_file = os.path.join(downloads, file_basename) if using_tuf: print('TUF ...') # Update TUF metadata before attacker modifies anything. util_test_tools.tuf_refresh_repo(root_repo, keyids) # Modify the url. Remember that the interposition will intercept # urls that have 'localhost:9999' hostname, which was specified in # the json interposition configuration file. Look for 'hostname' # in 'util_test_tools.py'. Further, the 'file_basename' is the target # path relative to 'targets_dir'. url_to_repo = 'http://localhost:9999/' + file_basename # Make timestamp metadata with close expiration date (2s). _remake_timestamp(metadata_dir, keyids) # Client performs initial download. If the computer is slow, it may # take longer time than expiration time. In this case you will see # an ExpiredMetadataError. try: _download(url_to_repo, downloaded_file, using_tuf) except: print('Initial download failed! It may be because your machine is '+ \ 'busy. Try again later.') else: # Expire timestamp. time.sleep(EXPIRATION) # Try downloading again, this should raise an error. try: _download(url_to_repo, downloaded_file, using_tuf) except tuf.ExpiredMetadataError, error: print('Caught an expiration error!') else:
def test_replay_attack(using_tuf=False): """ <Arguments> using_tuf: If set to 'False' all directories that start with 'tuf_' are ignored, indicating that tuf is not implemented. <Purpose> Illustrate replay attack vulnerability. """ ERROR_MSG = '\tReplay Attack was Successful!\n\n' FIRST_CONTENT = 'Test A' SECOND_CONTENT = 'Test B' try: # Setup. root_repo, url, server_proc, keyids = \ util_test_tools.init_repo(using_tuf=using_tuf) reg_repo = os.path.join(root_repo, 'reg_repo') tuf_repo = os.path.join(root_repo, 'tuf_repo') tuf_repo_copy = os.path.join(root_repo, 'tuf_repo_copy') downloads = os.path.join(root_repo, 'downloads') tuf_targets = os.path.join(tuf_repo, 'targets') # Add file to 'repo' directory: {root_repo} filepath = util_test_tools.add_file_to_repository( reg_repo, FIRST_CONTENT) file_basename = os.path.basename(filepath) url_to_repo = url + 'reg_repo/' + file_basename downloaded_file = os.path.join(downloads, file_basename) # Attacker saves the original file into 'evil_dir'. evil_dir = tempfile.mkdtemp(dir=root_repo) original_file = os.path.join(evil_dir, file_basename) shutil.copy(filepath, evil_dir) if using_tuf: # Update TUF metadata before attacker modifies anything. util_test_tools.tuf_refresh_repo(root_repo, keyids) # Copy the first version of the repository for replay later. shutil.copytree(tuf_repo, tuf_repo_copy) # Modify the url. Remember that the interposition will intercept # urls that have 'localhost:9999' hostname, which was specified in # the json interposition configuration file. Look for 'hostname' # in 'util_test_tools.py'. Further, the 'file_basename' is the target # path relative to 'targets_dir'. url_to_repo = 'http://localhost:9999/' + file_basename # End of Setup. # Client performs initial update. _download(url=url_to_repo, filename=downloaded_file, using_tuf=using_tuf) # Downloads are stored in the same directory '{root_repo}/downloads/' # for regular and tuf clients. downloaded_content = util_test_tools.read_file_content(downloaded_file) if FIRST_CONTENT != downloaded_content: raise TestSetupError( '[Initial Update] Failed to download the file.') # Developer patches the file and updates the repository. util_test_tools.modify_file_at_repository(filepath, SECOND_CONTENT) # Updating tuf repository. This will copy files from regular repository # into tuf repository and refresh the metadata if using_tuf: util_test_tools.tuf_refresh_repo(root_repo, keyids) # Client downloads the patched file. _download(url=url_to_repo, filename=downloaded_file, using_tuf=using_tuf) # Content of the downloaded file. downloaded_content = util_test_tools.read_file_content(downloaded_file) if SECOND_CONTENT != downloaded_content: raise TestSetupError('[Update] Failed to update the file.') # Attacker tries to be clever, he manages to modifies regular and tuf # targets directory by replacing a patched file with an old one. if using_tuf: # Delete the current TUF repository... shutil.rmtree(tuf_repo) # ...and replace it with a previous copy. shutil.move(tuf_repo_copy, tuf_repo) else: # Delete the current file... util_test_tools.delete_file_at_repository(filepath) # ...and replace it with a previous copy. shutil.copy(original_file, reg_repo) try: # Client downloads the file once more. _download(url=url_to_repo, filename=downloaded_file, using_tuf=using_tuf) except tuf.NoWorkingMirrorError, exception: replayed_metadata_attack = False for mirror_url, mirror_error in exception.mirror_errors.iteritems( ): if isinstance(mirror_error, tuf.ReplayedMetadataError): replayed_metadata_attack = True break # In case we did not detect what was likely a replayed metadata attack, # we reraise the exception to indicate that replayed metadata attack # detection failed. if not replayed_metadata_attack: raise else:
def test_mix_and_match_attack(using_tuf=False): """ Attack design: There are 3 stages: Stage 1: Consists of a usual mode of operations using tuf. Client downloads a target file. (Initial download) Stage 2: The target file is legitimately modified and metadata correctly updated. Client downloads the target file again. (Patched target download) Stage 3: The target file is legitimately modified and metadata correctly updated again. However, before client gets to download the newly patched target file the attacker replaces the release metadata, targets metadata and the target file with the ones from stage 1 (mix-and-match attack). Note that timestamp metadata is untouched. Further note that same would happen if only target metadata, and target file are reverted. """ ERROR_MSG = '\tMix-And-Match Attack was Successful!\n\n' try: # Setup / Stage 1 # --------------- root_repo, url, server_proc, keyids = util_test_tools.init_repo(using_tuf) reg_repo = os.path.join(root_repo, 'reg_repo') downloads = os.path.join(root_repo, 'downloads') evil_dir = tempfile.mkdtemp(dir=root_repo) # Add file to 'repo' directory: {root_repo} filepath = util_test_tools.add_file_to_repository(reg_repo, 'A'*10) file_basename = os.path.basename(filepath) url_to_file = url+'reg_repo/'+file_basename downloaded_file = os.path.join(downloads, file_basename) # Attacker saves the initial file. shutil.copy(filepath, evil_dir) unpatched_file = os.path.join(evil_dir, file_basename) if using_tuf: print('TUF ...') tuf_repo = os.path.join(root_repo, 'tuf_repo') tuf_targets = os.path.join(tuf_repo, 'targets') metadata_dir = os.path.join(tuf_repo, 'metadata') release_meta_file = os.path.join(metadata_dir, 'release.txt') targets_meta_file = os.path.join(metadata_dir, 'targets.txt') target = os.path.join(tuf_targets, file_basename) # Update TUF metadata before attacker modifies anything. util_test_tools.tuf_refresh_repo(root_repo, keyids) # Attacker saves the original metadata and the target file. #shutil.copy(target, evil_dir) shutil.copy(release_meta_file, evil_dir) shutil.copy(targets_meta_file, evil_dir) #target_old = os.path.join(evil_dir, file_basename) release_meta_file_old = os.path.join(evil_dir, 'release.txt') targets_meta_file_old = os.path.join(evil_dir, 'targets.txt') # Modify the url. Remember that the interposition will intercept # urls that have 'localhost:9999' hostname, which was specified in # the json interposition configuration file. Look for 'hostname' # in 'util_test_tools.py'. Further, the 'file_basename' is the target # path relative to 'targets_dir'. url_to_file = 'http://localhost:9999/'+file_basename # Wait for some time to let program set up local http server time.sleep(1) # Client's initial download. _download(url_to_file, downloaded_file, using_tuf) # Stage 2 # ------- # Developer patches the file and updates the repository. util_test_tools.modify_file_at_repository(filepath, 'B'*11) # Updating tuf repository. This will copy files from regular repository # into tuf repository and refresh the metadata if using_tuf: util_test_tools.tuf_refresh_repo(root_repo, keyids) # Client downloads the patched file. _download(url_to_file, downloaded_file, using_tuf) downloaded_content = util_test_tools.read_file_content(downloaded_file) # Stage 3 # ------- # Developer patches the file and updates the repository again. util_test_tools.modify_file_at_repository(filepath, 'C'*10) # Updating tuf repository. This will copy files from regular repository # into tuf repository and refresh the metadata if using_tuf: util_test_tools.tuf_refresh_repo(root_repo, keyids) # Attacker replaces the metadata and the target file. shutil.copyfile(unpatched_file, target) shutil.copyfile(release_meta_file_old, release_meta_file) shutil.copyfile(targets_meta_file_old, targets_meta_file) # Attacker replaces the patched file with the unpatched one. shutil.copyfile(unpatched_file, filepath) # Client tries to downloads the newly patched file. try: _download(url_to_file, downloaded_file, using_tuf) except tuf.NoWorkingMirrorError as errors: for mirror_url, mirror_error in errors.mirror_errors.iteritems(): if type(mirror_error) == tuf.BadHashError: print('Caught a Bad Hash Error!') # Check whether the attack succeeded by inspecting the content of the # update. The update should contain 'Test NOT A'. downloaded_content = util_test_tools.read_file_content(downloaded_file) if ('B'*11) != downloaded_content: raise MixAndMatchAttackAlert(ERROR_MSG) finally: util_test_tools.cleanup(root_repo, server_proc)
def test_extraneous_dependency_attack(using_tuf=False): """ <Purpose> Illustrate extraneous dependency attack vulnerability. <Arguments> using_tuf: If set to 'False' all directories that start with 'tuf_' are ignored, indicating that tuf is not implemented. """ ERROR_MSG = 'Extraneous Dependency Attack was Successful!' try: # Setup. root_repo, url, server_proc, keyids = util_test_tools.init_repo(using_tuf) reg_repo = os.path.join(root_repo, 'reg_repo') tuf_repo = os.path.join(root_repo, 'tuf_repo') downloads = os.path.join(root_repo, 'downloads') targets_dir = os.path.join(tuf_repo, 'targets') # Add files to 'repo' directory: {root_repo}. good_dependency_filepath = util_test_tools.add_file_to_repository(reg_repo, 'the file you need') good_dependency_basename = os.path.basename(good_dependency_filepath) bad_dependency_filepath = util_test_tools.add_file_to_repository(reg_repo, 'the file you don\'t need') bad_dependency_basename = os.path.basename(bad_dependency_filepath) # The dependent file lists the good dependency. dependent_filepath = util_test_tools.add_file_to_repository(reg_repo, 'requires:'+good_dependency_basename) dependent_basename = os.path.basename(dependent_filepath) url_to_repo = url+'reg_repo/'+dependent_basename # List the bad dependency first. If an attacker modifies a target by # simply appending the file contents, tuf.download will ignore the appended # data, downloading only as much data as the TUF metadata says the target # should contain. modified_dependency_list = bad_dependency_basename+','+\ good_dependency_basename if using_tuf: # Update TUF metadata before attacker modifies anything. util_test_tools.tuf_refresh_repo(root_repo, keyids) # Modify the url. Remember that the interposition will intercept # urls that have 'localhost:9999' hostname, which was specified in # the json interposition configuration file. Look for 'hostname' # in 'util_test_tools.py'. Further, the 'file_basename' is the target # path relative to 'targets_dir'. url_to_repo = 'http://localhost:9999/'+dependent_basename # Attacker modifies the depenent file in the targets repository, adding # the bad dependency to its list. dependent_target_filepath = os.path.join(targets_dir, dependent_basename) util_test_tools.modify_file_at_repository(dependent_target_filepath, 'requires:'+modified_dependency_list) # Attacker modifies the depenent file in the regular repository, adding # the bad dependency to its list. util_test_tools.modify_file_at_repository(dependent_filepath, 'requires:'+modified_dependency_list) # End of Setup. try: # Client downloads (tries to download) the file. _download(url_to_repo, dependent_basename, downloads, using_tuf) except tuf.NoWorkingMirrorError, error: # We only set up one mirror, so if it fails, we expect a # NoWorkingMirrorError. If TUF has worked as intended, the mirror error # contained within should be a BadHashError. mirror_error = \ error.mirror_errors[url+'tuf_repo/targets/'+dependent_basename] assert isinstance(mirror_error, tuf.BadHashError) else:
def setUp(self): """ The target delegations tree is fixed as such: targets -> [T1, T2] T1 -> [T3] """ global version version = version+1 expiration = tuf.formats.format_time(time.time()+86400) root_repo, url, server_proc, keyids = util_test_tools.init_repo(using_tuf=True) # Server side repository. tuf_repo = os.path.join(root_repo, 'tuf_repo') keystore_dir = os.path.join(tuf_repo, 'keystore') metadata_dir = os.path.join(tuf_repo, 'metadata') targets_dir = os.path.join(tuf_repo, 'targets') # We need to provide clients with a way to reach the tuf repository. tuf_repo_relpath = os.path.basename(tuf_repo) tuf_url = url+tuf_repo_relpath # Add files to the server side repository. # target1 = 'targets_dir/[random].txt' # target2 = 'targets_dir/[random].txt' add_target = util_test_tools.add_file_to_repository target1_path = add_target(targets_dir, data='target1') target2_path = add_target(targets_dir, data='target2') # Target paths relative to the 'targets_dir'. # Ex: targetX = 'targets/delegator/delegatee.txt' target1 = os.path.relpath(target1_path, tuf_repo) target2 = os.path.relpath(target2_path, tuf_repo) # Relative to repository's targets directory. target_filepaths = [target1, target2] # Store in self only the variables relevant for tests. self.root_repo = root_repo self.tuf_repo = tuf_repo self.server_proc = server_proc self.target_filepaths = target_filepaths # Targets delegated from A to B. self.delegated_targets = {} # Targets actually signed by B. self.signed_targets = {} self.mirrors = { "mirror1": { "url_prefix": tuf_url, "metadata_path": "metadata", "targets_path": "targets", "confined_target_dirs": [""] } } # Aliases for targets roles. self.T0 = 'targets' self.T1 = 'targets/T1' self.T2 = 'targets/T2' self.T3 = 'targets/T1/T3' # Get tracked and assigned targets, and generate targets metadata. self.make_targets_metadata() assert hasattr(self, 'T0_metadata') assert hasattr(self, 'T1_metadata') assert hasattr(self, 'T2_metadata') assert hasattr(self, 'T3_metadata') # Make delegation directories at the server's repository. metadata_targets_dir = os.path.join(metadata_dir, 'targets') metadata_T1_dir = os.path.join(metadata_targets_dir, 'T1') os.makedirs(metadata_T1_dir) # Delegations metadata paths for the 3 delegated targets roles. T0_path = os.path.join(metadata_dir, 'targets.txt') T1_path = os.path.join(metadata_targets_dir, 'T1.txt') T2_path = os.path.join(metadata_targets_dir, 'T2.txt') T3_path = os.path.join(metadata_T1_dir, 'T3.txt') # Generate RSA keys for the 3 delegatees. key1 = signerlib.generate_and_save_rsa_key(keystore_dir, 'T1') key2 = signerlib.generate_and_save_rsa_key(keystore_dir, 'T2') key3 = signerlib.generate_and_save_rsa_key(keystore_dir, 'T3') # ID for each of the 3 keys. key1_id = key1['keyid'] key2_id = key2['keyid'] key3_id = key3['keyid'] # ID, in a list, for each of the 3 keys. key1_ids = [key1_id] key2_ids = [key2_id] key3_ids = [key3_id] # Public-key JSON for each of the 3 keys. key1_val = tuf.rsa_key.create_in_metadata_format(key1['keyval']) key2_val = tuf.rsa_key.create_in_metadata_format(key2['keyval']) key3_val = tuf.rsa_key.create_in_metadata_format(key3['keyval']) # Create delegation role metadata for each of the 3 delegated targets roles. make_role_metadata = tuf.formats.make_role_metadata T1_targets = self.relpath_from_targets(self.delegated_targets[self.T1]) T1_role = make_role_metadata(key1_ids, 1, name=self.T1, paths=T1_targets) T2_targets = self.relpath_from_targets(self.delegated_targets[self.T2]) T2_role = make_role_metadata(key2_ids, 1, name=self.T2, paths=T2_targets) T3_targets = self.relpath_from_targets(self.delegated_targets[self.T3]) T3_role = make_role_metadata(key3_ids, 1, name=self.T3, paths=T3_targets) # Assign 'delegations' object for 'targets': self.T0_metadata['signed']['delegations'] = { 'keys': {key1_id: key1_val, key2_id: key2_val}, 'roles': [T1_role, T2_role] } # Assign 'delegations' object for 'targets/T1': self.T1_metadata['signed']['delegations'] = { 'keys': {key3_id: key3_val}, 'roles': [T3_role] } sign = signerlib.sign_metadata write = signerlib.write_metadata_file # Sign new metadata objects. T0_signable = sign(self.T0_metadata, keyids, T0_path) T1_signable = sign(self.T1_metadata, key1_ids, T1_path) T2_signable = sign(self.T2_metadata, key2_ids, T2_path) T3_signable = sign(self.T3_metadata, key3_ids, T3_path) # Save new metadata objects. write(T0_signable, T0_path) write(T1_signable, T1_path) write(T2_signable, T2_path) write(T3_signable, T3_path) # Timestamp a new release to reflect latest targets. signerlib.build_release_file(keyids, metadata_dir, version, expiration) signerlib.build_timestamp_file(keyids, metadata_dir, version, expiration) # Unload all keys. keystore.clear_keystore()