From 07b4b24343b486cfef09112963a78f877da28286 Mon Sep 17 00:00:00 2001 From: Vladimir Diaz Date: Fri, 21 Oct 2016 11:36:48 -0400 Subject: [PATCH] Finish edits to repository tool doc and update code to fix issues with slow retrieval errors --- tuf/README.md | 32 +++++++++++++++++++------------- tuf/__init__.py | 2 +- tuf/download.py | 17 +++++++++++++++-- 3 files changed, 35 insertions(+), 16 deletions(-) diff --git a/tuf/README.md b/tuf/README.md index d817d368..877735fa 100644 --- a/tuf/README.md +++ b/tuf/README.md @@ -891,25 +891,31 @@ Error: No working mirror was found: ### Slow Retrieval Attack ### In a slow retrieval attack, an attacker responds to clients with a very slow stream of data that essentially results in the client never continuing the -update process. For this example, we simulate a slow retrieval attack by -spawning a server that serves our update client data at a slow rate. TUF -should not be vulnerable to this attack, and the framework should raise an +update process. In this example, we simulate a slow retrieval attack by +spawning a server that serves data at a slow rate to our update client data. +TUF should not be vulnerable to this attack, and the framework should raise an exception or error when it detects that a malicious server is serving it data at a slow enough rate. -We first spawn a server that slowly streams data to a client request. The -'slow_retrieval_server.py' module can be copied over to the '../demo_repository' -directory from which to launch it. +We first spawn the server that slowly streams data to the client. The +'slow_retrieval_server.py' module (can be found in the tests/ directory of the +source code) should be copied over to the server's 'repository/' directory from +which to launch it. ```Bash +# Before launching the slow retrieval server, copy 'slow_retrieval_server.py' +# to the 'repository/' directory and run it from that directory. Assuming +# the current working directory is "repository/": +$ cp ../../../tests/slow_retrieval_server.py . $ python slow_retrieval_server.py 8002 mode_2 ``` -The client may now make a request to the slow server on port 8002. However, -before doing so, we'll need to reduce (for the purposes of this demo) the -minimum average download rate allowed. Open the '.../demo/tuf/tuf/conf.py' -file and set MIN_AVERAGE_DOWNLOAD_SPEED to 1. This should make it so -that client correctly detects the slow retrieval server's delayed streaming. +The client may now make a request to the slow retrieval server on port 8002. +However, before doing so, we'll reduce (for the purposes of this demo) the +minimum average download rate allowed and download chunk size. Open the +'conf.py' module and set MIN_AVERAGE_DOWNLOAD_SPEED = 5 and CHUNK_SIZE = 1. +This should make it so that the client detects the slow retrieval server's +delayed streaming. ```Bash $ python basic_client.py --verbose 1 --repo http://localhost:8002 @@ -917,8 +923,8 @@ Error: No working mirror was found: u'localhost:8002': SlowRetrievalError() ``` -The framework should detect the attack and raise a SlowRetrievalError -exception to the client application. +The framework should detect the slow retrieval attack and raise a +SlowRetrievalError exception to the client application. ## Conclusion ## diff --git a/tuf/__init__.py b/tuf/__init__.py index dc1639a3..ab527aaa 100755 --- a/tuf/__init__.py +++ b/tuf/__init__.py @@ -258,7 +258,7 @@ def __init__(self, average_download_speed): self.__average_download_speed = average_download_speed #bytes/second def __str__(self): - return 'Download was too slow. The average download speed has gone' +\ + return 'Download was too slow. The average download speed dropped' +\ ' below the minimum download speed of ' +\ repr(self.__average_download_speed) + ' bytes per second.' diff --git a/tuf/download.py b/tuf/download.py index 58a2efbf..3f59649e 100755 --- a/tuf/download.py +++ b/tuf/download.py @@ -330,14 +330,14 @@ def _download_fixed_amount_of_data(connection, temp_file, required_length): data = b'' read_amount = min(tuf.conf.CHUNK_SIZE, required_length - number_of_bytes_received) - + try: data = connection.read(read_amount) # Python 3.2 returns 'IOError' if the remote file object has timed out. except (socket.error, IOError): pass - + number_of_bytes_received = number_of_bytes_received + len(data) # Data successfully read from the connection. Store it. @@ -356,7 +356,13 @@ def _download_fixed_amount_of_data(connection, temp_file, required_length): average_download_speed = number_of_bytes_received / seconds_spent_receiving if average_download_speed < tuf.conf.MIN_AVERAGE_DOWNLOAD_SPEED: + logger.debug('The average download speed dropped below the minimum' + ' average download speed set in conf.py.') break + + else: + logger.debug('The average download speed has not dipped below the' + ' mimimum average download speed set in conf.py.') # We might have no more data to read. Check number of bytes downloaded. if not data: @@ -641,6 +647,13 @@ def _check_downloaded_length(total_downloaded, required_length, # will log a warning anyway. This is useful when we wish to download the # Timestamp or Root metadata, for which we have no signed metadata; so, # we must guess a reasonable required_length for it. + if average_download_speed < tuf.conf.MIN_AVERAGE_DOWNLOAD_SPEED: + raise tuf.SlowRetrievalError(average_download_speed) + + else: + logger.debug('Good average download speed: ' + + repr(average_download_speed) + ' bytes per second') + logger.info('Downloaded ' + str(total_downloaded) + ' bytes out of an' ' upper limit of ' + str(required_length) + ' bytes.')