From 7fada800f3e455cb27a4da01eab93c0084ab2b45 Mon Sep 17 00:00:00 2001 From: Lazlo Westerhof Date: Thu, 25 Jun 2015 21:55:29 +0200 Subject: [PATCH 1/6] Add some missing docstrings. --- onionshare/helpers.py | 23 +++++++++++++++++++++++ onionshare/strings.py | 7 +++++++ onionshare_gui/common.py | 6 ++++++ 3 files changed, 36 insertions(+) diff --git a/onionshare/helpers.py b/onionshare/helpers.py index 8c30c2e2..279cb56b 100644 --- a/onionshare/helpers.py +++ b/onionshare/helpers.py @@ -27,6 +27,9 @@ sys.setdefaultencoding("utf-8") def get_platform(): + """ + Returns the platform OnionShare is running on. + """ p = platform.system() if p == 'Linux' and platform.uname()[0:2] == ('Linux', 'amnesia'): p = 'Tails' @@ -40,14 +43,22 @@ if get_platform() == 'Darwin': else: osx_resources_dir = None + def get_onionshare_dir(): + """ + Returns the OnionShare directory. + """ if get_platform() == 'Darwin': onionshare_dir = os.path.dirname(__file__) else: onionshare_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) return onionshare_dir + def get_html_path(filename): + """ + Returns the path of the html files. + """ p = platform.system() if p == 'Darwin': prefix = os.path.join(osx_resources_dir, 'html') @@ -57,6 +68,9 @@ def get_html_path(filename): def constant_time_compare(val1, val2): + """ + Compares two values in constant time. + """ _builtin_constant_time_compare = getattr(hmac, 'compare_digest', None) if _builtin_constant_time_compare is not None: return _builtin_constant_time_compare(val1, val2) @@ -74,6 +88,9 @@ def constant_time_compare(val1, val2): def random_string(num_bytes, output_len=None): + """ + Returns a random string with a specified number of bytes. + """ b = os.urandom(num_bytes) h = hashlib.sha256(b).digest()[:16] s = base64.b32encode(h).lower().replace('=', '') @@ -83,6 +100,9 @@ def random_string(num_bytes, output_len=None): def human_readable_filesize(b): + """ + Returns filesize in a human readable format. + """ thresh = 1024.0 if b < thresh: return '{0:.1f} B'.format(b) @@ -96,6 +116,9 @@ def human_readable_filesize(b): def is_root(): + """ + Returns if user is root. + """ return os.geteuid() == 0 diff --git a/onionshare/strings.py b/onionshare/strings.py index fe4fc0e4..6e2521b0 100644 --- a/onionshare/strings.py +++ b/onionshare/strings.py @@ -24,6 +24,10 @@ strings = {} def load_strings(default="en"): + """ + Loads translated strings and fallback to English + if the translation does not exist. + """ global strings p = helpers.get_platform() @@ -55,6 +59,9 @@ def load_strings(default="en"): def translated(k, gui=False): + """ + Returns a translated string. + """ if gui: return strings[k].encode("utf-8").decode('utf-8', 'replace') else: diff --git a/onionshare_gui/common.py b/onionshare_gui/common.py index 60212f0b..5caedfbf 100644 --- a/onionshare_gui/common.py +++ b/onionshare_gui/common.py @@ -21,6 +21,9 @@ import os, sys, inspect, platform from onionshare import helpers def get_onionshare_gui_dir(): + """ + Returns the OnionShare gui directory. + """ p = helpers.get_platform() if p == 'Darwin': onionshare_gui_dir = os.path.dirname(__file__) @@ -32,6 +35,9 @@ onionshare_gui_dir = get_onionshare_gui_dir() def get_image_path(filename): + """ + Returns the OnionShare image path. + """ p = helpers.get_platform() if p == 'Linux' or p == 'Tails': prefix = os.path.join(sys.prefix, 'share/onionshare/images') From 272e4244c366edb60e1ff85b6f600fdfa417468c Mon Sep 17 00:00:00 2001 From: Lazlo Westerhof Date: Thu, 25 Jun 2015 22:01:33 +0200 Subject: [PATCH 2/6] Update Dutch translation strings. --- locale/nl.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/locale/nl.json b/locale/nl.json index aae63102..a70a0fe6 100644 --- a/locale/nl.json +++ b/locale/nl.json @@ -35,9 +35,12 @@ "gui_stop_server": "Stop server", "gui_copy_url": "Kopier URL", "gui_downloads": "Downloads:", + "gui_canceled": "Afgebroken", "gui_copied_url": "URL gekopieerd naar klembord", "gui_starting_server1": "Tor verborgen service wordt gestart...", "gui_starting_server2": "Bestanden verwerken...", "gui_starting_server3": "Wachten op Tor verborgen service...", - "gui_please_wait": "Moment geduld..." + "gui_please_wait": "Moment geduld...", + "error_hs_dir_cannot_create": "Kan verborgen service map {0:s} niet aanmaken", + "error_hs_dir_not_writable": "Verborgen service map {0:s} is niet schrijfbaar" } From 7446cee6551cc3bf1d6422969ae8dd416bfd32d4 Mon Sep 17 00:00:00 2001 From: Lazlo Westerhof Date: Fri, 26 Jun 2015 19:38:22 +0200 Subject: [PATCH 3/6] Fix some codestyle issues according to pep8. --- onionshare/helpers.py | 2 ++ onionshare/onionshare.py | 4 +-- onionshare/socks.py | 52 ++++++++++++++++---------------- onionshare/web.py | 2 +- onionshare_gui/common.py | 1 + onionshare_gui/onionshare_gui.py | 2 +- onionshare_gui/options.py | 3 +- onionshare_gui/server_status.py | 2 +- 8 files changed, 35 insertions(+), 33 deletions(-) diff --git a/onionshare/helpers.py b/onionshare/helpers.py index 8c30c2e2..05d69fe9 100644 --- a/onionshare/helpers.py +++ b/onionshare/helpers.py @@ -40,6 +40,7 @@ if get_platform() == 'Darwin': else: osx_resources_dir = None + def get_onionshare_dir(): if get_platform() == 'Darwin': onionshare_dir = os.path.dirname(__file__) @@ -47,6 +48,7 @@ def get_onionshare_dir(): onionshare_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) return onionshare_dir + def get_html_path(filename): p = platform.system() if p == 'Darwin': diff --git a/onionshare/onionshare.py b/onionshare/onionshare.py index d29b09c5..1a56d9b4 100644 --- a/onionshare/onionshare.py +++ b/onionshare/onionshare.py @@ -112,7 +112,7 @@ class OnionShare(object): args = ['/usr/bin/sudo', '--', '/usr/bin/onionshare'] print "Executing: {0:s}".format(args+[str(self.port)]) p = subprocess.Popen(args+[str(self.port)], stderr=subprocess.PIPE, stdout=subprocess.PIPE) - stdout = p.stdout.read(22) # .onion URLs are 22 chars long + stdout = p.stdout.read(22) # .onion URLs are 22 chars long if stdout: self.onion_host = stdout @@ -227,7 +227,7 @@ class OnionShare(object): except urllib2.HTTPError: # Tails error sys.stdout.write('{0:s}\n'.format(strings._('wait_for_hs_nope'))) sys.stdout.flush() - except httplib.BadStatusLine: # Tails (with bridge) error + except httplib.BadStatusLine: # Tails (with bridge) error sys.stdout.write('{0:s}\n'.format(strings._('wait_for_hs_nope'))) sys.stdout.flush() except KeyboardInterrupt: diff --git a/onionshare/socks.py b/onionshare/socks.py index cbfabd06..809a4444 100644 --- a/onionshare/socks.py +++ b/onionshare/socks.py @@ -14,7 +14,7 @@ are permitted provided that the following conditions are met: 3. Neither the name of Dan Haim nor the names of his contributors may be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY DAN HAIM "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO @@ -40,7 +40,7 @@ mainly to merge bug fixes found in Sourceforge Modifications made by Anorov (https://github.com/Anorov) -Forked and renamed to PySocks -Fixed issue with HTTP proxy failure checking (same bug that was in the old ___recvall() method) --Included SocksiPyHandler (sockshandler.py), to be used as a urllib2 handler, +-Included SocksiPyHandler (sockshandler.py), to be used as a urllib2 handler, courtesy of e000 (https://github.com/e000): https://gist.github.com/869791#file_socksipyhandler.py -Re-styled code to make it readable -Aliased PROXY_TYPE_SOCKS5 -> SOCKS5 etc. @@ -136,7 +136,7 @@ def set_default_proxy(proxy_type=None, addr=None, port=None, rdns=True, username Sets a default proxy which all further socksocket objects will use, unless explicitly changed. """ - socksocket.default_proxy = (proxy_type, addr.encode(), port, rdns, + socksocket.default_proxy = (proxy_type, addr.encode(), port, rdns, username.encode() if username else None, password.encode() if password else None) @@ -200,7 +200,7 @@ class socksocket(socket.socket): def __init__(self, family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, _sock=None): _orig_socket.__init__(self, family, type, proto, _sock) - + if self.default_proxy: self.proxy = self.default_proxy else: @@ -245,7 +245,7 @@ class socksocket(socket.socket): password - Password to authenticate with to the server. Only relevant when username is also provided. """ - self.proxy = (proxy_type, addr.encode(), port, rdns, + self.proxy = (proxy_type, addr.encode(), port, rdns, username.encode() if username else None, password.encode() if password else None) @@ -292,18 +292,18 @@ class socksocket(socket.socket): # No username/password were entered, therefore we # only support connections with no authentication. self.sendall(b"\x05\x01\x00") - + # We'll receive the server's response to determine which # method was selected chosen_auth = self._recvall(2) if chosen_auth[0:1] != b"\x05": - # Note: string[i:i+1] is used because indexing of a bytestring + # Note: string[i:i+1] is used because indexing of a bytestring # via bytestring[i] yields an integer in Python 3 raise GeneralProxyError("SOCKS5 proxy server sent invalid data") - + # Check the chosen authentication method - + if chosen_auth[1:2] == b"\x02": # Okay, we need to perform a basic username/password # authentication. @@ -318,17 +318,17 @@ class socksocket(socket.socket): if auth_status[1:2] != b"\x00": # Authentication failed raise SOCKS5AuthError("SOCKS5 authentication failed") - + # Otherwise, authentication succeeded - # No authentication is required if 0x00 + # No authentication is required if 0x00 elif chosen_auth[1:2] != b"\x00": # Reaching here is always bad if chosen_auth[1:2] == b"\xFF": raise SOCKS5AuthError("All offered SOCKS5 authentication methods were rejected") else: raise GeneralProxyError("SOCKS5 proxy server sent invalid data") - + # Now we can request the actual connection req = b"\x05\x01\x00" # If the given destination address is an IP address, we'll @@ -349,7 +349,7 @@ class socksocket(socket.socket): req += struct.pack(">H", dest_port) self.sendall(req) - + # Get the response resp = self._recvall(4) if resp[0:1] != b"\x05": @@ -360,7 +360,7 @@ class socksocket(socket.socket): # Connection failed: server returned an error error = SOCKS5_ERRORS.get(status, "Unknown error") raise SOCKS5Error("{:#04x}: {}".format(status, error)) - + # Get the bound address/port if resp[3:4] == b"\x01": bound_addr = self._recvall(4) @@ -369,7 +369,7 @@ class socksocket(socket.socket): bound_addr = self._recvall(ord(resp[4:5])) else: raise GeneralProxyError("SOCKS5 proxy server sent invalid data") - + bound_port = struct.unpack(">H", self._recvall(2))[0] self.proxy_sockname = bound_addr, bound_port if addr_bytes: @@ -394,15 +394,15 @@ class socksocket(socket.socket): remote_resolve = True else: addr_bytes = socket.inet_aton(socket.gethostbyname(dest_addr)) - + # Construct the request packet req = struct.pack(">BBH", 0x04, 0x01, dest_port) + addr_bytes - + # The username parameter is considered userid for SOCKS4 if username: req += username req += b"\x00" - + # DNS name if remote resolving is required # NOTE: This is actually an extension to the SOCKS4 protocol # called SOCKS4A and may not be supported in all cases. @@ -439,25 +439,25 @@ class socksocket(socket.socket): # If we need to resolve locally, we do this now addr = dest_addr if rdns else socket.gethostbyname(dest_addr) - self.sendall(b"CONNECT " + addr.encode() + b":" + str(dest_port).encode() + + self.sendall(b"CONNECT " + addr.encode() + b":" + str(dest_port).encode() + b" HTTP/1.1\r\n" + b"Host: " + dest_addr.encode() + b"\r\n\r\n") - + # We just need the first line to check if the connection was successful fobj = self.makefile() status_line = fobj.readline() fobj.close() - + if not status_line: raise GeneralProxyError("Connection closed unexpectedly") - + try: proto, status_code, status_msg = status_line.split(" ", 2) except ValueError: raise GeneralProxyError("HTTP proxy server sent invalid response") - + if not proto.startswith("HTTP/"): raise GeneralProxyError("Proxy server does not appear to be an HTTP proxy") - + try: status_code = int(status_code) except ValueError: @@ -475,7 +475,7 @@ class socksocket(socket.socket): self.proxy_peername = addr, dest_port def connect(self, dest_pair): - """ + """ Connects to the specified destination through a proxy. Uses the same API as socket's connect(). To select the proxy server, use set_proxy(). @@ -500,7 +500,7 @@ class socksocket(socket.socket): proxy_port = proxy_port or DEFAULT_PORTS.get(proxy_type) if not proxy_port: raise GeneralProxyError("Invalid proxy type") - + try: # Initial connection to proxy server _orig_socket.connect(self, (proxy_addr, proxy_port)) diff --git a/onionshare/web.py b/onionshare/web.py index 059475aa..eb50eab8 100644 --- a/onionshare/web.py +++ b/onionshare/web.py @@ -146,7 +146,7 @@ def download(slug_candidate): basename = os.path.basename(zip_filename) def generate(): - chunk_size = 102400 # 100kb + chunk_size = 102400 # 100kb fp = open(zip_filename, 'rb') done = False diff --git a/onionshare_gui/common.py b/onionshare_gui/common.py index 60212f0b..4ec83eab 100644 --- a/onionshare_gui/common.py +++ b/onionshare_gui/common.py @@ -20,6 +20,7 @@ along with this program. If not, see . import os, sys, inspect, platform from onionshare import helpers + def get_onionshare_gui_dir(): p = helpers.get_platform() if p == 'Darwin': diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py index b2642dbf..46757a0d 100644 --- a/onionshare_gui/onionshare_gui.py +++ b/onionshare_gui/onionshare_gui.py @@ -26,7 +26,7 @@ import common try: import onionshare except ImportError: - sys.path.append(os.path.abspath(common.onionshare_gui_dir+"/..")) + sys.path.append(os.path.abspath(common.onionshare_gui_dir + "/..")) import onionshare from onionshare import strings, helpers, web diff --git a/onionshare_gui/options.py b/onionshare_gui/options.py index f7443b85..2fce2c95 100644 --- a/onionshare_gui/options.py +++ b/onionshare_gui/options.py @@ -28,7 +28,7 @@ class Options(QtGui.QHBoxLayout): super(Options, self).__init__() self.web = web - + # close automatically self.close_automatically = QtGui.QCheckBox() if self.web.stay_open: @@ -46,4 +46,3 @@ class Options(QtGui.QHBoxLayout): self.web.set_stay_open(False) else: self.web.set_stay_open(True) - diff --git a/onionshare_gui/server_status.py b/onionshare_gui/server_status.py index 579e2e8f..02b4c580 100644 --- a/onionshare_gui/server_status.py +++ b/onionshare_gui/server_status.py @@ -144,7 +144,7 @@ class ServerStatus(QtGui.QVBoxLayout): GMEM_DDESHARE = 0x2000 ctypes.windll.user32.OpenClipboard(None) ctypes.windll.user32.EmptyClipboard() - hcd = ctypes.windll.kernel32.GlobalAlloc(GMEM_DDESHARE, len(bytes(url))+1) + hcd = ctypes.windll.kernel32.GlobalAlloc(GMEM_DDESHARE, len(bytes(url)) + 1) pch_data = ctypes.windll.kernel32.GlobalLock(hcd) ctypes.cdll.msvcrt.strcpy(ctypes.c_char_p(pch_data), bytes(url)) ctypes.windll.kernel32.GlobalUnlock(hcd) From ac97ddf7d0a69cc44253af698c0fddea0fef7421 Mon Sep 17 00:00:00 2001 From: jvoisin Date: Sat, 25 Jul 2015 10:37:05 +0200 Subject: [PATCH 4/6] Various fixes - more pep8 - add some forgotten deps in setup.py --- onionshare/helpers.py | 2 ++ onionshare/onionshare.py | 9 +++++---- onionshare/socks.py | 4 ++-- onionshare/strings.py | 16 ++++++++-------- onionshare/web.py | 8 ++++---- setup.py | 2 +- 6 files changed, 22 insertions(+), 19 deletions(-) diff --git a/onionshare/helpers.py b/onionshare/helpers.py index 8c30c2e2..05d69fe9 100644 --- a/onionshare/helpers.py +++ b/onionshare/helpers.py @@ -40,6 +40,7 @@ if get_platform() == 'Darwin': else: osx_resources_dir = None + def get_onionshare_dir(): if get_platform() == 'Darwin': onionshare_dir = os.path.dirname(__file__) @@ -47,6 +48,7 @@ def get_onionshare_dir(): onionshare_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) return onionshare_dir + def get_html_path(filename): p = platform.system() if p == 'Darwin': diff --git a/onionshare/onionshare.py b/onionshare/onionshare.py index d29b09c5..b9233316 100644 --- a/onionshare/onionshare.py +++ b/onionshare/onionshare.py @@ -17,7 +17,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . """ -import os, sys, subprocess, time, argparse, inspect, shutil, socket, threading, urllib2, httplib, tempfile +import os, sys, subprocess, time, argparse, shutil, socket, threading, urllib2, httplib, tempfile import socks from stem.control import Controller @@ -52,6 +52,7 @@ class OnionShare(object): self.port = None self.controller = None self.hidserv_dir = None + self.onion_host = None # debug mode if debug: @@ -112,14 +113,14 @@ class OnionShare(object): args = ['/usr/bin/sudo', '--', '/usr/bin/onionshare'] print "Executing: {0:s}".format(args+[str(self.port)]) p = subprocess.Popen(args+[str(self.port)], stderr=subprocess.PIPE, stdout=subprocess.PIPE) - stdout = p.stdout.read(22) # .onion URLs are 22 chars long + stdout = p.stdout.read(22) # .onion URLs are 22 chars long if stdout: self.onion_host = stdout print 'Got onion_host: {0:s}'.format(self.onion_host) else: if p.poll() == -1: - raise TailsError(o.stderr.read()) + raise TailsError(sys.stderr.read()) else: raise TailsError(strings._("error_tails_unknown_root")) @@ -227,7 +228,7 @@ class OnionShare(object): except urllib2.HTTPError: # Tails error sys.stdout.write('{0:s}\n'.format(strings._('wait_for_hs_nope'))) sys.stdout.flush() - except httplib.BadStatusLine: # Tails (with bridge) error + except httplib.BadStatusLine: # Tails (with bridge) error sys.stdout.write('{0:s}\n'.format(strings._('wait_for_hs_nope'))) sys.stdout.flush() except KeyboardInterrupt: diff --git a/onionshare/socks.py b/onionshare/socks.py index cbfabd06..16b1b8c7 100644 --- a/onionshare/socks.py +++ b/onionshare/socks.py @@ -198,8 +198,8 @@ class socksocket(socket.socket): default_proxy = None - def __init__(self, family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, _sock=None): - _orig_socket.__init__(self, family, type, proto, _sock) + def __init__(self, family=socket.AF_INET, socket_type=socket.SOCK_STREAM, proto=0, _sock=None): + _orig_socket.__init__(self, family, socket_type, proto, _sock) if self.default_proxy: self.proxy = self.default_proxy diff --git a/onionshare/strings.py b/onionshare/strings.py index fe4fc0e4..daa0574e 100644 --- a/onionshare/strings.py +++ b/onionshare/strings.py @@ -17,7 +17,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . """ -import json, locale, sys, os, inspect +import json, locale, sys, os import helpers strings = {} @@ -36,22 +36,22 @@ def load_strings(default="en"): locale_dir = os.path.join(os.path.dirname(helpers.get_onionshare_dir()), 'locale') # load all translations - translated = {} + translations = {} for filename in os.listdir(locale_dir): abs_filename = os.path.join(locale_dir, filename) lang, ext = os.path.splitext(filename) if abs_filename.endswith('.json'): - translated[lang] = json.loads(open(abs_filename).read()) + translations[lang] = json.loads(open(abs_filename).read()) - strings = translated[default] + strings = translations[default] lc, enc = locale.getdefaultlocale() if lc: lang = lc[:2] - if lang in translated: + if lang in translations: # if a string doesn't exist, fallback to English - for key in translated[default]: - if key in translated[lang]: - strings[key] = translated[lang][key] + for key in translations[default]: + if key in translations[lang]: + strings[key] = translations[lang][key] def translated(k, gui=False): diff --git a/onionshare/web.py b/onionshare/web.py index 059475aa..3bfb5042 100644 --- a/onionshare/web.py +++ b/onionshare/web.py @@ -17,7 +17,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . """ -import Queue, mimetypes, platform, os, sys, zipfile, urllib2 +import Queue, mimetypes, platform, os, sys, urllib2 from flask import Flask, Response, request, render_template_string, abort import strings, helpers @@ -70,10 +70,10 @@ REQUEST_CANCELED = 4 q = Queue.Queue() -def add_request(type, path, data=None): +def add_request(request_type, path, data=None): global q q.put({ - 'type': type, + 'type': request_type, 'path': path, 'data': data }) @@ -146,7 +146,7 @@ def download(slug_candidate): basename = os.path.basename(zip_filename) def generate(): - chunk_size = 102400 # 100kb + chunk_size = 102400 # 100kb fp = open(zip_filename, 'rb') done = False diff --git a/setup.py b/setup.py index 159bc599..e447a1ac 100644 --- a/setup.py +++ b/setup.py @@ -123,5 +123,5 @@ elif system == 'Darwin': 'PyQt4.QtSvg', 'PyQt4.QtXmlPatterns'] } }, - setup_requires=['py2app'], + setup_requires=['py2app', 'flask', 'stem'], ) From ee7b536073228309cfc017c97be512bc7ff3365a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Sat, 1 Aug 2015 15:02:52 +0200 Subject: [PATCH 5/6] Fix typo in SECURITY.md. Slug is generated using 16 bytes of entropy, not 16 bits. --- SECURITY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SECURITY.md b/SECURITY.md index db21ea9b..a08218fb 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -17,7 +17,7 @@ As soon as the shared files get downloaded, or when the sender closes OnionShare * **Third parties don't have access to files being shared.** The files are hosted directly on the sender's computer and don't get uploaded to any server. Instead, the sender's computer becomes the server. Traditional ways of sending files, like in an email or using a cloud hosting service, require trusting the service with access to the files being shared. * **Network eavesdroppers can't spy on files in transit.** Because connections between Tor hidden services and Tor Browser are end-to-end encrypted, no network attackers can eavesdrop on the shared files while the recipient is downloading them. If the eavesdropper is positioned on the sender's end, the recipient's end, or is a malicious Tor node, they will only see Tor traffic. If the eavesdropper is a malicious rendezvous node used to connect the recipient's Tor client with the sender's hidden service, the traffic will be encrypted using the hidden service key. * **Anonymity of sender and recipient are protected by Tor.** OnionShare and Tor Browser protect the anonymity of the users. As long as the sender anonymously communicates the OnionShare URL with the recipient, the recipient and eavesdroppers can't learn the identity of the sender. -* **If an attacker enumerates the hidden service, the shared files remain safe.** There have been attacks against the Tor network that can enumerate hidden services. If someone discovers the .onion address of an OnionShare hidden service, they still cannot download the shared files without knowing the slug. The slug is generated using 16 bits of entropy, and the OnionShare server checks request URIs using a constant time string comparison function, so timing attacks can't be used to guess the slug. +* **If an attacker enumerates the hidden service, the shared files remain safe.** There have been attacks against the Tor network that can enumerate hidden services. If someone discovers the .onion address of an OnionShare hidden service, they still cannot download the shared files without knowing the slug. The slug is generated using 16 bytes of entropy, and the OnionShare server checks request URIs using a constant time string comparison function, so timing attacks can't be used to guess the slug. ## What it doesn't protect against From 89c363d247e50b4f4338980f91e4213e1f378206 Mon Sep 17 00:00:00 2001 From: "Christopher J. Markiewicz" Date: Fri, 6 Nov 2015 20:43:37 -0500 Subject: [PATCH 6/6] Clean up and exit on Ctrl-Q --- onionshare_gui/onionshare_gui.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py index b2642dbf..1794b945 100644 --- a/onionshare_gui/onionshare_gui.py +++ b/onionshare_gui/onionshare_gui.py @@ -42,6 +42,14 @@ class Application(QtGui.QApplication): if platform == 'Tails' or platform == 'Linux': self.setAttribute(QtCore.Qt.AA_X11InitThreads, True) QtGui.QApplication.__init__(self, sys.argv) + self.installEventFilter(self) + + def eventFilter(self, obj, event): + if (event.type() == QtCore.QEvent.KeyPress and + event.key() == QtCore.Qt.Key_Q and + event.modifiers() == QtCore.Qt.ControlModifier): + self.quit() + return False class OnionShareGui(QtGui.QWidget):