Modified test_replay_attack.py, util_test_tools.py and test_util_test_tools.py.

This commit is contained in:
Kon 2013-03-03 03:47:36 -05:00
parent e7ba0092e4
commit c0d5d2cbb5
4 changed files with 242 additions and 292 deletions

View file

@ -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

View file

@ -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):
"""
<Arguments>
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()
test_replay_attack(TUF=True)
except ReplayAttackError, err:
print err

View file

@ -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))

View file

@ -24,45 +24,45 @@
<Directories>
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.
<Methods>
@ -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:
# <urlopen error [Errno 111] Connection refused>
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):
"""
<Purpose>
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):
"""
<Purpose>
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']
signerlib.build_timestamp_file(keyids, metadata_dir)