diff --git a/onionshare/onion.py b/onionshare/onion.py index 2f79719b..b95d975d 100644 --- a/onionshare/onion.py +++ b/onionshare/onion.py @@ -396,24 +396,31 @@ class Onion(object): else: basic_auth = None + if self.settings.get('private_key'): + key_type = "RSA1024" + key_content = self.settings.get('private_key') + else: + key_type = "NEW" + key_content = "RSA1024" try: if basic_auth != None : - res = self.c.create_ephemeral_hidden_service({ 80: port }, await_publication=True, basic_auth=basic_auth) + 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) + 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')) self.service_id = res.content()[0][2].split('=')[1] onion_host = self.service_id + '.onion' + 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) - return onion_host + return (onion_host, private_key) def cleanup(self): """ diff --git a/onionshare/onionshare.py b/onionshare/onionshare.py index 85bfaf22..128e71af 100644 --- a/onionshare/onionshare.py +++ b/onionshare/onionshare.py @@ -76,7 +76,7 @@ class OnionShare(object): if self.shutdown_timeout > 0: self.shutdown_timer = common.close_after_seconds(self.shutdown_timeout) - self.onion_host = self.onion.start_onion_service(self.port) + self.onion_host, self.private_key = self.onion.start_onion_service(self.port) if self.stealth: self.auth_string = self.onion.auth_string diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py index 3ed30db7..dd5c00bf 100644 --- a/onionshare_gui/onionshare_gui.py +++ b/onionshare_gui/onionshare_gui.py @@ -69,7 +69,7 @@ class OnionShareGui(QtWidgets.QMainWindow): self.file_selection.file_list.add_file(filename) # Server status - self.server_status = ServerStatus(self.qtapp, self.app, web, self.file_selection) + self.server_status = ServerStatus(self.qtapp, self.app, web, self.file_selection, self.settings) self.server_status.server_started.connect(self.file_selection.server_started) self.server_status.server_started.connect(self.start_server) self.server_status.server_stopped.connect(self.file_selection.server_stopped) diff --git a/onionshare_gui/server_status.py b/onionshare_gui/server_status.py index 48bffdca..c2cb8f47 100644 --- a/onionshare_gui/server_status.py +++ b/onionshare_gui/server_status.py @@ -22,6 +22,7 @@ from .alert import Alert from PyQt5 import QtCore, QtWidgets, QtGui from onionshare import strings, common +from onionshare.settings import Settings class ServerStatus(QtWidgets.QVBoxLayout): """ @@ -31,12 +32,13 @@ class ServerStatus(QtWidgets.QVBoxLayout): server_stopped = QtCore.pyqtSignal() url_copied = QtCore.pyqtSignal() hidservauth_copied = QtCore.pyqtSignal() + private_key_saved = QtCore.pyqtSignal() STATUS_STOPPED = 0 STATUS_WORKING = 1 STATUS_STARTED = 2 - def __init__(self, qtapp, app, web, file_selection): + def __init__(self, qtapp, app, web, file_selection, settings): super(ServerStatus, self).__init__() self.status = self.STATUS_STOPPED @@ -44,6 +46,7 @@ class ServerStatus(QtWidgets.QVBoxLayout): self.app = app self.web = web self.file_selection = file_selection + self.settings = settings # Helper boolean as this is used in a few places self.timer_enabled = False @@ -87,10 +90,13 @@ class ServerStatus(QtWidgets.QVBoxLayout): self.copy_url_button.clicked.connect(self.copy_url) self.copy_hidservauth_button = QtWidgets.QPushButton(strings._('gui_copy_hidservauth', True)) self.copy_hidservauth_button.clicked.connect(self.copy_hidservauth) + self.save_private_key_button = QtWidgets.QPushButton(strings._('gui_save_private_key', True)) + self.save_private_key_button.clicked.connect(self.save_private_key) url_layout = QtWidgets.QHBoxLayout() url_layout.addWidget(self.url_label) url_layout.addWidget(self.copy_url_button) url_layout.addWidget(self.copy_hidservauth_button) + url_layout.addWidget(self.save_private_key_button) # add the widgets self.addLayout(shutdown_timeout_layout_group) @@ -140,6 +146,8 @@ class ServerStatus(QtWidgets.QVBoxLayout): self.url_label.setText('http://{0:s}/{1:s}'.format(self.app.onion_host, self.web.slug)) self.url_label.show() self.copy_url_button.show() + if not self.settings.get('private_key'): + self.save_private_key_button.show() if self.app.stealth: self.copy_hidservauth_button.show() @@ -153,6 +161,7 @@ class ServerStatus(QtWidgets.QVBoxLayout): self.url_label.hide() self.copy_url_button.hide() self.copy_hidservauth_button.hide() + self.save_private_key_button.hide() # button if self.file_selection.get_num_files() == 0: @@ -249,3 +258,13 @@ class ServerStatus(QtWidgets.QVBoxLayout): clipboard.setText(self.app.auth_string) self.hidservauth_copied.emit() + + def save_private_key(self): + """ + Save the Onion private key to settings, so the Onion URL can be re-used. + """ + self.save_private_key_button.setEnabled(False) + self.settings.set('private_key', self.app.private_key) + self.settings.save() + + self.private_key_saved.emit() diff --git a/onionshare_gui/settings_dialog.py b/onionshare_gui/settings_dialog.py index df806a06..3421255d 100644 --- a/onionshare_gui/settings_dialog.py +++ b/onionshare_gui/settings_dialog.py @@ -60,10 +60,16 @@ class SettingsDialog(QtWidgets.QDialog): self.systray_notifications_checkbox.setCheckState(QtCore.Qt.Checked) self.systray_notifications_checkbox.setText(strings._("gui_settings_systray_notifications", True)) + # Whether or not to save the Onion private key for reuse + 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)) + # Sharing options layout sharing_group_layout = QtWidgets.QVBoxLayout() sharing_group_layout.addWidget(self.close_after_first_download_checkbox) sharing_group_layout.addWidget(self.systray_notifications_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) @@ -275,6 +281,13 @@ class SettingsDialog(QtWidgets.QDialog): else: self.systray_notifications_checkbox.setCheckState(QtCore.Qt.Unchecked) + save_private_key = self.old_settings.get('private_key') + if save_private_key: + self.save_private_key_checkbox.setCheckState(QtCore.Qt.Checked) + else: + self.save_private_key_checkbox.setCheckState(QtCore.Qt.Unchecked) + self.save_private_key_checkbox.hide() + use_stealth = self.old_settings.get('use_stealth') if use_stealth: self.stealth_checkbox.setCheckState(QtCore.Qt.Checked) @@ -529,6 +542,10 @@ class SettingsDialog(QtWidgets.QDialog): settings.set('close_after_first_download', self.close_after_first_download_checkbox.isChecked()) settings.set('systray_notifications', self.systray_notifications_checkbox.isChecked()) + if self.save_private_key_checkbox.isChecked(): + settings.set('private_key', settings.get('private_key')) + else: + settings.set('private_key', '') settings.set('use_stealth', self.stealth_checkbox.isChecked()) if self.connection_type_bundled_radio.isChecked(): diff --git a/share/locale/en.json b/share/locale/en.json index 0756843e..5ec68229 100644 --- a/share/locale/en.json +++ b/share/locale/en.json @@ -121,5 +121,7 @@ "gui_tor_connection_canceled": "OnionShare cannot connect to Tor.\n\nMake sure you're connected to the internet, then re-open OnionShare to configure the Tor connection.", "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" + "share_via_onionshare": "Share via OnionShare", + "gui_save_private_key": "Save private key?", + "gui_save_private_key_checkbox": "Should the private key be saved for re-use?\nThis makes the Onion share URL persistent.\nUnchecking will remove the private key from settings." }