コード例 #1
0
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)
コード例 #2
0
ファイル: test_util_test_tools.py プロジェクト: nekt/tuf
    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]
コード例 #3
0
ファイル: test_endless_data_attack.py プロジェクト: nekt/tuf
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)
コード例 #4
0
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:
コード例 #5
0
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:
コード例 #6
0
ファイル: test_replay_attack.py プロジェクト: nekt/tuf
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:
コード例 #7
0
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)
コード例 #8
0
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:
コード例 #9
0
  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()