From c0d5d2cbb5ef80c4552cbcc035b5efd3cdb6c5b2 Mon Sep 17 00:00:00 2001 From: Kon Date: Sun, 3 Mar 2013 03:47:36 -0500 Subject: [PATCH] Modified test_replay_attack.py, util_test_tools.py and test_util_test_tools.py. --- tuf/interposition.py | 2 +- tuf/tests/system_tests/test_replay_attack.py | 187 ++++++------ .../system_tests/test_util_test_tools.py | 65 ++-- tuf/tests/system_tests/util_test_tools.py | 280 +++++++----------- 4 files changed, 242 insertions(+), 292 deletions(-) diff --git a/tuf/interposition.py b/tuf/interposition.py index 34c9e2a0..1b31361d 100644 --- a/tuf/interposition.py +++ b/tuf/interposition.py @@ -546,6 +546,6 @@ def wrapper( self, *args, **kwargs ): ############################## GLOBAL VARIABLES ################################ -# We use False as a sentinal value. +# We use False as a sentinel value. _previous_urllib_urlopener = False _previous_urllib2_opener = False diff --git a/tuf/tests/system_tests/test_replay_attack.py b/tuf/tests/system_tests/test_replay_attack.py index 1235eba7..5db7efa6 100755 --- a/tuf/tests/system_tests/test_replay_attack.py +++ b/tuf/tests/system_tests/test_replay_attack.py @@ -15,10 +15,17 @@ Simulate a replay attack. A simple client update vs. client update implementing TUF. +Note: The interposition provided by 'tuf.interposition' is used to intercept +all calls made by urllib/urillib2 to certain network locations specified in +the interposition configuration file. Look up interposition.py for more +information and illustration of a sample contents of the interposition +configuration file. Interposition was meant to make TUF integration with an +existing software updater an easy process. This allows for more flexibility +to the existing software updater. However, if you are planning to solely use +TUF there should be no need for interposition, all necessary calls will be +generated from within TUF. Note: There is no difference between 'updates' and 'target' files. -Note: If TUF is implemented - you would NOT use urllib like it's done here -for the testing purposes. TUF handles the downloads. """ @@ -28,12 +35,20 @@ import tempfile import util_test_tools +import tuf.interposition -def test_replay_attack(tuf=False): +class TestSetupError(Exception): + pass + +class ReplayAttackError(Exception): + pass + + +def test_replay_attack(TUF=False): """ - tuf: + TUF: If set to 'False' all directories that start with 'tuf_' are ignored, indicating that tuf is not implemented. @@ -42,112 +57,100 @@ def test_replay_attack(tuf=False): """ - # Setup. - temp_root, url = util_test_tools.init_repo(tuf=tuf) - repo = os.path.join(temp_root, 'repo') - tuf_repo = os.path.join(temp_root, 'tuf_repo') - downloads =os.path.join(temp_root, 'downloads') + try: + # Setup. + root_repo, url, server_proc, keyids, interpose_json = \ + util_test_tools.init_repo(tuf=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') - # Add file to 'repo' directory: {temp_root} - filepath = util_test_tools.add_file_to_repository('Test A') - file_basename = os.path.basename(filepath) - url_to_repo = url+'repo/'+file_basename - downloaded_file = os.path.join(downloads, file_basename) + # 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) + # Attacker saves the original file into 'evil_dir'. + evil_dir = tempfile.mkdtemp(dir=root_repo) + vulnerable_file = os.path.join(evil_dir, file_basename) + shutil.copy(filepath, evil_dir) - # Client performs initial update. - if tuf: - util_test_tools.tuf_refresh_and_download() - else: + # Refresh the tuf repository and apply tuf interpose. + if TUF: + util_test_tools.tuf_refresh_repo(root_repo, keyids) + tuf.interposition.configure(interpose_json) + tuf.interposition.interpose() + + # End Setup. + + # Client performs initial update. urllib.urlretrieve(url_to_repo, downloaded_file) - # Content of the downloaded file. - # Downloads are stored in the same directory '{temp_root}/downloads/' - # independent of who stores there (tuf or regular client). See warning - # in util_test_tools.init_repo(). - downloaded_content = util_test_tools.read_file_content(downloaded_file) - msg = '[Initial Updata] Failed to download the file.' - assert 'Test A' == downloaded_content, msg + # Content of the downloaded file. + # Downloads are stored in the same directory '{root_repo}/downloads/' + # independent of who stores there (tuf or regular client). See warning + # in util_test_tools.init_repo(). + downloaded_content = util_test_tools.read_file_content(downloaded_file) + msg = '[Initial Updata] Failed to download the file.' + if 'Test A' != downloaded_content: + raise TestSetupError(msg) + # Developer patches the file and updates the repository. + util_test_tools.modify_file_at_repository(filepath, 'Test NOT A') + if TUF: + util_test_tools.tuf_refresh_repo(root_repo, keyids) - # Attacker finds a vulnerability in the file. - evil_dir = tempfile.mkdtemp(dir=temp_root) - vulnerable_file = os.path.join(evil_dir, file_basename) - urllib.urlretrieve(url_to_repo, vulnerable_file) - - # Developer patches the file and updates the repository. - util_test_tools.modify_file_at_repository(filepath, 'Test NOT A') - - - # Client downloads the patched file. - if tuf: - util_test_tools.tuf_refresh_and_download() - else: + # Client downloads the patched file. urllib.urlretrieve(url_to_repo, downloaded_file) - # Content of the downloaded file. - downloaded_content = util_test_tools.read_file_content(downloaded_file) - msg = '[Updata] Failed to update the file.' - assert 'Test NOT A' == downloaded_content, msg + # Content of the downloaded file. + downloaded_content = util_test_tools.read_file_content(downloaded_file) + msg = '[Update] Failed to update the file.' + if 'Test NOT A' != downloaded_content: + raise TestSetupError(msg) + # Attacker tries to be clever, he manages to modifies regular and tuf + # targets directory by replacing a patched file with an old one. + if os.path.isdir(tuf_targets): + target = os.path.join(tuf_targets, file_basename) + util_test_tools.delete_file_at_repository(target) + shutil.copy(vulnerable_file, tuf_targets) + # Verify that 'target' is an old, un-patched file. + target = os.path.join(tuf_targets, file_basename) + target_content = util_test_tools.read_file_content(target) + msg = "The 'target' file contains new data!" + if 'Test A' != target_content: + raise TestSetupError(msg) + else: + util_test_tools.delete_file_at_repository(filepath) + shutil.copy(vulnerable_file, reg_repo) - # Attacker tries to be clever, he manages to modifies tuf targets directory - # by replacing a patched file with an old one. - # - # Since we don't really have any restriction where regular download - # retrieves the files from, this works fine. On the other hand, when - # tuf is used this will guarantee that tuf-client will be retrieving the - # attacker's file. This happens, because mirror's list is pointing to - # the tuf repository. - # - # If tuf is False none of the tuf directories are created, but attacker - # needs tuf targets directory in order to be able to attack both tuf and - # non-tuf clients. For this purpose he creates an artificial tuf targets - # directory (Remember: the tuf is not setup at this point!). - targets_dir = os.path.join(tuf_repo, 'targets') - if not os.path.isdir(targets_dir): - os.makedirs(targets_dir) - shutil.copy(vulnerable_file, targets_dir) - url_to_tuf = url+'tuf_repo/targets/'+file_basename + # Client downloads the file once time. + urllib.urlretrieve(url_to_repo, downloaded_file) - # Verify that 'target' is an old, un-patched file. - target = os.path.join(targets_dir, file_basename) - target_content = util_test_tools.read_file_content(target) - msg = 'The \'target\' file contains new data!' - assert 'Test A' == target_content, msg + # 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) + msg = 'Replay attack was successful!\n' + if 'Test NOT A' != downloaded_content: + raise ReplayAttackError(msg) - - # Client downloads the file once time. - if tuf: - util_test_tools.tuf_refresh_and_download() - else: - urllib.urlretrieve(url_to_tuf, downloaded_file) - - # 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) - msg = 'Replay attack was successful!\n' - assert 'Test NOT A' == downloaded_content, msg + finally: + tuf.interposition.go_away() + util_test_tools.cleanup(root_repo, server_proc) try: - test_replay_attack(tuf=False) -except AssertionError, e: - print 'Expected Failure: '+repr(e) -else: - print 'Unexpected Failure!' -finally: - util_test_tools.cleanup() - + test_replay_attack(TUF=False) +except ReplayAttackError, err: + print err try: - test_replay_attack(tuf=True) -except AssertionError, e: - print 'Unexpected Failure: '+repr(e) -else: - print 'Expected Success!' -finally: - util_test_tools.cleanup() \ No newline at end of file + test_replay_attack(TUF=True) +except ReplayAttackError, err: + print err \ No newline at end of file diff --git a/tuf/tests/system_tests/test_util_test_tools.py b/tuf/tests/system_tests/test_util_test_tools.py index d358e91e..9d0af14f 100644 --- a/tuf/tests/system_tests/test_util_test_tools.py +++ b/tuf/tests/system_tests/test_util_test_tools.py @@ -25,11 +25,18 @@ class test_UtilTestTools(unittest.TestCase): def setUp(self): unittest.TestCase.setUp(self) - self.temp_root, self.url = util_test_tools.init_repo(tuf=True) + + # Unpacking necessary parameters returned from init_repo() + essential_params = util_test_tools.init_repo(tuf=True) + self.root_repo = essential_params[0] + self.url = essential_params[1] + self.server_proc = essential_params[2] + self.keyids = essential_params[3] + self.interpose_json = essential_params[4] def tearDown(self): unittest.TestCase.tearDown(self) - util_test_tools.cleanup() + util_test_tools.cleanup(self.root_repo, self.server_proc) #================================================# @@ -40,14 +47,15 @@ def tearDown(self): # A few quick internal tests to see if everything runs smoothly. def test_direct_download(self): # Setup. - downloads = os.path.join(self.temp_root, 'downloads') - filepath = util_test_tools.add_file_to_repository('Test') + reg_repo = os.path.join(self.root_repo, 'reg_repo') + downloads = os.path.join(self.root_repo, 'downloads') + filepath = util_test_tools.add_file_to_repository(reg_repo, 'Test') file_basename = os.path.basename(filepath) - url = self.url+'repo/'+file_basename + url_to_reg_repo = self.url+'reg_repo/'+file_basename downloaded_file = os.path.join(downloads, file_basename) # Test direct download using 'urllib.urlretrieve'. - urllib.urlretrieve(url, downloaded_file) + urllib.urlretrieve(url_to_reg_repo, downloaded_file) self.assertTrue(os.path.isfile(downloaded_file)) # Verify the content of the downloaded file. @@ -59,18 +67,18 @@ def test_direct_download(self): def test_correct_directory_structure(self): - # Verify following directories exists: '{temp_root}/repo/', - # '{temp_root}/downloads/. - self.assertTrue(os.path.isdir(os.path.join(self.temp_root, 'repo'))) - self.assertTrue(os.path.isdir(os.path.join(self.temp_root, 'downloads'))) + # Verify following directories exists: '{root_repo}/reg_repo/', + # '{root_repo}/downloads/. + self.assertTrue(os.path.isdir(os.path.join(self.root_repo, 'reg_repo'))) + self.assertTrue(os.path.isdir(os.path.join(self.root_repo, 'downloads'))) # Verify that all necessary TUF-paths exist. - tuf_repo = os.path.join(self.temp_root, 'tuf_repo') - tuf_client = os.path.join(self.temp_root, 'tuf_client') + tuf_repo = os.path.join(self.root_repo, 'tuf_repo') + tuf_client = os.path.join(self.root_repo, 'tuf_client') metadata_dir = os.path.join(tuf_repo, 'metadata') current_dir = os.path.join(tuf_client, 'metadata', 'current') - # Verify '{temp_root}/tuf_repo/metadata/role.txt' paths exists. + # Verify '{root_repo}/tuf_repo/metadata/role.txt' paths exists. for role in ['root', 'targets', 'release', 'timestamp']: # Repository side. role_file = os.path.join(metadata_dir, role+'.txt') @@ -80,11 +88,11 @@ def test_correct_directory_structure(self): role_file = os.path.join(current_dir, role+'.txt') self.assertTrue(os.path.isfile(role_file)) - # Verify '{temp_root}/tuf_repo/keystore/keyid.key' exists. + # Verify '{root_repo}/tuf_repo/keystore/keyid.key' exists. keys_list = os.listdir(os.path.join(tuf_repo, 'keystore')) self.assertEquals(len(keys_list), 1) - # Verify '{temp_root}/tuf_repo/targets/' directory exists. + # Verify '{root_repo}/tuf_repo/targets/' directory exists. self.assertTrue(os.path.isdir(os.path.join(tuf_repo, 'targets'))) @@ -103,19 +111,19 @@ def test_methods(self): Note: here file at the 'filepath' and the 'target' file at tuf-targets directory are identical files. - Ex: filepath = '{temp_root}/repo/file.txt' - target = '{temp_root}/tuf_repo/targets/file.txt' + Ex: filepath = '{root_repo}/reg_repo/file.txt' + target = '{root_repo}/tuf_repo/targets/file.txt' """ - repo = os.path.join(self.temp_root, 'repo') - tuf_repo = os.path.join(self.temp_root, 'tuf_repo') - downloads = os.path.join(self.temp_root, 'downloads') + reg_repo = os.path.join(self.root_repo, 'reg_repo') + tuf_repo = os.path.join(self.root_repo, 'tuf_repo') + downloads = os.path.join(self.root_repo, 'downloads') - # Test 'add_file_to_repository(data)' and read_file_content(filepath) - # methods - filepath = util_test_tools.add_file_to_repository('Test') + # Test 'add_file_to_repository(directory, data)' and + # read_file_content(filepath) methods. + filepath = util_test_tools.add_file_to_repository(reg_repo, 'Test') self.assertTrue(os.path.isfile(filepath)) - self.assertEquals(os.path.dirname(filepath), repo) + self.assertEquals(os.path.dirname(filepath), reg_repo) filepath_content = util_test_tools.read_file_content(filepath) self.assertEquals('Test', filepath_content) @@ -126,22 +134,17 @@ def test_methods(self): self.assertEquals('Modify', filepath_content) # Test 'tuf_refresh_repo' method. - util_test_tools.tuf_refresh_repo() + util_test_tools.tuf_refresh_repo(self.root_repo, self.keyids) file_basename = os.path.basename(filepath) target = os.path.join(tuf_repo, 'targets', file_basename) self.assertTrue(os.path.isfile(target)) - # Test 'tuf_refresh_and_download()' method. - util_test_tools.tuf_refresh_and_download() - target = os.path.join(downloads, file_basename) - self.assertTrue(os.path.isfile(target)) - # Test 'delete_file_at_repository(filepath)' method. util_test_tools.delete_file_at_repository(filepath) self.assertFalse(os.path.exists(filepath)) # Test 'tuf_refresh_repo' method once more. - util_test_tools.tuf_refresh_repo() + util_test_tools.tuf_refresh_repo(self.root_repo, self.keyids) file_basename = os.path.basename(filepath) target = os.path.join(tuf_repo, 'targets', file_basename) self.assertFalse(os.path.isfile(target)) diff --git a/tuf/tests/system_tests/util_test_tools.py b/tuf/tests/system_tests/util_test_tools.py index fb8b86b4..452a3637 100644 --- a/tuf/tests/system_tests/util_test_tools.py +++ b/tuf/tests/system_tests/util_test_tools.py @@ -24,45 +24,45 @@ Initialized by init_repo() - The server is pointing to 'temp_root' directory, including the '/'. + The server is pointing to 'root_repo' directory, including the '/'. - temp_root + root_repo | - ------------------------------------------------------- - | | | | | - repo tuf_repo tuf_client tuf_downloads downloads + ---------------------------------------- + | | | | + reg_repo tuf_repo tuf_client downloads - '{temp_repo}/downloads/': stores all direct downloads made by the client. - '{temp_repo}/tuf_downloads/': stores all downloads made by the client using + '{root_repo}/downloads/': stores all direct downloads made by the client. + '{root_repo}/tuf_downloads/': stores all downloads made by the client using tuf. - repo + reg_repo | ----------------------------- | | ... | file(1) file(2) ... file(n) - '{temp_repo}/repo/': main developer's repository that contains files or + '{root_repo}/reg_repo/': main developer's repository that contains files or updates that need to be distributed. tuf_repo | - ------------------------------------------ + -------------------------------------------- | | | keystore metadata targets | | | key1.key ... role.txt ... file(1) ... - '{temp_repo}/tuf_repo/': developer's tuf-repository directory containing + '{root_repo}/tuf_repo/': developer's tuf-repository directory containing following subdirectories: - '{temp_repo}/tuf_repo/keystore/': directory where all signing keys are - stored. - '{temp_repo}/tuf_repo/metadata/': directory where all metadata signed - metadata files are stored. - '{temp_repo}/tuf_repo/targets/': directory where all tuf verified files - are stored. + '{root_repo}/tuf_repo/keystore/': directory where all signing keys are + stored. + '{root_repo}/tuf_repo/metadata/': directory where all metadata signed + metadata files are stored. + '{root_repo}/tuf_repo/targets/': directory where all tuf verified files + are stored. tuf_client | @@ -74,10 +74,10 @@ | | role.txt ... role.txt ... - '{temp_repo}/tuf_cleint/': client directory containing tuf metadata. - '{temp_repo}/tuf_cleint/metadata/current': directory where client stores + '{root_repo}/tuf_client/': client directory containing tuf metadata. + '{root_repo}/tuf_client/metadata/current': directory where client stores latest metadata files. - '{temp_repo}/tuf_cleint/metadata/current': directory where client stores + '{root_repo}/tuf_client/metadata/current': directory where client stores previous metadata files. @@ -85,25 +85,25 @@ Initializes the repositories (depicted in the diagram above) and starts the server process. init_repo takes one boolean argument which when True sets-up tuf repository i.e. adds all of the - directories that start with 'tuf_' in the temp_root (depicted above). - Returns a tuple - full path of the 'temp_root' directory, and the url. + directories that start with 'tuf_' in the root_repo (depicted above). + Returns a tuple - full path of the 'root_repo' directory, and the url. This should be sufficient to construct the tests. cleanup(): Deletes all of the created repositories and shuts down the server. add_file_to_repository(data): - Adds a file to the 'repo' directory and writes 'data' into it. + Adds a file to the 'reg_repo' directory and writes 'data' into it. Returns full file path of the new file. modify_file_at_repository(filepath, data): - Modifies a file at the 'repo' directory by writing 'data' into it. - 'filepath' has to be an existing file at the 'repo' directory. + Modifies a file at the 'reg_repo' directory by writing 'data' into it. + 'filepath' has to be an existing file at the 'reg_repo' directory. Returns full file path of the modified file. delete_file_at_repository(filepath): - Deletes a file at the 'repo' directory. - 'filepath' has to be an existing file at the 'repo' directory. + Deletes a file at the 'reg_repo' directory. + 'filepath' has to be an existing file at the 'reg_repo' directory. read_file_content(filepath): Returns data string of the 'filepath' content. @@ -114,24 +114,9 @@ tuf_refresh_repo(): Refreshes metadata files at the 'tuf_repo' directory i.e. role.txt's at - '{temp_root}/tuf_repo/metadata/'. Following roles are refreshed: - targets, release and timestamp. Also, the whole 'repo' directory is - copied to targets directory i.e. '{temp_root}/tuf_repo/targets/'. - - tuf_refresh_client_metadata(): - Downloads latests metadata files from '{temp_root}/tuf_repo/metadata/' - into '{temp_root}/tuf_client/metadata/current/'. - - tuf_download_updates() - Downloads files in the secure manner and then performs all tuf security - checks i.e. length and hash comparisons based on the information in the - metadata files. - - tuf_refresh_and_download() - Combines tuf_refresh_repo(), tuf_refresh_client_metadata(), and - tuf_download_updates(). - Returns 'tuf_downloads' directory where all tuf downloaded files are - located. + '{root_repo}/tuf_repo/metadata/'. Following roles are refreshed: + targets, release and timestamp. Also, the whole 'reg_repo' directory is + copied to targets directory i.e. '{root_repo}/tuf_repo/targets/'. Note: metadata files are root.txt, targets.txt, release.txt and timestamp.txt (denoted as 'role.txt in the diagrams'). There could be @@ -151,6 +136,8 @@ import tempfile import subprocess +import tuf.util +import tuf.interposition import tuf.client.updater import tuf.repo.signerlib as signerlib @@ -161,69 +148,64 @@ # 'setup_info' stores all important setup information like the path of the -# 'temp_root' directory, etc. -setup_info = {} +# 'root_repo' directory, etc. def init_repo(tuf=False): - setup_info['tuf'] = tuf - # Temp root directory for regular and tuf repositories. - # WARNING: tuf client stores files in '{temp_root}/downloads/' directory! + # WARNING: tuf client stores files in '{root_repo}/downloads/' directory! # Make sure regular download are NOT stored in the that directory when # tuf stores its downloads there. If regular download needs to happen at - # the time when tuf has or will have tuf downloads stored there just create - # a separate directory in {temp_root} to store regular downloads in. - # Ex: mkdir(temp_root, 'reg_downloads'). - setup_info['temp_root'] = temp_root = tempfile.mkdtemp(dir=os.getcwd()) - setup_info['repo'] = os.path.join(temp_root, 'repo') - setup_info['downloads'] = os.path.join(temp_root, 'downloads') - os.mkdir(setup_info['repo']) - os.mkdir(setup_info['downloads']) + # the time when tuf has or will have tuf downloads stored there, create + # a separate directory in {root_repo} to store regular downloads in. + # Ex: mkdir(root_repo, 'reg_downloads'). + root_repo = tempfile.mkdtemp(dir=os.getcwd()) + os.mkdir(os.path.join(root_repo, 'reg_repo')) + os.mkdir(os.path.join(root_repo, 'downloads')) # Start a simple server pointing to the repository directory. port = random.randint(30000, 45000) command = ['python', '-m', 'SimpleHTTPServer', str(port)] - setup_info['server_proc'] = subprocess.Popen(command, stderr=subprocess.PIPE) + server_proc = subprocess.Popen(command, stderr=subprocess.PIPE) # Tailor url for the repository. In order to download a 'file.txt' - # from 'repo' do: url+'repo/file.txt' - relpath = os.path.basename(temp_root) - setup_info['url'] = url = 'http://localhost:'+str(port)+'/'+relpath+'/' + # from 'reg_repo' do: url+'reg_repo/file.txt' + relpath = os.path.basename(root_repo) + url = 'http://localhost:'+str(port)+'/'+relpath+'/' # NOTE: The delay is needed to make up for asynchronous subprocess. # Otherwise following error might be raised: # time.sleep(.1) + + keyids = None + interpose_json = None if tuf: - init_tuf() + keyids, interpose_json = init_tuf(root_repo, url) - return temp_root, url + return root_repo, url, server_proc, keyids, interpose_json -def cleanup(): - if not setup_info: - msg = 'init_repo() must be called before cleanup().\n' - sys.exit(msg) - - if setup_info['server_proc'].returncode is None: - setup_info['server_proc'].kill() +def cleanup(root_repo, server_process): + if server_process.returncode is None: + server_process.kill() print 'Server terminated.\n' # Removing repository directory. try: - shutil.rmtree(setup_info['temp_root']) + shutil.rmtree(root_repo) except OSError, e: pass -def add_file_to_repository(data='Test String'): - junk, filepath = tempfile.mkstemp(dir=setup_info['repo']) + +def add_file_to_repository(directory, data='Test String'): + junk, filepath = tempfile.mkstemp(dir=directory) fileobj = open(filepath, 'wb') fileobj.write(data) fileobj.close() @@ -234,9 +216,8 @@ def add_file_to_repository(data='Test String'): def modify_file_at_repository(filepath, data='Modified String'): - repo = os.path.dirname(filepath) - if repo != setup_info['repo'] or not os.path.isfile(filepath): - msg = 'Provide a valid file on the repository to modify.' + if not os.path.isfile(filepath): + msg = ('Cannot modify file path '+repr(filepath)+', it does not exist.') sys.exit(msg) fileobj = open(filepath, 'wb') @@ -255,9 +236,8 @@ def delete_file_at_repository(filepath): """ - repo = os.path.dirname(filepath) - if repo != setup_info['repo'] or not os.path.isfile(filepath): - msg = 'Provide a valid file on the repository to delete.' + if not os.path.isfile(filepath): + msg = ('Cannot remove file path '+repr(filepath)+', it does not exist.') sys.exit(msg) os.remove(filepath) @@ -268,7 +248,8 @@ def delete_file_at_repository(filepath): def read_file_content(filepath): if not os.path.isfile(filepath): - msg = 'Provide a valid file to read.' + msg = ('File path '+repr(filepath)+' does not exist. '+ + 'Provide a valid file to read.') sys.exit(msg) fileobj = open(filepath, 'rb') @@ -280,7 +261,7 @@ def read_file_content(filepath): -def init_tuf(): +def init_tuf(root_repo, url): """ Setup TUF directory structure and populated it with TUF metadata and @@ -292,8 +273,7 @@ def init_tuf(): threshold = 1 # Setup TUF-repo directory structure. - setup_info['tuf_repo'] = tuf_repo = \ - os.path.join(setup_info['temp_root'], 'tuf_repo') + 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') @@ -301,13 +281,12 @@ def init_tuf(): os.mkdir(tuf_repo) os.mkdir(keystore_dir) os.mkdir(metadata_dir) - shutil.copytree(setup_info['repo'], targets_dir) + shutil.copytree(os.path.join(root_repo, 'reg_repo'), targets_dir) # Setting TUF-client directory structure. # 'tuf.client.updater.py' expects the 'current' and 'previous' # directories to exist under client's 'metadata' directory. - setup_info['tuf_client'] = tuf_client = \ - os.path.join(setup_info['temp_root'], 'tuf_client') + tuf_client = os.path.join(root_repo, 'tuf_client') tuf_client_metadata_dir = os.path.join(tuf_client, 'metadata') current_dir = os.path.join(tuf_client_metadata_dir, 'current') previous_dir = os.path.join(tuf_client_metadata_dir, 'previous') @@ -315,8 +294,7 @@ def init_tuf(): # Generate at least one rsa key. key = signerlib.generate_and_save_rsa_key(keystore_dir, passwd) - keyid = [key['keyid']] - setup_info['keyid'] = keyid + keyids = [key['keyid']] # Set role info. info = {'keyids': [key['keyid']], 'threshold': threshold} @@ -337,16 +315,16 @@ def init_tuf(): conf_path = signerlib.build_config_file(metadata_dir, 365, role_info) # Generate the 'root.txt' metadata file. - signerlib.build_root_file(conf_path, keyid, metadata_dir) + signerlib.build_root_file(conf_path, keyids, metadata_dir) # Generate the 'targets.txt' metadata file. - signerlib.build_targets_file(targets_dir, keyid, metadata_dir) + signerlib.build_targets_file(targets_dir, keyids, metadata_dir) # Generate the 'release.txt' metadata file. - signerlib.build_release_file(keyid, metadata_dir) + signerlib.build_release_file(keyids, metadata_dir) # Generate the 'timestamp.txt' metadata file. - signerlib.build_timestamp_file(keyid, metadata_dir) + signerlib.build_timestamp_file(keyids, metadata_dir) # Move the metadata to the client's 'current' and 'previous' directories. shutil.copytree(metadata_dir, current_dir) @@ -357,100 +335,66 @@ def init_tuf(): # Here is a mirrors dictionary that will allow a client to seek out # places to download the metadata and targets from. tuf_repo_relpath = os.path.basename(tuf_repo) - url_prefix = setup_info['url']+tuf_repo_relpath+'/' - setup_info['mirrors'] = {'mirror1': - {'url_prefix': url_prefix, - 'metadata_path': 'metadata', - 'targets_path': 'targets', - 'confined_target_dirs': ['']}} + tuf_url = url+tuf_repo_relpath + mirrors = {'mirror1': {'url_prefix': tuf_url, + 'metadata_path': 'metadata', + 'targets_path': 'targets', + 'confined_target_dirs': ['']}} # Adjusting configuration file (tuf.conf.py). - tuf.conf.repository_directory = setup_info['tuf_client'] + tuf.conf.repository_directory = tuf_client - # Instantiate an updater. - setup_info['updater'] = \ - tuf.client.updater.Updater('updater', setup_info['mirrors']) + # In order to implement interposition we need to have a config file with + # the following dictionary JSON-serialized. + # tuf_url: http://localhost:port/root_repo/tuf_repo/ + interposition_dict = {"network_locations": + {"localhost": + {"repository_directory": tuf_client+'/', + "repository_mirrors" : + {"mirror1": + {"url_prefix": tuf_url, + "metadata_path": "metadata", + "targets_path": "targets", + "confined_target_dirs": [ "" ]}}, + "target_paths": [ { "(.*\\.html)": "{0}" } ]}}} + + junk, interpose_json = tempfile.mkstemp(dir=root_repo) + with open(interpose_json, 'wb') as fileobj: + tuf.util.json.dump(interposition_dict, fileobj) + + return keyids, interpose_json -def tuf_refresh_repo(): +def tuf_refresh_repo(root_repo, keyids): """ Update TUF metadata files. Call this method whenever targets files have - changed in the 'repo'. + changed in the 'reg_repo'. """ - if not setup_info['tuf']: - msg = 'TUF needs to be initialized.\n' - sys.exist(msg) + reg_repo = os.path.join(root_repo, 'reg_repo') + tuf_repo = os.path.join(root_repo, 'tuf_repo') + targets_dir = os.path.join(tuf_repo, 'targets') + metadata_dir = os.path.join(tuf_repo, 'metadata') + + for directory in [reg_repo, tuf_repo, targets_dir, metadata_dir]: + if not os.path.isdir(directory): + msg = ('Directory '+repr(directory)+' does not exist. '+ + 'Verify that all directories were setup properly.') + raise OSError(msg) - keyid = setup_info['keyid'] - metadata_dir = os.path.join(setup_info['tuf_repo'], 'metadata') - targets_dir = os.path.join(setup_info['tuf_repo'], 'targets') shutil.rmtree(targets_dir) - shutil.copytree(setup_info['repo'], targets_dir) + shutil.copytree(reg_repo, targets_dir) # Regenerate the 'targets.txt' metadata file. - signerlib.build_targets_file(targets_dir, keyid, metadata_dir) + signerlib.build_targets_file(targets_dir, keyids, metadata_dir) # Regenerate the 'release.txt' metadata file. - signerlib.build_release_file(keyid, metadata_dir) + signerlib.build_release_file(keyids, metadata_dir) # Regenerate the 'timestamp.txt' metadata file. - signerlib.build_timestamp_file(keyid, metadata_dir) - - - - - -def tuf_refresh_client_metadata(): - if not setup_info['tuf']: - msg = 'TUF needs to be initialized.\n' - sys.exist(msg) - - # Update all metadata. - setup_info['updater'].refresh() - - - - - -def tuf_download_updates(): - """ - Here it is assumed that client has already downloaded latest metadata files. - """ - if not setup_info['tuf']: - msg = 'TUF needs to be initialized.\n' - sys.exist(msg) - - # Get the latest information on targets. - targets = setup_info['updater'].all_targets() - - # Create destination directory for the tuf targets. - dest = setup_info['downloads'] - - # Determine which targets have changed or are new. - updated_targets = \ - setup_info['updater'].updated_targets(targets, dest) - - # Download new/changed targets and store them in the 'tuf_downloads' dir. - for target in updated_targets: - setup_info['updater'].download_target(target, dest) - - - - - -def tuf_refresh_and_download(): - """ - Combines tuf_refresh_repo(), tuf_refresh_client_metadata(), and - tuf_download_updates(). - Returns 'tuf_downloads' directory. - """ - tuf_refresh_repo() - tuf_refresh_client_metadata() - tuf_download_updates() - return setup_info['downloads'] \ No newline at end of file + signerlib.build_timestamp_file(keyids, metadata_dir) \ No newline at end of file