From 2de9359629d07a57cf7a439f485907a0cc0d2560 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Tue, 21 Aug 2018 19:31:02 +1000 Subject: [PATCH 01/21] Introduce v3 onion support --- .travis.yml | 2 +- BUILD.md | 4 +- install/requirements-windows.txt | 4 + install/requirements.txt | 4 + onionshare/onion.py | 51 +++++++++--- onionshare/onionkey.py | 126 ++++++++++++++++++++++++++++++ onionshare_gui/server_status.py | 11 ++- onionshare_gui/settings_dialog.py | 9 ++- 8 files changed, 195 insertions(+), 16 deletions(-) create mode 100644 onionshare/onionkey.py diff --git a/.travis.yml b/.travis.yml index 9010e77a..301f87a7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ python: - "nightly" # command to install dependencies install: - - pip install Flask==0.12 stem==1.5.4 pytest-cov coveralls flake8 + - pip install Flask==0.12 stem==1.5.4 pytest-cov coveralls flake8 pycrypto pynacl cryptography pysha3 before_script: # stop the build if there are Python syntax errors or undefined names - flake8 . --count --select=E901,E999,F821,F822,F823 --show-source --statistics diff --git a/BUILD.md b/BUILD.md index e6e54951..57c83438 100644 --- a/BUILD.md +++ b/BUILD.md @@ -11,9 +11,9 @@ cd onionshare Install the needed dependencies: -For Debian-like distros: `apt install -y build-essential fakeroot python3-all python3-stdeb dh-python python3-flask python3-stem python3-pyqt5 python-nautilus python3-pytest tor obfs4proxy` +For Debian-like distros: `apt install -y build-essential fakeroot python3-all python3-stdeb dh-python python3-flask python3-stem python3-pyqt5 python-nautilus python3-pytest tor obfs4proxy python3-cryptography python3-crypto python3-nacl python3-pip; pip3 install pysha3` -For Fedora-like distros: `dnf install -y rpm-build python3-flask python3-stem python3-qt5 python3-pytest nautilus-python tor obfs4` +For Fedora-like distros: `dnf install -y rpm-build python3-flask python3-stem python3-qt5 python3-pytest nautilus-python tor obfs4 python3-pynacl python3-cryptography python3-crypto python3-pip; pip3 install pysha3` After that you can try both the CLI and the GUI version of OnionShare: diff --git a/install/requirements-windows.txt b/install/requirements-windows.txt index 32b8da4a..611edf3c 100644 --- a/install/requirements-windows.txt +++ b/install/requirements-windows.txt @@ -1,4 +1,5 @@ click==6.7 +cryptography==2.1.4 Flask==0.12.2 future==0.16.0 itsdangerous==0.24 @@ -8,6 +9,9 @@ pefile==2017.11.5 PyInstaller==3.3.1 PyQt5==5.9.2 PySocks==1.6.7 +pynacl==1.2.1 +pycrypto==2.6.1 +pysha3==1.0.2 sip==4.19.6 stem==1.6.0 Werkzeug==0.14.1 diff --git a/install/requirements.txt b/install/requirements.txt index c7828080..964030e8 100644 --- a/install/requirements.txt +++ b/install/requirements.txt @@ -1,4 +1,5 @@ click==6.7 +cryptography==2.1.4 Flask==0.12.2 itsdangerous==0.24 Jinja2==2.10 @@ -6,6 +7,9 @@ MarkupSafe==1.0 PyInstaller==3.3.1 PyQt5==5.9.2 PySocks==1.6.7 +pycrypto==2.6.1 +pynacl==1.2.1 +pysha3==1.0.2 sip==4.19.6 stem==1.6.0 Werkzeug==0.14.1 diff --git a/onionshare/onion.py b/onionshare/onion.py index 4812842a..73c94600 100644 --- a/onionshare/onion.py +++ b/onionshare/onion.py @@ -21,8 +21,10 @@ along with this program. If not, see . from stem.control import Controller from stem import ProtocolError, SocketClosed from stem.connection import MissingPassword, UnreadableCookieFile, AuthenticationFailure -import os, sys, tempfile, shutil, urllib, platform, subprocess, time, shlex +import base64, os, sys, tempfile, shutil, urllib, platform, subprocess, time, shlex +from distutils.version import LooseVersion as Version +from . import onionkey from . import socks from . import common, strings from .settings import Settings @@ -444,20 +446,49 @@ class Onion(object): basic_auth = None if self.settings.get('private_key'): - key_type = "RSA1024" - key_content = self.settings.get('private_key') - self.common.log('Onion', 'start_onion_service', 'Starting a hidden service with a saved private key') + try: + # is the key a v2 key? + key = onionkey.is_v2_key(self.settings.get('private_key')) + key_type = "RSA1024" + key_content = self.settings.get('private_key') + # The below section is commented out because re-publishing + # a pre-prepared v3 private key is currently unstable in Tor. + # This is fixed upstream but won't reach stable until 0.3.5 + # (expected in December 2018) + # See https://trac.torproject.org/projects/tor/ticket/25552 + # Until then, we will deliberately not work with 'persistent' + # v3 onions, which should not be possible via the GUI settings + # anyway. + # Our ticket: https://github.com/micahflee/onionshare/issues/677 + except: + pass + # Assume it was a v3 key + # key_type = "ED25519-V3" + # key_content = self.settings.get('private_key') + self.common.log('Onion', 'Starting a hidden service with a saved private key') else: - key_type = "NEW" - key_content = "RSA1024" - self.common.log('Onion', 'start_onion_service', 'Starting a hidden service with a new private key') + # Work out if we can support v3 onion services, which are preferred + if Version(self.tor_version) >= Version('0.3.3'): + key_type = "ED25519-V3" + key_content = onionkey.generate_v3_private_key()[0] + else: + # fall back to v2 onion services + key_type = "RSA1024" + key_content = onionkey.generate_v2_private_key()[0] + self.common.log('Onion', 'Starting a hidden service with a new private key') + + # v3 onions don't yet support basic auth. Our ticket: + # https://github.com/micahflee/onionshare/issues/697 + if key_type == "ED25519-V3": + basic_auth = None + self.stealth = False try: if basic_auth != None: - res = self.c.create_ephemeral_hidden_service({ 80: port }, await_publication=True, basic_auth=basic_auth, key_type = key_type, key_content=key_content) + res = self.c.create_ephemeral_hidden_service({ 80: port }, await_publication=True, basic_auth=basic_auth, key_type=key_type, key_content=key_content) else: # if the stem interface is older than 1.5.0, basic_auth isn't a valid keyword arg - res = self.c.create_ephemeral_hidden_service({ 80: port }, await_publication=True, key_type = key_type, key_content=key_content) + res = self.c.create_ephemeral_hidden_service({ 80: port }, await_publication=True, key_type=key_type, key_content=key_content) except ProtocolError: raise TorErrorProtocolError(strings._('error_tor_protocol_error')) @@ -468,7 +499,7 @@ class Onion(object): # A new private key was generated and is in the Control port response. if self.settings.get('save_private_key'): if not self.settings.get('private_key'): - self.settings.set('private_key', res.private_key) + self.settings.set('private_key', key_content) if self.stealth: # Similar to the PrivateKey, the Control port only returns the ClientAuth diff --git a/onionshare/onionkey.py b/onionshare/onionkey.py new file mode 100644 index 00000000..89c781ab --- /dev/null +++ b/onionshare/onionkey.py @@ -0,0 +1,126 @@ +# -*- coding: utf-8 -*- +""" +OnionShare | https://onionshare.org/ + +Copyright (C) 2017 Micah Lee + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +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 +import sys + +import base64 +import hashlib +# Need sha3 if python version is older than 3.6, otherwise +# we can't use hashlib.sha3_256 +if sys.version_info < (3, 6): + import sha3 + +import nacl.signing + +from Crypto.PublicKey import RSA + +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric import rsa + + +b = 256 + +def bit(h, i): + return (h[i // 8] >> (i % 8)) & 1 + + +def encodeint(y): + bits = [(y >> i) & 1 for i in range(b)] + return b''.join([bytes([(sum([bits[i * 8 + j] << j for j in range(8)]))]) for i in range(b // 8)]) + + +def H(m): + return hashlib.sha512(m).digest() + + +def expandSK(sk): + h = H(sk) + a = 2 ** (b - 2) + sum(2 ** i * bit(h, i) for i in range(3, b - 2)) + k = b''.join([bytes([h[i]]) for i in range(b // 8, b // 4)]) + assert len(k) == 32 + return encodeint(a) + k + + +def onion_url_from_private_key(private_key): + """ + Derives the public key (.onion hostname) from a v3-style + Onion private key. + """ + private_key = nacl.signing.SigningKey(seed=private_key) + pubkey = bytes(private_key.verify_key) + version = b'\x03' + checksum = hashlib.sha3_256(b".onion checksum" + pubkey + version).digest()[:2] + onion_address = "http://{}.onion".format(base64.b32encode(pubkey + checksum + version).decode().lower()) + return onion_address + + +def generate_v3_private_key(): + """ + Generates a private and public key for use with v3 style Onions. + Returns both the private key as well as the public key (.onion hostname) + """ + secretKey = os.urandom(32) + expandedSecretKey = expandSK(secretKey) + private_key = base64.b64encode(expandedSecretKey).decode() + return (private_key, onion_url_from_private_key(secretKey)) + +def generate_v2_private_key(): + """ + Generates a private and public key for use with v2 style Onions. + Returns both the serialized private key (compatible with Stem) + as well as the public key (.onion hostname) + """ + # Generate v2 Onion Service private key + private_key = rsa.generate_private_key(public_exponent=65537, + key_size=1024, + backend=default_backend()) + hs_public_key = private_key.public_key() + + # Pre-generate the public key (.onion hostname) + der_format = hs_public_key.public_bytes(encoding=serialization.Encoding.DER, + format=serialization.PublicFormat.PKCS1) + + onion_url = base64.b32encode(hashlib.sha1(der_format).digest()[:-10]).lower().decode() + + # Generate Stem-compatible key content + pem_format = private_key.private_bytes(encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.TraditionalOpenSSL, + encryption_algorithm=serialization.NoEncryption()) + serialized_key = ''.join(pem_format.decode().split('\n')[1:-2]) + + return (serialized_key, onion_url) + +def is_v2_key(key): + """ + Helper function for determining if a key is RSA1024 (v2) or not. + """ + try: + # Import the key + key = RSA.importKey(base64.b64decode(key)) + # Is this a v2 Onion key? (1024 bits) If so, we should keep using it. + if key.n.bit_length() == 1024: + return True + else: + return False + except: + return False + diff --git a/onionshare_gui/server_status.py b/onionshare_gui/server_status.py index 1562ee10..46310605 100644 --- a/onionshare_gui/server_status.py +++ b/onionshare_gui/server_status.py @@ -18,6 +18,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . """ import platform +import textwrap from PyQt5 import QtCore, QtWidgets, QtGui from onionshare import strings @@ -88,7 +89,7 @@ class ServerStatus(QtWidgets.QWidget): self.url = QtWidgets.QLabel() self.url.setFont(url_font) self.url.setWordWrap(True) - self.url.setMinimumHeight(60) + self.url.setMinimumHeight(65) self.url.setMinimumSize(self.url.sizeHint()) self.url.setStyleSheet(self.common.css['server_status_url']) @@ -162,7 +163,13 @@ class ServerStatus(QtWidgets.QWidget): else: self.url_description.setToolTip(strings._('gui_url_label_stay_open', True)) - self.url.setText(self.get_url()) + # Wrap the Onion URL if it's a big v3 one + url_length=len(self.get_url()) + if url_length > 60: + wrapped_onion_url = textwrap.fill(self.get_url(), 50) + self.url.setText(wrapped_onion_url) + else: + self.url.setText(self.get_url()) self.url.show() self.copy_url_button.show() diff --git a/onionshare_gui/settings_dialog.py b/onionshare_gui/settings_dialog.py index 94480205..a41226f6 100644 --- a/onionshare_gui/settings_dialog.py +++ b/onionshare_gui/settings_dialog.py @@ -19,6 +19,7 @@ along with this program. If not, see . """ from PyQt5 import QtCore, QtWidgets, QtGui import sys, platform, datetime, re +from distutils.version import LooseVersion as Version from onionshare import strings, common from onionshare.settings import Settings @@ -64,7 +65,7 @@ class SettingsDialog(QtWidgets.QDialog): self.shutdown_timeout_checkbox.setCheckState(QtCore.Qt.Checked) self.shutdown_timeout_checkbox.setText(strings._("gui_settings_shutdown_timeout_checkbox", True)) - # Whether or not to save the Onion private key for reuse + # Whether or not to save the Onion private key for reuse (persistent URLs) self.save_private_key_checkbox = QtWidgets.QCheckBox() self.save_private_key_checkbox.setCheckState(QtCore.Qt.Unchecked) self.save_private_key_checkbox.setText(strings._("gui_save_private_key_checkbox", True)) @@ -421,6 +422,9 @@ class SettingsDialog(QtWidgets.QDialog): self.save_private_key_checkbox.setCheckState(QtCore.Qt.Checked) else: self.save_private_key_checkbox.setCheckState(QtCore.Qt.Unchecked) + # Using persistent URLs with v3 onions is not yet stable + if Version(self.onion.tor_version) >= Version('0.3.2.9'): + self.save_private_key_checkbox.hide() downloads_dir = self.old_settings.get('downloads_dir') self.downloads_dir_lineedit.setText(downloads_dir) @@ -445,6 +449,9 @@ class SettingsDialog(QtWidgets.QDialog): self.hidservauth_copy_button.show() else: self.stealth_checkbox.setCheckState(QtCore.Qt.Unchecked) + # Using Client Auth with v3 onions is not yet possible + if Version(self.onion.tor_version) >= Version('0.3.2.9'): + stealth_group.hide() use_autoupdate = self.old_settings.get('use_autoupdate') if use_autoupdate: From 5c8b0d77965caba786677098bc01b4aab152e480 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Wed, 22 Aug 2018 11:45:08 +1000 Subject: [PATCH 02/21] Rather than hide persistence/stealth mode altogether if the Tor version is high enough for v3, give the user the option to 'use legacy v2 onions' in Settings dialog, so that they may continue to use persistence etc --- onionshare/onion.py | 4 +- onionshare/settings.py | 1 + onionshare_gui/settings_dialog.py | 88 ++++++++++++++++++++++++++++--- 3 files changed, 85 insertions(+), 8 deletions(-) diff --git a/onionshare/onion.py b/onionshare/onion.py index 73c94600..18ebeb5d 100644 --- a/onionshare/onion.py +++ b/onionshare/onion.py @@ -468,7 +468,7 @@ class Onion(object): self.common.log('Onion', 'Starting a hidden service with a saved private key') else: # Work out if we can support v3 onion services, which are preferred - if Version(self.tor_version) >= Version('0.3.3'): + if Version(self.tor_version) >= Version('0.3.2.9') and not self.settings.get('use_legacy_v2_onions'): key_type = "ED25519-V3" key_content = onionkey.generate_v3_private_key()[0] else: @@ -479,7 +479,7 @@ class Onion(object): # v3 onions don't yet support basic auth. Our ticket: # https://github.com/micahflee/onionshare/issues/697 - if key_type == "ED25519-V3": + if key_type == "ED25519-V3" and not self.settings.get('use_legacy_v2_onions'): basic_auth = None self.stealth = False diff --git a/onionshare/settings.py b/onionshare/settings.py index 6d551ca0..9968a828 100644 --- a/onionshare/settings.py +++ b/onionshare/settings.py @@ -68,6 +68,7 @@ class Settings(object): 'tor_bridges_use_meek_lite_amazon': False, 'tor_bridges_use_meek_lite_azure': False, 'tor_bridges_use_custom_bridges': '', + 'use_legacy_v2_onions': False, 'save_private_key': False, 'private_key': '', 'slug': '', diff --git a/onionshare_gui/settings_dialog.py b/onionshare_gui/settings_dialog.py index a41226f6..8574bc6e 100644 --- a/onionshare_gui/settings_dialog.py +++ b/onionshare_gui/settings_dialog.py @@ -65,15 +65,25 @@ class SettingsDialog(QtWidgets.QDialog): self.shutdown_timeout_checkbox.setCheckState(QtCore.Qt.Checked) self.shutdown_timeout_checkbox.setText(strings._("gui_settings_shutdown_timeout_checkbox", True)) + # Whether or not to use legacy v2 onions + self.use_legacy_v2_onions_checkbox = QtWidgets.QCheckBox() + self.use_legacy_v2_onions_checkbox.setCheckState(QtCore.Qt.Unchecked) + self.use_legacy_v2_onions_checkbox.setText(strings._("gui_use_legacy_v2_onions_checkbox", True)) + self.use_legacy_v2_onions_checkbox.clicked.connect(self.use_legacy_v2_onions_clicked) + if Version(self.onion.tor_version) < Version('0.3.2.9'): + self.use_legacy_v2_onions_checkbox.hide() + # Whether or not to save the Onion private key for reuse (persistent URLs) self.save_private_key_checkbox = QtWidgets.QCheckBox() self.save_private_key_checkbox.setCheckState(QtCore.Qt.Unchecked) self.save_private_key_checkbox.setText(strings._("gui_save_private_key_checkbox", True)) + self.save_private_key_checkbox.clicked.connect(self.save_private_key_checkbox_clicked) # Sharing options layout sharing_group_layout = QtWidgets.QVBoxLayout() sharing_group_layout.addWidget(self.close_after_first_download_checkbox) sharing_group_layout.addWidget(self.shutdown_timeout_checkbox) + sharing_group_layout.addWidget(self.use_legacy_v2_onions_checkbox) sharing_group_layout.addWidget(self.save_private_key_checkbox) sharing_group = QtWidgets.QGroupBox(strings._("gui_settings_sharing_label", True)) sharing_group.setLayout(sharing_group_layout) @@ -118,6 +128,7 @@ class SettingsDialog(QtWidgets.QDialog): self.stealth_checkbox = QtWidgets.QCheckBox() self.stealth_checkbox.setCheckState(QtCore.Qt.Unchecked) self.stealth_checkbox.setText(strings._("gui_settings_stealth_option", True)) + self.stealth_checkbox.clicked.connect(self.stealth_checkbox_clicked_connect) hidservauth_details = QtWidgets.QLabel(strings._('gui_settings_stealth_hidservauth_string', True)) hidservauth_details.setWordWrap(True) @@ -134,8 +145,8 @@ class SettingsDialog(QtWidgets.QDialog): stealth_group_layout.addWidget(self.stealth_checkbox) stealth_group_layout.addWidget(hidservauth_details) stealth_group_layout.addWidget(self.hidservauth_copy_button) - stealth_group = QtWidgets.QGroupBox(strings._("gui_settings_stealth_label", True)) - stealth_group.setLayout(stealth_group_layout) + self.stealth_group = QtWidgets.QGroupBox(strings._("gui_settings_stealth_label", True)) + self.stealth_group.setLayout(stealth_group_layout) # Automatic updates options @@ -380,7 +391,7 @@ class SettingsDialog(QtWidgets.QDialog): left_col_layout = QtWidgets.QVBoxLayout() left_col_layout.addWidget(sharing_group) left_col_layout.addWidget(receiving_group) - left_col_layout.addWidget(stealth_group) + left_col_layout.addWidget(self.stealth_group) left_col_layout.addWidget(autoupdate_group) left_col_layout.addStretch() @@ -417,15 +428,25 @@ class SettingsDialog(QtWidgets.QDialog): else: self.shutdown_timeout_checkbox.setCheckState(QtCore.Qt.Unchecked) + use_legacy_v2_onions = self.old_settings.get('use_legacy_v2_onions') + save_private_key = self.old_settings.get('save_private_key') if save_private_key: self.save_private_key_checkbox.setCheckState(QtCore.Qt.Checked) + # Legacy v2 mode is forced on if persistence is enabled + self.use_legacy_v2_onions_checkbox.setEnabled(False) else: self.save_private_key_checkbox.setCheckState(QtCore.Qt.Unchecked) + self.use_legacy_v2_onions_checkbox.setEnabled(True) # Using persistent URLs with v3 onions is not yet stable - if Version(self.onion.tor_version) >= Version('0.3.2.9'): + if Version(self.onion.tor_version) >= Version('0.3.2.9') and not use_legacy_v2_onions: self.save_private_key_checkbox.hide() + if use_legacy_v2_onions or save_private_key: + self.use_legacy_v2_onions_checkbox.setCheckState(QtCore.Qt.Checked) + self.save_private_key_checkbox.show() + self.stealth_group.show() + downloads_dir = self.old_settings.get('downloads_dir') self.downloads_dir_lineedit.setText(downloads_dir) @@ -444,14 +465,17 @@ class SettingsDialog(QtWidgets.QDialog): use_stealth = self.old_settings.get('use_stealth') if use_stealth: self.stealth_checkbox.setCheckState(QtCore.Qt.Checked) + # Legacy v2 mode is forced on if Stealth is enabled + self.use_legacy_v2_onions_checkbox.setEnabled(False) if save_private_key: hidservauth_details.show() self.hidservauth_copy_button.show() else: self.stealth_checkbox.setCheckState(QtCore.Qt.Unchecked) + self.use_legacy_v2_onions_checkbox.setEnabled(True) # Using Client Auth with v3 onions is not yet possible - if Version(self.onion.tor_version) >= Version('0.3.2.9'): - stealth_group.hide() + if not use_legacy_v2_onions: + self.stealth_group.hide() use_autoupdate = self.old_settings.get('use_autoupdate') if use_autoupdate: @@ -627,6 +651,37 @@ class SettingsDialog(QtWidgets.QDialog): clipboard = self.qtapp.clipboard() clipboard.setText(self.old_settings.get('hidservauth_string')) + def use_legacy_v2_onions_clicked(self, checked): + """ + Show the persistent and stealth options since we're using legacy onions. + """ + if checked: + self.save_private_key_checkbox.show() + self.stealth_group.show() + else: + self.save_private_key_checkbox.hide() + self.stealth_group.hide() + + def save_private_key_checkbox_clicked(self, checked): + """ + Prevent the v2 legacy mode being switched off if persistence is enabled + """ + if checked: + self.use_legacy_v2_onions_checkbox.setCheckState(QtCore.Qt.Checked) + self.use_legacy_v2_onions_checkbox.setEnabled(False) + else: + self.use_legacy_v2_onions_checkbox.setEnabled(True) + + def stealth_checkbox_clicked_connect(self, checked): + """ + Prevent the v2 legacy mode being switched off if stealth is enabled + """ + if checked: + self.use_legacy_v2_onions_checkbox.setCheckState(QtCore.Qt.Checked) + self.use_legacy_v2_onions_checkbox.setEnabled(False) + else: + self.use_legacy_v2_onions_checkbox.setEnabled(True) + def downloads_button_clicked(self): """ Browse for a new downloads directory @@ -813,7 +868,16 @@ class SettingsDialog(QtWidgets.QDialog): settings.set('close_after_first_download', self.close_after_first_download_checkbox.isChecked()) settings.set('shutdown_timeout', self.shutdown_timeout_checkbox.isChecked()) + + # Complicated logic here to force v2 onion mode on or off depending on other settings + if self.use_legacy_v2_onions_checkbox.isChecked(): + use_legacy_v2_onions = True + else: + use_legacy_v2_onions = False + if self.save_private_key_checkbox.isChecked(): + # force the legacy mode on + use_legacy_v2_onions = True settings.set('save_private_key', True) settings.set('private_key', self.old_settings.get('private_key')) settings.set('slug', self.old_settings.get('slug')) @@ -824,6 +888,18 @@ class SettingsDialog(QtWidgets.QDialog): settings.set('slug', '') # Also unset the HidServAuth if we are removing our reusable private key settings.set('hidservauth_string', '') + + if use_legacy_v2_onions: + settings.set('use_legacy_v2_onions', True) + else: + settings.set('use_legacy_v2_onions', False) + # If we are not using legacy mode, but we previously had persistence turned on, force it off! + settings.set('save_private_key', False) + settings.set('private_key', '') + settings.set('slug', '') + # Also unset the HidServAuth if we are removing our reusable private key + settings.set('hidservauth_string', '') + settings.set('downloads_dir', self.downloads_dir_lineedit.text()) settings.set('receive_allow_receiver_shutdown', self.receive_allow_receiver_shutdown_checkbox.isChecked()) settings.set('receive_public_mode', self.receive_public_mode_checkbox.isChecked()) From 16430f5fad1df7747848154d9e3dbf8e6e4a5249 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Wed, 22 Aug 2018 11:50:16 +1000 Subject: [PATCH 03/21] Fix tests --- test/test_onionshare_settings.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test_onionshare_settings.py b/test/test_onionshare_settings.py index 3942ab8c..19851db5 100644 --- a/test/test_onionshare_settings.py +++ b/test/test_onionshare_settings.py @@ -60,6 +60,7 @@ class TestSettings: 'tor_bridges_use_meek_lite_amazon': False, 'tor_bridges_use_meek_lite_azure': False, 'tor_bridges_use_custom_bridges': '', + 'use_legacy_v2_onions': False, 'save_private_key': False, 'private_key': '', 'slug': '', From f0efb10d08a51c8176773ea7bb432cbaa36de3ec Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Wed, 22 Aug 2018 17:03:15 +1000 Subject: [PATCH 04/21] Add missing locale key for legacy v2 onions --- share/locale/en.json | 1 + 1 file changed, 1 insertion(+) diff --git a/share/locale/en.json b/share/locale/en.json index b1d247d9..8205a29a 100644 --- a/share/locale/en.json +++ b/share/locale/en.json @@ -153,6 +153,7 @@ "gui_server_started_after_timeout": "The server started after your chosen auto-timeout.\nPlease start a new share.", "gui_server_timeout_expired": "The chosen timeout has already expired.\nPlease update the timeout and then you may start sharing.", "share_via_onionshare": "Share via OnionShare", + "gui_use_legacy_v2_onions_checkbox": "Use legacy (v2) .onion addresses?", "gui_save_private_key_checkbox": "Use a persistent address\n(unchecking will delete any saved addresses)", "gui_share_url_description": "Anyone with this link can download your files using the Tor Browser: ", "gui_receive_url_description": "Anyone with this link can upload files to your computer using the Tor Browser: ", From 47fc55aac1d200ca9624d60ac91af20e3d120216 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Thu, 23 Aug 2018 11:02:28 +1000 Subject: [PATCH 05/21] Don't try and parse Tor version in order whether or not to show v2-only features. Just note in the QLabel what is v2-only. Still force v2 legacy mode on when using persistence or stealth. --- onionshare_gui/settings_dialog.py | 35 +++++++------------------------ share/locale/en.json | 4 ++-- 2 files changed, 9 insertions(+), 30 deletions(-) diff --git a/onionshare_gui/settings_dialog.py b/onionshare_gui/settings_dialog.py index 8574bc6e..b2515a69 100644 --- a/onionshare_gui/settings_dialog.py +++ b/onionshare_gui/settings_dialog.py @@ -19,7 +19,6 @@ along with this program. If not, see . """ from PyQt5 import QtCore, QtWidgets, QtGui import sys, platform, datetime, re -from distutils.version import LooseVersion as Version from onionshare import strings, common from onionshare.settings import Settings @@ -69,9 +68,6 @@ class SettingsDialog(QtWidgets.QDialog): self.use_legacy_v2_onions_checkbox = QtWidgets.QCheckBox() self.use_legacy_v2_onions_checkbox.setCheckState(QtCore.Qt.Unchecked) self.use_legacy_v2_onions_checkbox.setText(strings._("gui_use_legacy_v2_onions_checkbox", True)) - self.use_legacy_v2_onions_checkbox.clicked.connect(self.use_legacy_v2_onions_clicked) - if Version(self.onion.tor_version) < Version('0.3.2.9'): - self.use_legacy_v2_onions_checkbox.hide() # Whether or not to save the Onion private key for reuse (persistent URLs) self.save_private_key_checkbox = QtWidgets.QCheckBox() @@ -145,8 +141,8 @@ class SettingsDialog(QtWidgets.QDialog): stealth_group_layout.addWidget(self.stealth_checkbox) stealth_group_layout.addWidget(hidservauth_details) stealth_group_layout.addWidget(self.hidservauth_copy_button) - self.stealth_group = QtWidgets.QGroupBox(strings._("gui_settings_stealth_label", True)) - self.stealth_group.setLayout(stealth_group_layout) + stealth_group = QtWidgets.QGroupBox(strings._("gui_settings_stealth_label", True)) + stealth_group.setLayout(stealth_group_layout) # Automatic updates options @@ -391,7 +387,7 @@ class SettingsDialog(QtWidgets.QDialog): left_col_layout = QtWidgets.QVBoxLayout() left_col_layout.addWidget(sharing_group) left_col_layout.addWidget(receiving_group) - left_col_layout.addWidget(self.stealth_group) + left_col_layout.addWidget(stealth_group) left_col_layout.addWidget(autoupdate_group) left_col_layout.addStretch() @@ -438,14 +434,9 @@ class SettingsDialog(QtWidgets.QDialog): else: self.save_private_key_checkbox.setCheckState(QtCore.Qt.Unchecked) self.use_legacy_v2_onions_checkbox.setEnabled(True) - # Using persistent URLs with v3 onions is not yet stable - if Version(self.onion.tor_version) >= Version('0.3.2.9') and not use_legacy_v2_onions: - self.save_private_key_checkbox.hide() if use_legacy_v2_onions or save_private_key: self.use_legacy_v2_onions_checkbox.setCheckState(QtCore.Qt.Checked) - self.save_private_key_checkbox.show() - self.stealth_group.show() downloads_dir = self.old_settings.get('downloads_dir') self.downloads_dir_lineedit.setText(downloads_dir) @@ -473,9 +464,6 @@ class SettingsDialog(QtWidgets.QDialog): else: self.stealth_checkbox.setCheckState(QtCore.Qt.Unchecked) self.use_legacy_v2_onions_checkbox.setEnabled(True) - # Using Client Auth with v3 onions is not yet possible - if not use_legacy_v2_onions: - self.stealth_group.hide() use_autoupdate = self.old_settings.get('use_autoupdate') if use_autoupdate: @@ -651,17 +639,6 @@ class SettingsDialog(QtWidgets.QDialog): clipboard = self.qtapp.clipboard() clipboard.setText(self.old_settings.get('hidservauth_string')) - def use_legacy_v2_onions_clicked(self, checked): - """ - Show the persistent and stealth options since we're using legacy onions. - """ - if checked: - self.save_private_key_checkbox.show() - self.stealth_group.show() - else: - self.save_private_key_checkbox.hide() - self.stealth_group.hide() - def save_private_key_checkbox_clicked(self, checked): """ Prevent the v2 legacy mode being switched off if persistence is enabled @@ -670,7 +647,8 @@ class SettingsDialog(QtWidgets.QDialog): self.use_legacy_v2_onions_checkbox.setCheckState(QtCore.Qt.Checked) self.use_legacy_v2_onions_checkbox.setEnabled(False) else: - self.use_legacy_v2_onions_checkbox.setEnabled(True) + if not self.stealth_checkbox.isChecked(): + self.use_legacy_v2_onions_checkbox.setEnabled(True) def stealth_checkbox_clicked_connect(self, checked): """ @@ -680,7 +658,8 @@ class SettingsDialog(QtWidgets.QDialog): self.use_legacy_v2_onions_checkbox.setCheckState(QtCore.Qt.Checked) self.use_legacy_v2_onions_checkbox.setEnabled(False) else: - self.use_legacy_v2_onions_checkbox.setEnabled(True) + if not self.save_private_key_checkbox.isChecked(): + self.use_legacy_v2_onions_checkbox.setEnabled(True) def downloads_button_clicked(self): """ diff --git a/share/locale/en.json b/share/locale/en.json index 8205a29a..d73fb369 100644 --- a/share/locale/en.json +++ b/share/locale/en.json @@ -84,7 +84,7 @@ "gui_settings_window_title": "Settings", "gui_settings_stealth_label": "Stealth (advanced)", "gui_settings_stealth_option": "Create stealth onion services", - "gui_settings_stealth_option_details": "This makes OnionShare more secure, but also more difficult for the recipient to connect to.
More information.", + "gui_settings_stealth_option_details": "This makes OnionShare more secure, but also more difficult for the recipient to connect to.

NOTE: currently this only works with v2 onions.
More information.", "gui_settings_stealth_hidservauth_string": "You have saved the private key for reuse, so your HidServAuth string is also reused.\nClick below to copy the HidServAuth.", "gui_settings_autoupdate_label": "Check for upgrades", "gui_settings_autoupdate_option": "Notify me when upgrades are available", @@ -154,7 +154,7 @@ "gui_server_timeout_expired": "The chosen timeout has already expired.\nPlease update the timeout and then you may start sharing.", "share_via_onionshare": "Share via OnionShare", "gui_use_legacy_v2_onions_checkbox": "Use legacy (v2) .onion addresses?", - "gui_save_private_key_checkbox": "Use a persistent address\n(unchecking will delete any saved addresses)", + "gui_save_private_key_checkbox": "Use a persistent (v2 only) address\n(unchecking will delete any saved addresses)", "gui_share_url_description": "Anyone with this link can download your files using the Tor Browser: ", "gui_receive_url_description": "Anyone with this link can upload files to your computer using the Tor Browser: ", "gui_url_label_persistent": "This share will not expire automatically unless a timer is set.

Every share will have the same address (to use one-time addresses, disable persistence in Settings)", From 7879697ec6966deabf732b841afee9e0a0bf681f Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Tue, 28 Aug 2018 09:33:49 +1000 Subject: [PATCH 06/21] Only wrap the v3 onion if the window is too small to show it unwrapped --- onionshare_gui/server_status.py | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/onionshare_gui/server_status.py b/onionshare_gui/server_status.py index 46310605..c0409749 100644 --- a/onionshare_gui/server_status.py +++ b/onionshare_gui/server_status.py @@ -57,6 +57,8 @@ class ServerStatus(QtWidgets.QWidget): self.web = None + self.resizeEvent(None) + # Shutdown timeout layout self.shutdown_timeout_label = QtWidgets.QLabel(strings._('gui_settings_shutdown_timeout', True)) self.shutdown_timeout = QtWidgets.QDateTimeEdit() @@ -129,6 +131,24 @@ class ServerStatus(QtWidgets.QWidget): self.update() + def resizeEvent(self, event): + """ + When the widget is resized, try and adjust the display of a v3 onion URL. + """ + try: + self.get_url() + url_length=len(self.get_url()) + if url_length > 60: + width = self.frameGeometry().width() + if width < 530: + wrapped_onion_url = textwrap.fill(self.get_url(), 50) + self.url.setText(wrapped_onion_url) + else: + self.url.setText(self.get_url()) + except: + pass + + def shutdown_timeout_reset(self): """ Reset the timeout in the UI after stopping a share @@ -163,13 +183,7 @@ class ServerStatus(QtWidgets.QWidget): else: self.url_description.setToolTip(strings._('gui_url_label_stay_open', True)) - # Wrap the Onion URL if it's a big v3 one - url_length=len(self.get_url()) - if url_length > 60: - wrapped_onion_url = textwrap.fill(self.get_url(), 50) - self.url.setText(wrapped_onion_url) - else: - self.url.setText(self.get_url()) + self.url.setText(self.get_url()) self.url.show() self.copy_url_button.show() From f9e614eba125846dc8739fb8e09f04a6f432da36 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Thu, 13 Sep 2018 11:35:28 +1000 Subject: [PATCH 07/21] Update cryptography dependency to 2.3.1 --- install/requirements-windows.txt | 2 +- install/requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/install/requirements-windows.txt b/install/requirements-windows.txt index 611edf3c..f016c523 100644 --- a/install/requirements-windows.txt +++ b/install/requirements-windows.txt @@ -1,5 +1,5 @@ click==6.7 -cryptography==2.1.4 +cryptography==2.3.1 Flask==0.12.2 future==0.16.0 itsdangerous==0.24 diff --git a/install/requirements.txt b/install/requirements.txt index 964030e8..15e9a108 100644 --- a/install/requirements.txt +++ b/install/requirements.txt @@ -1,5 +1,5 @@ click==6.7 -cryptography==2.1.4 +cryptography==2.3.1 Flask==0.12.2 itsdangerous==0.24 Jinja2==2.10 From 8955ce0699f391f1950d2029e4aeaf615245285f Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Thu, 13 Sep 2018 12:21:38 +1000 Subject: [PATCH 08/21] Refactor the onionkey stuff to be more like @maqp's revised version (thanks) --- onionshare/onionkey.py | 60 ++++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/onionshare/onionkey.py b/onionshare/onionkey.py index 89c781ab..e9dcc9c4 100644 --- a/onionshare/onionkey.py +++ b/onionshare/onionkey.py @@ -37,39 +37,40 @@ from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import rsa -b = 256 +def stem_compatible_base64_blob_from_private_key(private_key_seed: bytes) -> str: + """ + Provides a base64-encoded private key for v3-style Onions. + """ + b = 256 -def bit(h, i): - return (h[i // 8] >> (i % 8)) & 1 + def bit(h: bytes, i: int) -> int: + return (h[i // 8] >> (i % 8)) & 1 + + def encode_int(y: int) -> bytes: + bits = [(y >> i) & 1 for i in range(b)] + return b''.join([bytes([(sum([bits[i * 8 + j] << j for j in range(8)]))]) for i in range(b // 8)]) + + def expand_private_key(sk: bytes) -> bytes: + h = hashlib.sha512(sk).digest() + a = 2 ** (b - 2) + sum(2 ** i * bit(h, i) for i in range(3, b - 2)) + k = b''.join([bytes([h[i]]) for i in range(b // 8, b // 4)]) + assert len(k) == 32 + return encode_int(a) + k + + expanded_private_key = expand_private_key(private_key_seed) + return base64.b64encode(expanded_private_key).decode() -def encodeint(y): - bits = [(y >> i) & 1 for i in range(b)] - return b''.join([bytes([(sum([bits[i * 8 + j] << j for j in range(8)]))]) for i in range(b // 8)]) - - -def H(m): - return hashlib.sha512(m).digest() - - -def expandSK(sk): - h = H(sk) - a = 2 ** (b - 2) + sum(2 ** i * bit(h, i) for i in range(3, b - 2)) - k = b''.join([bytes([h[i]]) for i in range(b // 8, b // 4)]) - assert len(k) == 32 - return encodeint(a) + k - - -def onion_url_from_private_key(private_key): +def onion_url_from_private_key(private_key_seed: bytes) -> str: """ Derives the public key (.onion hostname) from a v3-style Onion private key. """ - private_key = nacl.signing.SigningKey(seed=private_key) - pubkey = bytes(private_key.verify_key) + signing_key = nacl.signing.SigningKey(seed=private_key_seed) + public_key = bytes(signing_key.verify_key) version = b'\x03' - checksum = hashlib.sha3_256(b".onion checksum" + pubkey + version).digest()[:2] - onion_address = "http://{}.onion".format(base64.b32encode(pubkey + checksum + version).decode().lower()) + checksum = hashlib.sha3_256(b".onion checksum" + public_key + version).digest()[:2] + onion_address = "http://{}.onion".format(base64.b32encode(public_key + checksum + version).decode().lower()) return onion_address @@ -78,10 +79,10 @@ def generate_v3_private_key(): Generates a private and public key for use with v3 style Onions. Returns both the private key as well as the public key (.onion hostname) """ - secretKey = os.urandom(32) - expandedSecretKey = expandSK(secretKey) - private_key = base64.b64encode(expandedSecretKey).decode() - return (private_key, onion_url_from_private_key(secretKey)) + private_key_seed = os.urandom(32) + private_key = stem_compatible_base64_blob_from_private_key(private_key_seed) + return (private_key, onion_url_from_private_key(private_key_seed)) + def generate_v2_private_key(): """ @@ -109,6 +110,7 @@ def generate_v2_private_key(): return (serialized_key, onion_url) + def is_v2_key(key): """ Helper function for determining if a key is RSA1024 (v2) or not. From 0b0eef724561eec094482c1c7961e40bc62fbb13 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Thu, 13 Sep 2018 12:29:48 +1000 Subject: [PATCH 09/21] More clarity for the returned values in generate_v3_private_key(), also more consistent with generate_v2_private_key() --- onionshare/onionkey.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/onionshare/onionkey.py b/onionshare/onionkey.py index e9dcc9c4..d2c6ad17 100644 --- a/onionshare/onionkey.py +++ b/onionshare/onionkey.py @@ -81,7 +81,8 @@ def generate_v3_private_key(): """ private_key_seed = os.urandom(32) private_key = stem_compatible_base64_blob_from_private_key(private_key_seed) - return (private_key, onion_url_from_private_key(private_key_seed)) + onion_url = onion_url_from_private_key(private_key_seed) + return (private_key, onion_url) def generate_v2_private_key(): From 25eed81b004532481a6e794dca6698a6adc08920 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Thu, 13 Sep 2018 16:35:24 +1000 Subject: [PATCH 10/21] Fixing a future check for persistent v3 onions (still disabled for now) --- onionshare/onion.py | 17 ++++++++--------- share/locale/en.json | 1 + 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/onionshare/onion.py b/onionshare/onion.py index 18ebeb5d..031f418a 100644 --- a/onionshare/onion.py +++ b/onionshare/onion.py @@ -446,11 +446,10 @@ class Onion(object): basic_auth = None if self.settings.get('private_key'): - try: - # is the key a v2 key? - key = onionkey.is_v2_key(self.settings.get('private_key')) + key_content = self.settings.get('private_key') + # is the key a v2 key? + if onionkey.is_v2_key(key_content): key_type = "RSA1024" - key_content = self.settings.get('private_key') # The below section is commented out because re-publishing # a pre-prepared v3 private key is currently unstable in Tor. # This is fixed upstream but won't reach stable until 0.3.5 @@ -460,11 +459,11 @@ class Onion(object): # v3 onions, which should not be possible via the GUI settings # anyway. # Our ticket: https://github.com/micahflee/onionshare/issues/677 - except: - pass - # Assume it was a v3 key - # key_type = "ED25519-V3" - # key_content = self.settings.get('private_key') + # + # Assume it was a v3 key + # key_type = "ED25519-V3" + else: + raise TorErrorProtocolError(strings._('error_invalid_private_key')) self.common.log('Onion', 'Starting a hidden service with a saved private key') else: # Work out if we can support v3 onion services, which are preferred diff --git a/share/locale/en.json b/share/locale/en.json index d73fb369..06301e80 100644 --- a/share/locale/en.json +++ b/share/locale/en.json @@ -139,6 +139,7 @@ "settings_error_bundled_tor_broken": "OnionShare could not connect to Tor in the background:\n{}", "settings_test_success": "Congratulations, OnionShare can connect to the Tor controller.\n\nTor version: {}\nSupports ephemeral onion services: {}\nSupports stealth onion services: {}", "error_tor_protocol_error": "Could not communicate with the Tor controller.\nIf you're using Whonix, check out https://www.whonix.org/wiki/onionshare to make OnionShare work.", + "error_invalid_private_key": "This private key type is unsupported", "connecting_to_tor": "Connecting to the Tor network", "update_available": "A new version of OnionShare is available. Click here to download it.

Installed version: {}
Latest version: {}", "update_error_check_error": "Error checking for updates: Maybe you're not connected to Tor, or maybe the OnionShare website is down.", From 30ee2290d78837c1c8dfabdc31a9ddfb7098a386 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Sat, 15 Sep 2018 16:07:08 +1000 Subject: [PATCH 11/21] Fix bug where lack of stealth mode re-enabled v2 legacy checkbox even if persistence was still enabled --- onionshare_gui/settings_dialog.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/onionshare_gui/settings_dialog.py b/onionshare_gui/settings_dialog.py index b2515a69..24fea800 100644 --- a/onionshare_gui/settings_dialog.py +++ b/onionshare_gui/settings_dialog.py @@ -463,7 +463,8 @@ class SettingsDialog(QtWidgets.QDialog): self.hidservauth_copy_button.show() else: self.stealth_checkbox.setCheckState(QtCore.Qt.Unchecked) - self.use_legacy_v2_onions_checkbox.setEnabled(True) + if not save_private_key: + self.use_legacy_v2_onions_checkbox.setEnabled(True) use_autoupdate = self.old_settings.get('use_autoupdate') if use_autoupdate: From d8566c2d7868e003faef1228b76fc35940891304 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Sun, 16 Sep 2018 13:54:47 +1000 Subject: [PATCH 12/21] Move stealth to general options, and add hyperlinks for more info for this and legacy addresses --- onionshare_gui/settings_dialog.py | 62 +++++++++++++++---------------- share/locale/en.json | 10 ++--- 2 files changed, 35 insertions(+), 37 deletions(-) diff --git a/onionshare_gui/settings_dialog.py b/onionshare_gui/settings_dialog.py index 5b052375..856ca993 100644 --- a/onionshare_gui/settings_dialog.py +++ b/onionshare_gui/settings_dialog.py @@ -58,6 +58,10 @@ class SettingsDialog(QtWidgets.QDialog): self.use_legacy_v2_onions_checkbox = QtWidgets.QCheckBox() self.use_legacy_v2_onions_checkbox.setCheckState(QtCore.Qt.Unchecked) self.use_legacy_v2_onions_checkbox.setText(strings._("gui_use_legacy_v2_onions_checkbox", True)) + use_legacy_v2_onions_label = QtWidgets.QLabel(strings._("gui_use_legacy_v2_onions_label", True)) + use_legacy_v2_onions_label.setWordWrap(True) + use_legacy_v2_onions_label.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction) + use_legacy_v2_onions_label.setOpenExternalLinks(True) # Whether or not to save the Onion private key for reuse (persistent URL mode) self.save_private_key_checkbox = QtWidgets.QCheckBox() @@ -70,11 +74,37 @@ class SettingsDialog(QtWidgets.QDialog): self.public_mode_checkbox.setCheckState(QtCore.Qt.Unchecked) self.public_mode_checkbox.setText(strings._("gui_settings_public_mode_checkbox", True)) + # Stealth + self.stealth_checkbox = QtWidgets.QCheckBox() + self.stealth_checkbox.setCheckState(QtCore.Qt.Unchecked) + self.stealth_checkbox.setText(strings._("gui_settings_stealth_option", True)) + self.stealth_checkbox.clicked.connect(self.stealth_checkbox_clicked_connect) + stealth_details = QtWidgets.QLabel(strings._("gui_settings_stealth_option_details", True)) + stealth_details.setWordWrap(True) + stealth_details.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction) + stealth_details.setOpenExternalLinks(True) + stealth_details.setMinimumSize(stealth_details.sizeHint()) + + hidservauth_details = QtWidgets.QLabel(strings._('gui_settings_stealth_hidservauth_string', True)) + hidservauth_details.setWordWrap(True) + hidservauth_details.setMinimumSize(hidservauth_details.sizeHint()) + hidservauth_details.hide() + + self.hidservauth_copy_button = QtWidgets.QPushButton(strings._('gui_copy_hidservauth', True)) + self.hidservauth_copy_button.clicked.connect(self.hidservauth_copy_button_clicked) + self.hidservauth_copy_button.hide() + # General options layout general_group_layout = QtWidgets.QVBoxLayout() general_group_layout.addWidget(self.use_legacy_v2_onions_checkbox) + general_group_layout.addWidget(use_legacy_v2_onions_label) general_group_layout.addWidget(self.save_private_key_checkbox) general_group_layout.addWidget(self.public_mode_checkbox) + general_group_layout.addWidget(self.stealth_checkbox) + general_group_layout.addWidget(stealth_details) + general_group_layout.addWidget(hidservauth_details) + general_group_layout.addWidget(self.hidservauth_copy_button) + general_group = QtWidgets.QGroupBox(strings._("gui_settings_general_label", True)) general_group.setLayout(general_group_layout) @@ -120,37 +150,6 @@ class SettingsDialog(QtWidgets.QDialog): receiving_group = QtWidgets.QGroupBox(strings._("gui_settings_receiving_label", True)) receiving_group.setLayout(receiving_group_layout) - # Stealth options - - # Stealth - stealth_details = QtWidgets.QLabel(strings._("gui_settings_stealth_option_details", True)) - stealth_details.setWordWrap(True) - stealth_details.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction) - stealth_details.setOpenExternalLinks(True) - stealth_details.setMinimumSize(stealth_details.sizeHint()) - self.stealth_checkbox = QtWidgets.QCheckBox() - self.stealth_checkbox.setCheckState(QtCore.Qt.Unchecked) - self.stealth_checkbox.setText(strings._("gui_settings_stealth_option", True)) - self.stealth_checkbox.clicked.connect(self.stealth_checkbox_clicked_connect) - - hidservauth_details = QtWidgets.QLabel(strings._('gui_settings_stealth_hidservauth_string', True)) - hidservauth_details.setWordWrap(True) - hidservauth_details.setMinimumSize(hidservauth_details.sizeHint()) - hidservauth_details.hide() - - self.hidservauth_copy_button = QtWidgets.QPushButton(strings._('gui_copy_hidservauth', True)) - self.hidservauth_copy_button.clicked.connect(self.hidservauth_copy_button_clicked) - self.hidservauth_copy_button.hide() - - # Stealth options layout - stealth_group_layout = QtWidgets.QVBoxLayout() - stealth_group_layout.addWidget(stealth_details) - stealth_group_layout.addWidget(self.stealth_checkbox) - stealth_group_layout.addWidget(hidservauth_details) - stealth_group_layout.addWidget(self.hidservauth_copy_button) - stealth_group = QtWidgets.QGroupBox(strings._("gui_settings_stealth_label", True)) - stealth_group.setLayout(stealth_group_layout) - # Automatic updates options # Autoupdate @@ -383,7 +382,6 @@ class SettingsDialog(QtWidgets.QDialog): left_col_layout.addWidget(general_group) left_col_layout.addWidget(sharing_group) left_col_layout.addWidget(receiving_group) - left_col_layout.addWidget(stealth_group) left_col_layout.addWidget(autoupdate_group) left_col_layout.addStretch() diff --git a/share/locale/en.json b/share/locale/en.json index e8649106..0239037a 100644 --- a/share/locale/en.json +++ b/share/locale/en.json @@ -71,9 +71,8 @@ "error_stealth_not_supported": "To create stealth onion services, you need at least Tor 0.2.9.1-alpha (or Tor Browser 6.5) and at least python3-stem 1.5.0.", "error_ephemeral_not_supported": "OnionShare requires at least Tor 0.2.7.1 and at least python3-stem 1.4.0.", "gui_settings_window_title": "Settings", - "gui_settings_stealth_label": "Stealth (advanced)", - "gui_settings_stealth_option": "Create stealth onion services", - "gui_settings_stealth_option_details": "This makes OnionShare more secure, but also more difficult for the recipient to connect to.

NOTE: currently this only works with v2 onions.
More information.", + "gui_settings_stealth_option": "Create stealth onion services (legacy)", + "gui_settings_stealth_option_details": "(what's this?)", "gui_settings_stealth_hidservauth_string": "You have saved the private key for reuse, so your HidServAuth string is also reused.\nClick below to copy the HidServAuth.", "gui_settings_autoupdate_label": "Check for upgrades", "gui_settings_autoupdate_option": "Notify me when upgrades are available", @@ -139,8 +138,9 @@ "gui_server_started_after_timeout": "The server started after your chosen auto-timeout.\nPlease start a new share.", "gui_server_timeout_expired": "The chosen timeout has already expired.\nPlease update the timeout and then you may start sharing.", "share_via_onionshare": "Share via OnionShare", - "gui_use_legacy_v2_onions_checkbox": "Use legacy (v2) .onion addresses?", - "gui_save_private_key_checkbox": "Use a persistent (v2 only) address\n(unchecking will delete any saved addresses)", + "gui_use_legacy_v2_onions_checkbox": "Use legacy addresses", + "gui_use_legacy_v2_onions_label": "(what's this?)", + "gui_save_private_key_checkbox": "Use a persistent address (legacy)\n(unchecking will delete any saved addresses)", "gui_share_url_description": "Anyone with this link can download your files using the Tor Browser: ", "gui_receive_url_description": "Anyone with this link can upload files to your computer using the Tor Browser: ", "gui_url_label_persistent": "This share will not expire automatically unless a timer is set.

Every share will have the same address (to use one-time addresses, disable persistence in Settings)", From 95f097eae32ba2a46f9d483a6e8b5584f56b4ae5 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Sun, 16 Sep 2018 14:00:41 +1000 Subject: [PATCH 13/21] Move the hyperlink labels into HBox layouts with the checkboxes --- onionshare_gui/settings_dialog.py | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/onionshare_gui/settings_dialog.py b/onionshare_gui/settings_dialog.py index 856ca993..2897b944 100644 --- a/onionshare_gui/settings_dialog.py +++ b/onionshare_gui/settings_dialog.py @@ -62,6 +62,12 @@ class SettingsDialog(QtWidgets.QDialog): use_legacy_v2_onions_label.setWordWrap(True) use_legacy_v2_onions_label.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction) use_legacy_v2_onions_label.setOpenExternalLinks(True) + use_legacy_v2_onions_layout = QtWidgets.QHBoxLayout() + use_legacy_v2_onions_layout.addWidget(self.use_legacy_v2_onions_checkbox) + use_legacy_v2_onions_layout.addWidget(use_legacy_v2_onions_label) + use_legacy_v2_onions_layout.addStretch() + use_legacy_v2_onions_widget = QtWidgets.QWidget() + use_legacy_v2_onions_widget.setLayout(use_legacy_v2_onions_layout) # Whether or not to save the Onion private key for reuse (persistent URL mode) self.save_private_key_checkbox = QtWidgets.QCheckBox() @@ -79,11 +85,18 @@ class SettingsDialog(QtWidgets.QDialog): self.stealth_checkbox.setCheckState(QtCore.Qt.Unchecked) self.stealth_checkbox.setText(strings._("gui_settings_stealth_option", True)) self.stealth_checkbox.clicked.connect(self.stealth_checkbox_clicked_connect) - stealth_details = QtWidgets.QLabel(strings._("gui_settings_stealth_option_details", True)) - stealth_details.setWordWrap(True) - stealth_details.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction) - stealth_details.setOpenExternalLinks(True) - stealth_details.setMinimumSize(stealth_details.sizeHint()) + use_stealth_label = QtWidgets.QLabel(strings._("gui_settings_stealth_option_details", True)) + use_stealth_label.setWordWrap(True) + use_stealth_label.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction) + use_stealth_label.setOpenExternalLinks(True) + use_stealth_label.setMinimumSize(use_stealth_label.sizeHint()) + + use_stealth_layout = QtWidgets.QHBoxLayout() + use_stealth_layout.addWidget(self.stealth_checkbox) + use_stealth_layout.addWidget(use_stealth_label) + use_stealth_layout.addStretch() + use_stealth_widget = QtWidgets.QWidget() + use_stealth_widget.setLayout(use_stealth_layout) hidservauth_details = QtWidgets.QLabel(strings._('gui_settings_stealth_hidservauth_string', True)) hidservauth_details.setWordWrap(True) @@ -96,12 +109,10 @@ class SettingsDialog(QtWidgets.QDialog): # General options layout general_group_layout = QtWidgets.QVBoxLayout() - general_group_layout.addWidget(self.use_legacy_v2_onions_checkbox) - general_group_layout.addWidget(use_legacy_v2_onions_label) + general_group_layout.addWidget(use_legacy_v2_onions_widget) general_group_layout.addWidget(self.save_private_key_checkbox) general_group_layout.addWidget(self.public_mode_checkbox) - general_group_layout.addWidget(self.stealth_checkbox) - general_group_layout.addWidget(stealth_details) + general_group_layout.addWidget(use_stealth_widget) general_group_layout.addWidget(hidservauth_details) general_group_layout.addWidget(self.hidservauth_copy_button) From 026322b4585cddd8c03e903484d07abf4248411b Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Sun, 16 Sep 2018 14:06:55 +1000 Subject: [PATCH 14/21] Fix margins on HBoxLayouts in settings --- onionshare_gui/settings_dialog.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/onionshare_gui/settings_dialog.py b/onionshare_gui/settings_dialog.py index 2897b944..f8201fea 100644 --- a/onionshare_gui/settings_dialog.py +++ b/onionshare_gui/settings_dialog.py @@ -66,6 +66,7 @@ class SettingsDialog(QtWidgets.QDialog): use_legacy_v2_onions_layout.addWidget(self.use_legacy_v2_onions_checkbox) use_legacy_v2_onions_layout.addWidget(use_legacy_v2_onions_label) use_legacy_v2_onions_layout.addStretch() + use_legacy_v2_onions_layout.setContentsMargins(0,0,0,0) use_legacy_v2_onions_widget = QtWidgets.QWidget() use_legacy_v2_onions_widget.setLayout(use_legacy_v2_onions_layout) @@ -95,6 +96,7 @@ class SettingsDialog(QtWidgets.QDialog): use_stealth_layout.addWidget(self.stealth_checkbox) use_stealth_layout.addWidget(use_stealth_label) use_stealth_layout.addStretch() + use_stealth_layout.setContentsMargins(0,0,0,0) use_stealth_widget = QtWidgets.QWidget() use_stealth_widget.setLayout(use_stealth_layout) From 8c3c0eb02bb608e35519d18faf83ffa2f24ea336 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Mon, 17 Sep 2018 08:43:19 +1000 Subject: [PATCH 15/21] Use 'settings' rather than 'options' in the SettingsDialog labels --- share/locale/en.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/share/locale/en.json b/share/locale/en.json index 0239037a..df4d03e6 100644 --- a/share/locale/en.json +++ b/share/locale/en.json @@ -79,8 +79,8 @@ "gui_settings_autoupdate_timestamp": "Last checked: {}", "gui_settings_autoupdate_timestamp_never": "Never", "gui_settings_autoupdate_check_button": "Check For Upgrades", - "gui_settings_general_label": "General options", - "gui_settings_sharing_label": "Sharing options", + "gui_settings_general_label": "General settings", + "gui_settings_sharing_label": "Sharing settings", "gui_settings_close_after_first_download_option": "Stop sharing after first download", "gui_settings_connection_type_label": "How should OnionShare connect to Tor?", "gui_settings_connection_type_bundled_option": "Use the Tor version that is bundled with OnionShare", @@ -91,7 +91,7 @@ "gui_settings_control_port_label": "Control port", "gui_settings_socket_file_label": "Socket file", "gui_settings_socks_label": "SOCKS port", - "gui_settings_authenticate_label": "Tor authentication options", + "gui_settings_authenticate_label": "Tor authentication settings", "gui_settings_authenticate_no_auth_option": "No authentication, or cookie authentication", "gui_settings_authenticate_password_option": "Password", "gui_settings_password_label": "Password", @@ -166,7 +166,7 @@ "receive_mode_received_file": "Received file: {}", "gui_mode_share_button": "Share Files", "gui_mode_receive_button": "Receive Files", - "gui_settings_receiving_label": "Receiving options", + "gui_settings_receiving_label": "Receiving settings", "gui_settings_downloads_label": "Save files to", "gui_settings_downloads_button": "Browse", "gui_settings_receive_allow_receiver_shutdown_checkbox": "Receive mode can be stopped by the sender", From 7c55f0adaea6257fc3758f74390ec297114a4b5e Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Mon, 17 Sep 2018 08:44:56 +1000 Subject: [PATCH 16/21] Reorder the general settings --- onionshare_gui/settings_dialog.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/onionshare_gui/settings_dialog.py b/onionshare_gui/settings_dialog.py index f8201fea..cee119f4 100644 --- a/onionshare_gui/settings_dialog.py +++ b/onionshare_gui/settings_dialog.py @@ -81,6 +81,11 @@ class SettingsDialog(QtWidgets.QDialog): self.public_mode_checkbox.setCheckState(QtCore.Qt.Unchecked) self.public_mode_checkbox.setText(strings._("gui_settings_public_mode_checkbox", True)) + # Whether or not to use a shutdown timer + self.shutdown_timeout_checkbox = QtWidgets.QCheckBox() + self.shutdown_timeout_checkbox.setCheckState(QtCore.Qt.Checked) + self.shutdown_timeout_checkbox.setText(strings._("gui_settings_shutdown_timeout_checkbox", True)) + # Stealth self.stealth_checkbox = QtWidgets.QCheckBox() self.stealth_checkbox.setCheckState(QtCore.Qt.Unchecked) @@ -111,9 +116,10 @@ class SettingsDialog(QtWidgets.QDialog): # General options layout general_group_layout = QtWidgets.QVBoxLayout() + general_group_layout.addWidget(self.public_mode_checkbox) + general_group_layout.addWidget(self.shutdown_timeout_checkbox) general_group_layout.addWidget(use_legacy_v2_onions_widget) general_group_layout.addWidget(self.save_private_key_checkbox) - general_group_layout.addWidget(self.public_mode_checkbox) general_group_layout.addWidget(use_stealth_widget) general_group_layout.addWidget(hidservauth_details) general_group_layout.addWidget(self.hidservauth_copy_button) @@ -128,15 +134,9 @@ class SettingsDialog(QtWidgets.QDialog): self.close_after_first_download_checkbox.setCheckState(QtCore.Qt.Checked) self.close_after_first_download_checkbox.setText(strings._("gui_settings_close_after_first_download_option", True)) - # Whether or not to use a shutdown timer - self.shutdown_timeout_checkbox = QtWidgets.QCheckBox() - self.shutdown_timeout_checkbox.setCheckState(QtCore.Qt.Checked) - self.shutdown_timeout_checkbox.setText(strings._("gui_settings_shutdown_timeout_checkbox", True)) - # Sharing options layout sharing_group_layout = QtWidgets.QVBoxLayout() sharing_group_layout.addWidget(self.close_after_first_download_checkbox) - sharing_group_layout.addWidget(self.shutdown_timeout_checkbox) sharing_group = QtWidgets.QGroupBox(strings._("gui_settings_sharing_label", True)) sharing_group.setLayout(sharing_group_layout) From 6c01d7a2daa360ad768932109bb023f2a76c9ced Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Mon, 17 Sep 2018 09:01:30 +1000 Subject: [PATCH 17/21] Add 'what's this' labels to each General Setting --- onionshare_gui/settings_dialog.py | 60 +++++++++++++++++++++++-------- share/locale/en.json | 5 ++- 2 files changed, 49 insertions(+), 16 deletions(-) diff --git a/onionshare_gui/settings_dialog.py b/onionshare_gui/settings_dialog.py index cee119f4..b33aa94c 100644 --- a/onionshare_gui/settings_dialog.py +++ b/onionshare_gui/settings_dialog.py @@ -54,12 +54,43 @@ class SettingsDialog(QtWidgets.QDialog): # General options + # Use a slug or not ('public mode') + self.public_mode_checkbox = QtWidgets.QCheckBox() + self.public_mode_checkbox.setCheckState(QtCore.Qt.Unchecked) + self.public_mode_checkbox.setText(strings._("gui_settings_public_mode_checkbox", True)) + public_mode_label = QtWidgets.QLabel(strings._("gui_settings_public_mode_details", True)) + public_mode_label.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction) + public_mode_label.setOpenExternalLinks(True) + public_mode_label.setMinimumSize(public_mode_label.sizeHint()) + public_mode_layout = QtWidgets.QHBoxLayout() + public_mode_layout.addWidget(self.public_mode_checkbox) + public_mode_layout.addWidget(public_mode_label) + public_mode_layout.addStretch() + public_mode_layout.setContentsMargins(0,0,0,0) + public_mode_widget = QtWidgets.QWidget() + public_mode_widget.setLayout(public_mode_layout) + + # Whether or not to use a shutdown ('auto-stop') timer + self.shutdown_timeout_checkbox = QtWidgets.QCheckBox() + self.shutdown_timeout_checkbox.setCheckState(QtCore.Qt.Checked) + self.shutdown_timeout_checkbox.setText(strings._("gui_settings_shutdown_timeout_checkbox", True)) + shutdown_timeout_label = QtWidgets.QLabel(strings._("gui_settings_shutdown_timeout_details", True)) + shutdown_timeout_label.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction) + shutdown_timeout_label.setOpenExternalLinks(True) + shutdown_timeout_label.setMinimumSize(public_mode_label.sizeHint()) + shutdown_timeout_layout = QtWidgets.QHBoxLayout() + shutdown_timeout_layout.addWidget(self.shutdown_timeout_checkbox) + shutdown_timeout_layout.addWidget(shutdown_timeout_label) + shutdown_timeout_layout.addStretch() + shutdown_timeout_layout.setContentsMargins(0,0,0,0) + shutdown_timeout_widget = QtWidgets.QWidget() + shutdown_timeout_widget.setLayout(shutdown_timeout_layout) + # Whether or not to use legacy v2 onions self.use_legacy_v2_onions_checkbox = QtWidgets.QCheckBox() self.use_legacy_v2_onions_checkbox.setCheckState(QtCore.Qt.Unchecked) self.use_legacy_v2_onions_checkbox.setText(strings._("gui_use_legacy_v2_onions_checkbox", True)) use_legacy_v2_onions_label = QtWidgets.QLabel(strings._("gui_use_legacy_v2_onions_label", True)) - use_legacy_v2_onions_label.setWordWrap(True) use_legacy_v2_onions_label.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction) use_legacy_v2_onions_label.setOpenExternalLinks(True) use_legacy_v2_onions_layout = QtWidgets.QHBoxLayout() @@ -75,16 +106,16 @@ class SettingsDialog(QtWidgets.QDialog): self.save_private_key_checkbox.setCheckState(QtCore.Qt.Unchecked) self.save_private_key_checkbox.setText(strings._("gui_save_private_key_checkbox", True)) self.save_private_key_checkbox.clicked.connect(self.save_private_key_checkbox_clicked) - - # Use a slug - self.public_mode_checkbox = QtWidgets.QCheckBox() - self.public_mode_checkbox.setCheckState(QtCore.Qt.Unchecked) - self.public_mode_checkbox.setText(strings._("gui_settings_public_mode_checkbox", True)) - - # Whether or not to use a shutdown timer - self.shutdown_timeout_checkbox = QtWidgets.QCheckBox() - self.shutdown_timeout_checkbox.setCheckState(QtCore.Qt.Checked) - self.shutdown_timeout_checkbox.setText(strings._("gui_settings_shutdown_timeout_checkbox", True)) + save_private_key_label = QtWidgets.QLabel(strings._("gui_save_private_key_label", True)) + save_private_key_label.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction) + save_private_key_label.setOpenExternalLinks(True) + save_private_key_layout = QtWidgets.QHBoxLayout() + save_private_key_layout.addWidget(self.save_private_key_checkbox) + save_private_key_layout.addWidget(save_private_key_label) + save_private_key_layout.addStretch() + save_private_key_layout.setContentsMargins(0,0,0,0) + save_private_key_widget = QtWidgets.QWidget() + save_private_key_widget.setLayout(save_private_key_layout) # Stealth self.stealth_checkbox = QtWidgets.QCheckBox() @@ -92,7 +123,6 @@ class SettingsDialog(QtWidgets.QDialog): self.stealth_checkbox.setText(strings._("gui_settings_stealth_option", True)) self.stealth_checkbox.clicked.connect(self.stealth_checkbox_clicked_connect) use_stealth_label = QtWidgets.QLabel(strings._("gui_settings_stealth_option_details", True)) - use_stealth_label.setWordWrap(True) use_stealth_label.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction) use_stealth_label.setOpenExternalLinks(True) use_stealth_label.setMinimumSize(use_stealth_label.sizeHint()) @@ -116,10 +146,10 @@ class SettingsDialog(QtWidgets.QDialog): # General options layout general_group_layout = QtWidgets.QVBoxLayout() - general_group_layout.addWidget(self.public_mode_checkbox) - general_group_layout.addWidget(self.shutdown_timeout_checkbox) + general_group_layout.addWidget(public_mode_widget) + general_group_layout.addWidget(shutdown_timeout_widget) general_group_layout.addWidget(use_legacy_v2_onions_widget) - general_group_layout.addWidget(self.save_private_key_checkbox) + general_group_layout.addWidget(save_private_key_widget) general_group_layout.addWidget(use_stealth_widget) general_group_layout.addWidget(hidservauth_details) general_group_layout.addWidget(self.hidservauth_copy_button) diff --git a/share/locale/en.json b/share/locale/en.json index df4d03e6..9c7501cf 100644 --- a/share/locale/en.json +++ b/share/locale/en.json @@ -109,6 +109,7 @@ "gui_settings_button_cancel": "Cancel", "gui_settings_button_help": "Help", "gui_settings_shutdown_timeout_checkbox": "Use auto-stop timer", + "gui_settings_shutdown_timeout_details": "(what's this?)", "gui_settings_shutdown_timeout": "Stop the share at:", "settings_saved": "Settings saved to {}", "settings_error_unknown": "Can't connect to Tor controller because the settings don't make sense.", @@ -141,6 +142,7 @@ "gui_use_legacy_v2_onions_checkbox": "Use legacy addresses", "gui_use_legacy_v2_onions_label": "(what's this?)", "gui_save_private_key_checkbox": "Use a persistent address (legacy)\n(unchecking will delete any saved addresses)", + "gui_save_private_key_label": "(what's this?)", "gui_share_url_description": "Anyone with this link can download your files using the Tor Browser: ", "gui_receive_url_description": "Anyone with this link can upload files to your computer using the Tor Browser: ", "gui_url_label_persistent": "This share will not expire automatically unless a timer is set.

Every share will have the same address (to use one-time addresses, disable persistence in Settings)", @@ -170,7 +172,8 @@ "gui_settings_downloads_label": "Save files to", "gui_settings_downloads_button": "Browse", "gui_settings_receive_allow_receiver_shutdown_checkbox": "Receive mode can be stopped by the sender", - "gui_settings_public_mode_checkbox": "OnionShare is open to the public\n(don't prevent people from guessing the OnionShare address)", + "gui_settings_public_mode_checkbox": "Public mode", + "gui_settings_public_mode_details": "(what's this?)", "systray_close_server_title": "OnionShare Server Closed", "systray_close_server_message": "A user closed the server", "systray_page_loaded_title": "OnionShare Page Loaded", From ff8b7df5a55a37e3a486cbb1bf48bf75220c7c1b Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Mon, 17 Sep 2018 09:02:35 +1000 Subject: [PATCH 18/21] reduce verbosity of persistent mode label --- share/locale/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/locale/en.json b/share/locale/en.json index 9c7501cf..07a01464 100644 --- a/share/locale/en.json +++ b/share/locale/en.json @@ -141,7 +141,7 @@ "share_via_onionshare": "Share via OnionShare", "gui_use_legacy_v2_onions_checkbox": "Use legacy addresses", "gui_use_legacy_v2_onions_label": "(what's this?)", - "gui_save_private_key_checkbox": "Use a persistent address (legacy)\n(unchecking will delete any saved addresses)", + "gui_save_private_key_checkbox": "Use a persistent address (legacy)", "gui_save_private_key_label": "(what's this?)", "gui_share_url_description": "Anyone with this link can download your files using the Tor Browser: ", "gui_receive_url_description": "Anyone with this link can upload files to your computer using the Tor Browser: ", From 6ed5c94df75101fc9f62a10fe46323369ffa2dbf Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Mon, 17 Sep 2018 09:12:13 +1000 Subject: [PATCH 19/21] Hide the legacy settings if legacy mode is not enabled. Fix unrelated bug regarding displaying the HidServAuth copy button/label --- onionshare_gui/settings_dialog.py | 52 +++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/onionshare_gui/settings_dialog.py b/onionshare_gui/settings_dialog.py index b33aa94c..895cde13 100644 --- a/onionshare_gui/settings_dialog.py +++ b/onionshare_gui/settings_dialog.py @@ -67,8 +67,8 @@ class SettingsDialog(QtWidgets.QDialog): public_mode_layout.addWidget(public_mode_label) public_mode_layout.addStretch() public_mode_layout.setContentsMargins(0,0,0,0) - public_mode_widget = QtWidgets.QWidget() - public_mode_widget.setLayout(public_mode_layout) + self.public_mode_widget = QtWidgets.QWidget() + self.public_mode_widget.setLayout(public_mode_layout) # Whether or not to use a shutdown ('auto-stop') timer self.shutdown_timeout_checkbox = QtWidgets.QCheckBox() @@ -83,13 +83,14 @@ class SettingsDialog(QtWidgets.QDialog): shutdown_timeout_layout.addWidget(shutdown_timeout_label) shutdown_timeout_layout.addStretch() shutdown_timeout_layout.setContentsMargins(0,0,0,0) - shutdown_timeout_widget = QtWidgets.QWidget() - shutdown_timeout_widget.setLayout(shutdown_timeout_layout) + self.shutdown_timeout_widget = QtWidgets.QWidget() + self.shutdown_timeout_widget.setLayout(shutdown_timeout_layout) # Whether or not to use legacy v2 onions self.use_legacy_v2_onions_checkbox = QtWidgets.QCheckBox() self.use_legacy_v2_onions_checkbox.setCheckState(QtCore.Qt.Unchecked) self.use_legacy_v2_onions_checkbox.setText(strings._("gui_use_legacy_v2_onions_checkbox", True)) + self.use_legacy_v2_onions_checkbox.clicked.connect(self.use_legacy_v2_onions_checkbox_clicked) use_legacy_v2_onions_label = QtWidgets.QLabel(strings._("gui_use_legacy_v2_onions_label", True)) use_legacy_v2_onions_label.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction) use_legacy_v2_onions_label.setOpenExternalLinks(True) @@ -98,8 +99,8 @@ class SettingsDialog(QtWidgets.QDialog): use_legacy_v2_onions_layout.addWidget(use_legacy_v2_onions_label) use_legacy_v2_onions_layout.addStretch() use_legacy_v2_onions_layout.setContentsMargins(0,0,0,0) - use_legacy_v2_onions_widget = QtWidgets.QWidget() - use_legacy_v2_onions_widget.setLayout(use_legacy_v2_onions_layout) + self.use_legacy_v2_onions_widget = QtWidgets.QWidget() + self.use_legacy_v2_onions_widget.setLayout(use_legacy_v2_onions_layout) # Whether or not to save the Onion private key for reuse (persistent URL mode) self.save_private_key_checkbox = QtWidgets.QCheckBox() @@ -114,8 +115,8 @@ class SettingsDialog(QtWidgets.QDialog): save_private_key_layout.addWidget(save_private_key_label) save_private_key_layout.addStretch() save_private_key_layout.setContentsMargins(0,0,0,0) - save_private_key_widget = QtWidgets.QWidget() - save_private_key_widget.setLayout(save_private_key_layout) + self.save_private_key_widget = QtWidgets.QWidget() + self.save_private_key_widget.setLayout(save_private_key_layout) # Stealth self.stealth_checkbox = QtWidgets.QCheckBox() @@ -126,14 +127,13 @@ class SettingsDialog(QtWidgets.QDialog): use_stealth_label.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction) use_stealth_label.setOpenExternalLinks(True) use_stealth_label.setMinimumSize(use_stealth_label.sizeHint()) - use_stealth_layout = QtWidgets.QHBoxLayout() use_stealth_layout.addWidget(self.stealth_checkbox) use_stealth_layout.addWidget(use_stealth_label) use_stealth_layout.addStretch() use_stealth_layout.setContentsMargins(0,0,0,0) - use_stealth_widget = QtWidgets.QWidget() - use_stealth_widget.setLayout(use_stealth_layout) + self.use_stealth_widget = QtWidgets.QWidget() + self.use_stealth_widget.setLayout(use_stealth_layout) hidservauth_details = QtWidgets.QLabel(strings._('gui_settings_stealth_hidservauth_string', True)) hidservauth_details.setWordWrap(True) @@ -146,11 +146,11 @@ class SettingsDialog(QtWidgets.QDialog): # General options layout general_group_layout = QtWidgets.QVBoxLayout() - general_group_layout.addWidget(public_mode_widget) - general_group_layout.addWidget(shutdown_timeout_widget) - general_group_layout.addWidget(use_legacy_v2_onions_widget) - general_group_layout.addWidget(save_private_key_widget) - general_group_layout.addWidget(use_stealth_widget) + general_group_layout.addWidget(self.public_mode_widget) + general_group_layout.addWidget(self.shutdown_timeout_widget) + general_group_layout.addWidget(self.use_legacy_v2_onions_widget) + general_group_layout.addWidget(self.save_private_key_widget) + general_group_layout.addWidget(self.use_stealth_widget) general_group_layout.addWidget(hidservauth_details) general_group_layout.addWidget(self.hidservauth_copy_button) @@ -463,6 +463,13 @@ class SettingsDialog(QtWidgets.QDialog): use_legacy_v2_onions = self.old_settings.get('use_legacy_v2_onions') + if use_legacy_v2_onions: + self.save_private_key_widget.show() + self.use_stealth_widget.show() + else: + self.save_private_key_widget.hide() + self.use_stealth_widget.hide() + save_private_key = self.old_settings.get('save_private_key') if save_private_key: self.save_private_key_checkbox.setCheckState(QtCore.Qt.Checked) @@ -495,7 +502,7 @@ class SettingsDialog(QtWidgets.QDialog): self.stealth_checkbox.setCheckState(QtCore.Qt.Checked) # Legacy v2 mode is forced on if Stealth is enabled self.use_legacy_v2_onions_checkbox.setEnabled(False) - if save_private_key: + if save_private_key and self.old_settings.get('hidservauth_string') != "": hidservauth_details.show() self.hidservauth_copy_button.show() else: @@ -665,6 +672,17 @@ class SettingsDialog(QtWidgets.QDialog): clipboard = self.qtapp.clipboard() clipboard.setText(self.old_settings.get('hidservauth_string')) + def use_legacy_v2_onions_checkbox_clicked(self, checked): + """ + Show the legacy settings if the legacy mode is enabled. + """ + if checked: + self.save_private_key_widget.show() + self.use_stealth_widget.show() + else: + self.save_private_key_widget.hide() + self.use_stealth_widget.hide() + def save_private_key_checkbox_clicked(self, checked): """ Prevent the v2 legacy mode being switched off if persistence is enabled From adf4b0298035a3079f78db1b87d31bf356ead59f Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Mon, 17 Sep 2018 09:16:11 +1000 Subject: [PATCH 20/21] Update stdeb.cfg to depend on bionic and Python 3.6 --- stdeb.cfg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdeb.cfg b/stdeb.cfg index 334502c0..e190fe8b 100644 --- a/stdeb.cfg +++ b/stdeb.cfg @@ -2,5 +2,5 @@ Package3: onionshare Depends3: python3-flask, python3-stem, python3-pyqt5, python-nautilus, tor, obfs4proxy Build-Depends: python3-pytest, python3-flask, python3-stem, python3-pyqt5 -Suite: xenial -X-Python3-Version: >= 3.4 +Suite: bionic +X-Python3-Version: >= 3.6 From 359e4703833a66eaa9ca7f83ead8053346e9c1a3 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Mon, 17 Sep 2018 16:11:52 -0700 Subject: [PATCH 21/21] Make what's this links use the same string, and change their style --- onionshare/common.py | 5 +++++ onionshare_gui/settings_dialog.py | 15 ++++++++++----- share/locale/en.json | 6 +----- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/onionshare/common.py b/onionshare/common.py index 61663f23..0ce411e8 100644 --- a/onionshare/common.py +++ b/onionshare/common.py @@ -346,6 +346,11 @@ class Common(object): background-color: #ffffff; color: #000000; padding: 10px; + }""", + + 'settings_whats_this': """ + QLabel { + font-size: 12px; }""" } diff --git a/onionshare_gui/settings_dialog.py b/onionshare_gui/settings_dialog.py index 895cde13..c31d4630 100644 --- a/onionshare_gui/settings_dialog.py +++ b/onionshare_gui/settings_dialog.py @@ -58,7 +58,8 @@ class SettingsDialog(QtWidgets.QDialog): self.public_mode_checkbox = QtWidgets.QCheckBox() self.public_mode_checkbox.setCheckState(QtCore.Qt.Unchecked) self.public_mode_checkbox.setText(strings._("gui_settings_public_mode_checkbox", True)) - public_mode_label = QtWidgets.QLabel(strings._("gui_settings_public_mode_details", True)) + public_mode_label = QtWidgets.QLabel(strings._("gui_settings_whats_this", True).format("https://github.com/micahflee/onionshare/wiki/Public-Mode")) + public_mode_label.setStyleSheet(self.common.css['settings_whats_this']) public_mode_label.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction) public_mode_label.setOpenExternalLinks(True) public_mode_label.setMinimumSize(public_mode_label.sizeHint()) @@ -74,7 +75,8 @@ class SettingsDialog(QtWidgets.QDialog): self.shutdown_timeout_checkbox = QtWidgets.QCheckBox() self.shutdown_timeout_checkbox.setCheckState(QtCore.Qt.Checked) self.shutdown_timeout_checkbox.setText(strings._("gui_settings_shutdown_timeout_checkbox", True)) - shutdown_timeout_label = QtWidgets.QLabel(strings._("gui_settings_shutdown_timeout_details", True)) + shutdown_timeout_label = QtWidgets.QLabel(strings._("gui_settings_whats_this", True).format("https://github.com/micahflee/onionshare/wiki/Using-the-Auto-Stop-Timer")) + shutdown_timeout_label.setStyleSheet(self.common.css['settings_whats_this']) shutdown_timeout_label.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction) shutdown_timeout_label.setOpenExternalLinks(True) shutdown_timeout_label.setMinimumSize(public_mode_label.sizeHint()) @@ -91,7 +93,8 @@ class SettingsDialog(QtWidgets.QDialog): self.use_legacy_v2_onions_checkbox.setCheckState(QtCore.Qt.Unchecked) self.use_legacy_v2_onions_checkbox.setText(strings._("gui_use_legacy_v2_onions_checkbox", True)) self.use_legacy_v2_onions_checkbox.clicked.connect(self.use_legacy_v2_onions_checkbox_clicked) - use_legacy_v2_onions_label = QtWidgets.QLabel(strings._("gui_use_legacy_v2_onions_label", True)) + use_legacy_v2_onions_label = QtWidgets.QLabel(strings._("gui_settings_whats_this", True).format("https://github.com/micahflee/onionshare/wiki/Legacy-Addresses")) + use_legacy_v2_onions_label.setStyleSheet(self.common.css['settings_whats_this']) use_legacy_v2_onions_label.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction) use_legacy_v2_onions_label.setOpenExternalLinks(True) use_legacy_v2_onions_layout = QtWidgets.QHBoxLayout() @@ -107,7 +110,8 @@ class SettingsDialog(QtWidgets.QDialog): self.save_private_key_checkbox.setCheckState(QtCore.Qt.Unchecked) self.save_private_key_checkbox.setText(strings._("gui_save_private_key_checkbox", True)) self.save_private_key_checkbox.clicked.connect(self.save_private_key_checkbox_clicked) - save_private_key_label = QtWidgets.QLabel(strings._("gui_save_private_key_label", True)) + save_private_key_label = QtWidgets.QLabel(strings._("gui_settings_whats_this", True).format("https://github.com/micahflee/onionshare/wiki/Using-a-Persistent-URL")) + save_private_key_label.setStyleSheet(self.common.css['settings_whats_this']) save_private_key_label.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction) save_private_key_label.setOpenExternalLinks(True) save_private_key_layout = QtWidgets.QHBoxLayout() @@ -123,7 +127,8 @@ class SettingsDialog(QtWidgets.QDialog): self.stealth_checkbox.setCheckState(QtCore.Qt.Unchecked) self.stealth_checkbox.setText(strings._("gui_settings_stealth_option", True)) self.stealth_checkbox.clicked.connect(self.stealth_checkbox_clicked_connect) - use_stealth_label = QtWidgets.QLabel(strings._("gui_settings_stealth_option_details", True)) + use_stealth_label = QtWidgets.QLabel(strings._("gui_settings_whats_this", True).format("https://github.com/micahflee/onionshare/wiki/Stealth-Onion-Services")) + use_stealth_label.setStyleSheet(self.common.css['settings_whats_this']) use_stealth_label.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction) use_stealth_label.setOpenExternalLinks(True) use_stealth_label.setMinimumSize(use_stealth_label.sizeHint()) diff --git a/share/locale/en.json b/share/locale/en.json index c70ca2eb..4e7143d3 100644 --- a/share/locale/en.json +++ b/share/locale/en.json @@ -71,8 +71,8 @@ "error_stealth_not_supported": "To create stealth onion services, you need at least Tor 0.2.9.1-alpha (or Tor Browser 6.5) and at least python3-stem 1.5.0.", "error_ephemeral_not_supported": "OnionShare requires at least Tor 0.2.7.1 and at least python3-stem 1.4.0.", "gui_settings_window_title": "Settings", + "gui_settings_whats_this": "what's this?", "gui_settings_stealth_option": "Create stealth onion services (legacy)", - "gui_settings_stealth_option_details": "(what's this?)", "gui_settings_stealth_hidservauth_string": "You have saved the private key for reuse, so your HidServAuth string is also reused.\nClick below to copy the HidServAuth.", "gui_settings_autoupdate_label": "Check for upgrades", "gui_settings_autoupdate_option": "Notify me when upgrades are available", @@ -109,7 +109,6 @@ "gui_settings_button_cancel": "Cancel", "gui_settings_button_help": "Help", "gui_settings_shutdown_timeout_checkbox": "Use auto-stop timer", - "gui_settings_shutdown_timeout_details": "(what's this?)", "gui_settings_shutdown_timeout": "Stop the share at:", "settings_saved": "Settings saved to {}", "settings_error_unknown": "Can't connect to Tor controller because the settings don't make sense.", @@ -140,9 +139,7 @@ "gui_server_timeout_expired": "The chosen timeout has already expired.\nPlease update the timeout and then you may start sharing.", "share_via_onionshare": "Share via OnionShare", "gui_use_legacy_v2_onions_checkbox": "Use legacy addresses", - "gui_use_legacy_v2_onions_label": "(what's this?)", "gui_save_private_key_checkbox": "Use a persistent address (legacy)", - "gui_save_private_key_label": "(what's this?)", "gui_share_url_description": "Anyone with this link can download your files using the Tor Browser: ", "gui_receive_url_description": "Anyone with this link can upload files to your computer using the Tor Browser: ", "gui_url_label_persistent": "This share will not expire automatically unless a timer is set.

Every share will have the same address (to use one-time addresses, disable persistence in Settings)", @@ -175,7 +172,6 @@ "gui_settings_downloads_button": "Browse", "gui_settings_receive_allow_receiver_shutdown_checkbox": "Receive mode can be stopped by the sender", "gui_settings_public_mode_checkbox": "Public mode", - "gui_settings_public_mode_details": "(what's this?)", "systray_close_server_title": "OnionShare Server Closed", "systray_close_server_message": "A user closed the server", "systray_page_loaded_title": "OnionShare Page Loaded",