diff --git a/onionshare/onion.py b/onionshare/onion.py
index 00b7dd51..409c9ad8 100644
--- a/onionshare/onion.py
+++ b/onionshare/onion.py
@@ -368,7 +368,7 @@ class Onion(object):
# Do the versions of stem and tor that I'm using support stealth onion services?
try:
res = self.c.create_ephemeral_hidden_service({1:1}, basic_auth={'onionshare':None}, await_publication=False)
- tmp_service_id = res.content()[0][2].split('=')[1]
+ tmp_service_id = res.service_id
self.c.remove_ephemeral_hidden_service(tmp_service_id)
self.supports_stealth = True
except:
@@ -392,20 +392,27 @@ class Onion(object):
print(strings._('using_ephemeral'))
if self.stealth:
- basic_auth = {'onionshare':None}
+ if self.settings.get('hidservauth_string'):
+ hidservauth_string = self.settings.get('hidservauth_string').split()[2]
+ basic_auth = {'onionshare':hidservauth_string}
+ else:
+ basic_auth = {'onionshare':None}
else:
basic_auth = None
if self.settings.get('private_key'):
key_type = "RSA1024"
key_content = self.settings.get('private_key')
+ common.log('Onion', 'Starting a hidden service with a saved private key')
else:
key_type = "NEW"
key_content = "RSA1024"
+ common.log('Onion', 'Starting a hidden service with a new private key')
+
try:
- if basic_auth != None :
+ 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)
- else :
+ 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)
@@ -414,11 +421,22 @@ class Onion(object):
self.service_id = res.content()[0][2].split('=')[1]
onion_host = self.service_id + '.onion'
- self.private_key = res.private_key
+
+ # A new private key was generated and is in the Control port response.
+ if not self.settings.get('private_key'):
+ self.private_key = res.private_key
if self.stealth:
- auth_cookie = res.content()[2][2].split('=')[1].split(':')[1]
- self.auth_string = 'HidServAuth {} {}'.format(onion_host, auth_cookie)
+ # Similar to the PrivateKey, the Control port only returns the ClientAuth
+ # in the response if it was responsible for creating the basic_auth password
+ # in the first place.
+ # If we sent the basic_auth (due to a saved hidservauth_string in the settings),
+ # there is no response here, so use the saved value from settings.
+ if self.settings.get('hidservauth_string'):
+ self.auth_string = self.settings.get('hidservauth_string')
+ else:
+ auth_cookie = list(res.client_auth.values())[0]
+ self.auth_string = 'HidServAuth {} {}'.format(onion_host, auth_cookie)
return onion_host
diff --git a/onionshare_gui/server_status.py b/onionshare_gui/server_status.py
index c2cb8f47..77964677 100644
--- a/onionshare_gui/server_status.py
+++ b/onionshare_gui/server_status.py
@@ -265,6 +265,9 @@ class ServerStatus(QtWidgets.QVBoxLayout):
"""
self.save_private_key_button.setEnabled(False)
self.settings.set('private_key', self.app.private_key)
+ if self.app.stealth:
+ self.settings.set('hidservauth_string', self.app.auth_string)
self.settings.save()
+ self.settings.load()
self.private_key_saved.emit()
diff --git a/onionshare_gui/settings_dialog.py b/onionshare_gui/settings_dialog.py
index 3421255d..22b24fc5 100644
--- a/onionshare_gui/settings_dialog.py
+++ b/onionshare_gui/settings_dialog.py
@@ -82,10 +82,20 @@ class SettingsDialog(QtWidgets.QDialog):
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.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)
@@ -291,6 +301,9 @@ 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:
+ hidservauth_details.show()
+ self.hidservauth_copy_button.show()
else:
self.stealth_checkbox.setCheckState(QtCore.Qt.Unchecked)
@@ -390,6 +403,15 @@ class SettingsDialog(QtWidgets.QDialog):
else:
self.authenticate_password_extras.hide()
+ def hidservauth_copy_button_clicked(self):
+ """
+ Toggle the 'Copy HidServAuth' button
+ to copy the saved HidServAuth to clipboard.
+ """
+ common.log('SettingsDialog', 'hidservauth_copy_button_clicked', 'HidServAuth was copied to clipboard')
+ clipboard = self.qtapp.clipboard()
+ clipboard.setText(self.old_settings.get('hidservauth_string'))
+
def test_tor_clicked(self):
"""
Test Tor Settings button clicked. With the given settings, see if we can
@@ -546,7 +568,12 @@ class SettingsDialog(QtWidgets.QDialog):
settings.set('private_key', settings.get('private_key'))
else:
settings.set('private_key', '')
+ # Also unset the HidServAuth if we are removing our reusable private key
+ settings.set('hidservauth_string', '')
settings.set('use_stealth', self.stealth_checkbox.isChecked())
+ # Always unset the HidServAuth if Stealth mode is unset
+ if not self.stealth_checkbox.isChecked():
+ settings.set('hidservauth_string', '')
if self.connection_type_bundled_radio.isChecked():
settings.set('connection_type', 'bundled')
diff --git a/share/locale/en.json b/share/locale/en.json
index 88113fba..2ec21580 100644
--- a/share/locale/en.json
+++ b/share/locale/en.json
@@ -68,6 +68,7 @@
"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 it.
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 updates",
"gui_settings_autoupdate_option": "Notify me when updates are available",
"gui_settings_autoupdate_timestamp": "Last checked: {}",