Fix conflict

This commit is contained in:
Miguel Jacq 2018-09-18 10:26:16 +10:00
commit c3755389dd
No known key found for this signature in database
GPG Key ID: EEA4341C6D97A0B6
15 changed files with 433 additions and 83 deletions

View File

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

View File

@ -11,9 +11,11 @@ cd onionshare
Install the needed dependencies:
For Debian-like distros: `apt install -y build-essential fakeroot python3-all python3-stdeb dh-python python3-socks 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 python3-socks python3-sha3`
For Fedora-like distros: `dnf install -y rpm-build python3-flask python3-stem python3-qt5 python3-pytest nautilus-python tor obfs4 rpm-build`
On some older versions of Debian you may need to install pysha3 with `pip3 install pysha3` if python3-sha3 is not available.
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 python3-pysocks`
After that you can try both the CLI and the GUI version of OnionShare:

View File

@ -1,14 +1,26 @@
altgraph==0.16.1
certifi==2018.8.24
chardet==3.0.4
click==6.7
Flask==0.12.2
Flask==1.0.2
future==0.16.0
idna==2.7
itsdangerous==0.24
Jinja2==2.10
macholib==1.11
MarkupSafe==1.0
pefile==2017.11.5
PyInstaller==3.3.1
PyQt5==5.9.2
PySocks==1.6.7
packaging==17.1
pefile==2018.8.8
PyInstaller==3.4
pyparsing==2.2.0
pypiwin32==223
PyQt5==5.11.2
PySocks==1.6.8
python-dateutil==2.7.3
pywin32==223
requests==2.19.1
sip==4.19.6
sip==4.19.8
six==1.11.0
stem==1.6.0
urllib3==1.23
Werkzeug==0.14.1

View File

@ -1,12 +1,21 @@
altgraph==0.16.1
certifi==2018.8.24
chardet==3.0.4
click==6.7
Flask==0.12.2
Flask==1.0.2
future==0.16.0
idna==2.7
itsdangerous==0.24
Jinja2==2.10
macholib==1.11
MarkupSafe==1.0
PyInstaller==3.3.1
PyQt5==5.9.2
PySocks==1.6.7
pefile==2018.8.8
PyInstaller==3.4
PyQt5==5.11.2
PyQt5-sip==4.19.12
PySocks==1.6.8
requests==2.19.1
sip==4.19.6
sip==4.19.8
stem==1.6.0
urllib3==1.23
Werkzeug==0.14.1

View File

@ -346,6 +346,11 @@ class Common(object):
background-color: #ffffff;
color: #000000;
padding: 10px;
}""",
'settings_whats_this': """
QLabel {
font-size: 12px;
}"""
}

View File

@ -21,8 +21,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
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 common, strings
from .settings import Settings
@ -437,20 +439,48 @@ 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')
# is the key a v2 key?
if onionkey.is_v2_key(key_content):
key_type = "RSA1024"
# 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
#
# 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:
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.2.9') and not self.settings.get('use_legacy_v2_onions'):
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" and not self.settings.get('use_legacy_v2_onions'):
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'))
@ -461,7 +491,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

129
onionshare/onionkey.py Normal file
View File

@ -0,0 +1,129 @@
# -*- coding: utf-8 -*-
"""
OnionShare | https://onionshare.org/
Copyright (C) 2017 Micah Lee <micah@micahflee.com>
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 <http://www.gnu.org/licenses/>.
"""
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
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: 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 onion_url_from_private_key(private_key_seed: bytes) -> str:
"""
Derives the public key (.onion hostname) from a v3-style
Onion private 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" + public_key + version).digest()[:2]
onion_address = "http://{}.onion".format(base64.b32encode(public_key + 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)
"""
private_key_seed = os.urandom(32)
private_key = stem_compatible_base64_blob_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():
"""
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

View File

@ -67,6 +67,7 @@ class Settings(object):
'tor_bridges_use_obfs4': False,
'tor_bridges_use_meek_lite_azure': False,
'tor_bridges_use_custom_bridges': '',
'use_legacy_v2_onions': False,
'save_private_key': False,
'private_key': '',
'public_mode': False,

View File

@ -33,6 +33,7 @@ from distutils.version import LooseVersion as Version
from urllib.request import urlopen
from datetime import datetime
import flask
from flask import (
Flask, Response, Request, request, render_template, abort, make_response,
flash, redirect, __version__ as flask_version
@ -42,6 +43,15 @@ from werkzeug.utils import secure_filename
from . import strings
from .common import DownloadsDirErrorCannotCreate, DownloadsDirErrorNotWritable
# Stub out flask's show_server_banner function, to avoiding showing warnings that
# are not applicable to OnionShare
def stubbed_show_server_banner(env, debug, app_import_path, eager_loading):
pass
flask.cli.show_server_banner = stubbed_show_server_banner
class Web(object):
"""
The Web object is the OnionShare web server, powered by flask

View File

@ -205,7 +205,7 @@ class ReceiveMode(Mode):
else:
image = self.common.get_resource_path('images/share_completed.png')
self.info_completed_uploads_count.setText('<img src="{0:s}" /> {1:d}'.format(image, self.uploads_completed))
self.info_completed_uploads_count.setToolTip(strings._('info_completed_downloads_tooltip', True).format(self.uploads_completed))
self.info_completed_uploads_count.setToolTip(strings._('info_completed_uploads_tooltip', True).format(self.uploads_completed))
def update_uploads_in_progress(self):
"""
@ -217,7 +217,7 @@ class ReceiveMode(Mode):
image = self.common.get_resource_path('images/share_in_progress.png')
self.info_show_uploads.setIcon(QtGui.QIcon(self.common.get_resource_path('images/upload_window_green.png')))
self.info_in_progress_uploads_count.setText('<img src="{0:s}" /> {1:d}'.format(image, self.uploads_in_progress))
self.info_in_progress_uploads_count.setToolTip(strings._('info_in_progress_downloads_tooltip', True).format(self.uploads_in_progress))
self.info_in_progress_uploads_count.setToolTip(strings._('info_in_progress_uploads_tooltip', True).format(self.uploads_in_progress))
def update_primary_action(self):
self.common.log('ReceiveMode', 'update_primary_action')

View File

@ -18,6 +18,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
import platform
import textwrap
from PyQt5 import QtCore, QtWidgets, QtGui
from onionshare import strings
@ -56,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()
@ -88,7 +91,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'])
@ -128,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

View File

@ -54,20 +54,111 @@ class SettingsDialog(QtWidgets.QDialog):
# General options
# Whether or not to save the Onion private key for reuse (persistent URL mode)
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))
# Use a slug
# 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_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())
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)
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()
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_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())
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)
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_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()
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)
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()
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_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()
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)
self.save_private_key_widget = QtWidgets.QWidget()
self.save_private_key_widget.setLayout(save_private_key_layout)
# 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)
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())
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)
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)
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.save_private_key_checkbox)
general_group_layout.addWidget(self.public_mode_checkbox)
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)
general_group = QtWidgets.QGroupBox(strings._("gui_settings_general_label", True))
general_group.setLayout(general_group_layout)
@ -78,15 +169,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)
@ -107,36 +192,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))
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
@ -369,7 +424,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()
@ -406,11 +460,26 @@ 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')
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)
# 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)
if use_legacy_v2_onions or save_private_key:
self.use_legacy_v2_onions_checkbox.setCheckState(QtCore.Qt.Checked)
downloads_dir = self.old_settings.get('downloads_dir')
self.downloads_dir_lineedit.setText(downloads_dir)
@ -424,11 +493,15 @@ class SettingsDialog(QtWidgets.QDialog):
use_stealth = self.old_settings.get('use_stealth')
if use_stealth:
self.stealth_checkbox.setCheckState(QtCore.Qt.Checked)
if save_private_key:
# Legacy v2 mode is forced on if Stealth is enabled
self.use_legacy_v2_onions_checkbox.setEnabled(False)
if save_private_key and self.old_settings.get('hidservauth_string') != "":
hidservauth_details.show()
self.hidservauth_copy_button.show()
else:
self.stealth_checkbox.setCheckState(QtCore.Qt.Unchecked)
if not save_private_key:
self.use_legacy_v2_onions_checkbox.setEnabled(True)
use_autoupdate = self.old_settings.get('use_autoupdate')
if use_autoupdate:
@ -592,6 +665,39 @@ 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
"""
if checked:
self.use_legacy_v2_onions_checkbox.setCheckState(QtCore.Qt.Checked)
self.use_legacy_v2_onions_checkbox.setEnabled(False)
else:
if not self.stealth_checkbox.isChecked():
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:
if not self.save_private_key_checkbox.isChecked():
self.use_legacy_v2_onions_checkbox.setEnabled(True)
def downloads_button_clicked(self):
"""
Browse for a new downloads directory
@ -778,7 +884,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'))
@ -789,6 +904,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('public_mode', self.public_mode_checkbox.isChecked())
settings.set('use_stealth', self.stealth_checkbox.isChecked())

View File

@ -72,17 +72,16 @@
"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.<br><a href=\"https://github.com/micahflee/onionshare/wiki/Stealth-Onion-Services\">More information</a>.",
"gui_settings_whats_this": "<a href='{0:s}'>what's this?</a>",
"gui_settings_stealth_option": "Create stealth onion services (legacy)",
"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",
"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",
@ -93,7 +92,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",
@ -125,6 +124,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. <a href='{}'>Click here</a> to download it.<br><br>Installed version: {}<br>Latest version: {}",
"update_error_check_error": "Error checking for updates: Maybe you're not connected to Tor, or maybe the OnionShare website is down.",
@ -139,7 +139,8 @@
"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_save_private_key_checkbox": "Use a persistent address\n(unchecking will delete any saved addresses)",
"gui_use_legacy_v2_onions_checkbox": "Use legacy addresses",
"gui_save_private_key_checkbox": "Use a persistent address (legacy)",
"gui_share_url_description": "<b>Anyone</b> with this link can <b>download</b> your files using the <b>Tor Browser</b>: <img src='{}' />",
"gui_receive_url_description": "<b>Anyone</b> with this link can <b>upload</b> files to your computer using the <b>Tor Browser</b>: <img src='{}' />",
"gui_url_label_persistent": "This share will not expire automatically unless a timer is set.<br><br>Every share will have the same address (to use one-time addresses, disable persistence in Settings)",
@ -156,6 +157,8 @@
"gui_file_info_single": "{} File, {}",
"info_in_progress_downloads_tooltip": "{} download(s) in progress",
"info_completed_downloads_tooltip": "{} download(s) completed",
"info_in_progress_uploads_tooltip": "{} upload(s) in progress",
"info_completed_uploads_tooltip": "{} upload(s) completed",
"error_cannot_create_downloads_dir": "Error creating downloads folder: {}",
"error_downloads_dir_not_writable": "The downloads folder isn't writable: {}",
"receive_mode_downloads_dir": "Files people send you will appear in this folder: {}",
@ -165,10 +168,10 @@
"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_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",
"systray_close_server_title": "OnionShare Server Closed",
"systray_close_server_message": "A user closed the server",
"systray_page_loaded_title": "OnionShare Page Loaded",

View File

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

View File

@ -59,6 +59,7 @@ class TestSettings:
'tor_bridges_use_obfs4': 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': '',