Browse Source

Trezor: Add wipe device functionality

Also add a chicken box for PIN removal.
283
Neil Booth 9 years ago
parent
commit
a7028176cd
  1. 2
      gui/qt/installwizard.py
  2. 4
      gui/qt/qrcodewidget.py
  3. 7
      gui/qt/util.py
  4. 3
      plugins/trezor/client.py
  5. 25
      plugins/trezor/plugin.py
  6. 147
      plugins/trezor/qt_generic.py

2
gui/qt/installwizard.py

@ -55,7 +55,7 @@ class CosignWidget(QWidget):
class InstallWizard(WindowModalDialog, MessageBoxMixin, WizardBase): class InstallWizard(WindowModalDialog, WizardBase):
def __init__(self, config, app, plugins): def __init__(self, config, app, plugins):
title = 'Electrum - ' + _('Install Wizard') title = 'Electrum - ' + _('Install Wizard')

4
gui/qt/qrcodewidget.py

@ -8,7 +8,7 @@ import qrcode
import electrum import electrum
from electrum import bmp from electrum import bmp
from electrum.i18n import _ from electrum.i18n import _
from util import WindowModalDialog, MessageBoxMixin from util import WindowModalDialog
class QRCodeWidget(QWidget): class QRCodeWidget(QWidget):
@ -83,7 +83,7 @@ class QRCodeWidget(QWidget):
class QRDialog(WindowModalDialog, MessageBoxMixin): class QRDialog(WindowModalDialog):
def __init__(self, data, parent=None, title = "", show_text=False): def __init__(self, data, parent=None, title = "", show_text=False):
WindowModalDialog.__init__(self, parent, title) WindowModalDialog.__init__(self, parent, title)

7
gui/qt/util.py

@ -156,9 +156,10 @@ class CancelButton(QPushButton):
self.clicked.connect(dialog.reject) self.clicked.connect(dialog.reject)
class MessageBoxMixin: class MessageBoxMixin:
def question(self, msg, parent=None, title=None): def question(self, msg, parent=None, title=None, icon=None):
Yes, No = QMessageBox.Yes, QMessageBox.No Yes, No = QMessageBox.Yes, QMessageBox.No
return self.msg_box(QMessageBox.Question, parent or self, title or '', return self.msg_box(icon or QMessageBox.Question,
parent or self, title or '',
msg, buttons=Yes|No, defaultButton=No) == Yes msg, buttons=Yes|No, defaultButton=No) == Yes
def show_warning(self, msg, parent=None, title=None): def show_warning(self, msg, parent=None, title=None):
@ -188,7 +189,7 @@ class MessageBoxMixin:
d.setDefaultButton(defaultButton) d.setDefaultButton(defaultButton)
return d.exec_() return d.exec_()
class WindowModalDialog(QDialog): class WindowModalDialog(QDialog, MessageBoxMixin):
'''Handy wrapper; window modal dialogs are better for our multi-window '''Handy wrapper; window modal dialogs are better for our multi-window
daemon model as other wallet windows can still be accessed.''' daemon model as other wallet windows can still be accessed.'''
def __init__(self, parent, title=None): def __init__(self, parent, title=None):

3
plugins/trezor/client.py

@ -154,7 +154,8 @@ def trezor_client_class(protocol_mixin, base_client, proto):
cls = TrezorClient cls = TrezorClient
for method in ['apply_settings', 'change_pin', 'get_address', for method in ['apply_settings', 'change_pin', 'get_address',
'get_public_node', 'sign_message', 'sign_tx']: 'get_public_node', 'sign_message', 'sign_tx',
'wipe_device']:
setattr(cls, method, wrapper(getattr(cls, method))) setattr(cls, method, wrapper(getattr(cls, method)))
return cls return cls

25
plugins/trezor/plugin.py

@ -42,6 +42,14 @@ class TrezorCompatibleWallet(BIP44_Wallet):
self.print_error("connected") self.print_error("connected")
self.handler.watching_only_changed() self.handler.watching_only_changed()
def wiped(self):
self.print_error("wiped")
self.handler.watching_only_changed()
def initialized(self):
self.print_error("initialized")
self.handler.watching_only_changed()
def get_action(self): def get_action(self):
pass pass
@ -316,14 +324,15 @@ class TrezorCompatiblePlugin(BasePlugin):
@hook @hook
def close_wallet(self, wallet): def close_wallet(self, wallet):
# Don't retain references to a closed wallet if isinstance(wallet, self.wallet_class):
self.paired_wallets.discard(wallet) # Don't retain references to a closed wallet
client = self.lookup_client(wallet) self.paired_wallets.discard(wallet)
if client: client = self.lookup_client(wallet)
self.clear_session(client) if client:
# Release the device self.clear_session(client)
self.clients.discard(client) # Release the device
client.transport.close() self.clients.discard(client)
client.transport.close()
def sign_transaction(self, wallet, tx, prev_tx, xpub_path): def sign_transaction(self, wallet, tx, prev_tx, xpub_path):
self.prev_tx = prev_tx self.prev_tx = prev_tx

147
plugins/trezor/qt_generic.py

@ -145,8 +145,36 @@ def qt_plugin_class(base_plugin_class):
lambda: self.show_address(wallet, addrs[0])) lambda: self.show_address(wallet, addrs[0]))
def settings_dialog(self, window): def settings_dialog(self, window):
handler = window.wallet.handler
client = self.client(window.wallet) def client():
return self.client(wallet)
def add_rows_to_layout(layout, rows):
for row_num, items in enumerate(rows):
for col_num, txt in enumerate(items):
widget = txt if isinstance(txt, QWidget) else QLabel(txt)
layout.addWidget(widget, row_num, col_num)
def refresh():
features = client().features
bl_hash = features.bootloader_hash.encode('hex').upper()
bl_hash = "%s...%s" % (bl_hash[:10], bl_hash[-10:])
version = "%d.%d.%d" % (features.major_version,
features.minor_version,
features.patch_version)
bl_hash_label.setText(bl_hash)
device_label.setText(features.label)
device_id_label.setText(features.device_id)
initialized_label.setText(noyes[features.initialized])
version_label.setText(version)
pin_label.setText(noyes[features.pin_protection])
passphrase_label.setText(noyes[features.passphrase_protection])
language_label.setText(features.language)
pin_button.setText(_("Change") if features.pin_protection
else _("Set"))
clear_pin_button.setVisible(features.pin_protection)
def rename(): def rename():
title = _("Set Device Label") title = _("Set Device Label")
@ -154,64 +182,91 @@ def qt_plugin_class(base_plugin_class):
response = QInputDialog().getText(dialog, title, msg) response = QInputDialog().getText(dialog, title, msg)
if not response[1]: if not response[1]:
return return
new_label = str(response[0]) client().change_label(str(response[0]))
client.change_label(new_label) refresh()
device_label.setText(new_label)
def set_pin():
def update_pin_info(): client().set_pin(remove=False)
features = client.features refresh()
pin_label.setText(noyes[features.pin_protection])
pin_button.setText(_("Change") if features.pin_protection def clear_pin():
else _("Set")) title = _("Confirm Clear PIN")
clear_pin_button.setVisible(features.pin_protection) msg = _("WARNING: if your clear your PIN, anyone with physical "
"access to your %s device can spend your bitcoins.\n\n"
def set_pin(remove): "Are you certain you want to remove your PIN?") % device
client.set_pin(remove=remove) if not dialog.question(msg, title=title):
update_pin_info() return
client().set_pin(remove=True)
refresh()
def wipe_device():
title = _("Confirm Device Wipe")
msg = _("Are you sure you want to wipe the device? "
"You should make sure you have a copy of your recovery "
"seed and that your wallet holds no bitcoins.")
if not dialog.question(msg, title=title):
return
if sum(wallet.get_balance()):
title = _("Confirm Device Wipe")
msg = _("Are you SURE you want to wipe the device?\n"
"Your wallet still has bitcoins in it!")
if not dialog.question(msg, title=title,
icon=QMessageBox.Critical):
return
client().wipe_device()
refresh()
wallet = window.wallet
handler = wallet.handler
device = self.device
features = client.features
noyes = [_("No"), _("Yes")]
bl_hash = features.bootloader_hash.encode('hex').upper()
bl_hash = "%s...%s" % (bl_hash[:10], bl_hash[-10:])
info_tab = QWidget() info_tab = QWidget()
layout = QGridLayout(info_tab) info_layout = QGridLayout(info_tab)
device_label = QLabel(features.label) noyes = [_("No"), _("Yes")]
bl_hash_label = QLabel()
device_label = QLabel()
passphrase_label = QLabel()
initialized_label = QLabel()
device_id_label = QLabel()
version_label = QLabel()
pin_label = QLabel()
language_label = QLabel()
rename_button = QPushButton(_("Rename")) rename_button = QPushButton(_("Rename"))
rename_button.clicked.connect(rename) rename_button.clicked.connect(rename)
pin_label = QLabel()
pin_button = QPushButton() pin_button = QPushButton()
pin_button.clicked.connect(partial(set_pin, False)) pin_button.clicked.connect(set_pin)
clear_pin_button = QPushButton(_("Clear")) clear_pin_button = QPushButton(_("Clear"))
clear_pin_button.clicked.connect(partial(set_pin, True)) clear_pin_button.clicked.connect(clear_pin)
update_pin_info()
add_rows_to_layout(info_layout, [
version = "%d.%d.%d" % (features.major_version,
features.minor_version,
features.patch_version)
rows = [
(_("Bootloader Hash"), bl_hash),
(_("Device ID"), features.device_id),
(_("Device Label"), device_label, rename_button), (_("Device Label"), device_label, rename_button),
(_("Firmware Version"), version), (_("Has Passphrase"), passphrase_label),
(_("Language"), features.language), (_("Has PIN"), pin_label, pin_button, clear_pin_button),
(_("Has Passphrase"), noyes[features.passphrase_protection]), (_("Initialized"), initialized_label),
(_("Has PIN"), pin_label, pin_button, clear_pin_button) (_("Device ID"), device_id_label),
] (_("Bootloader Hash"), bl_hash_label),
(_("Firmware Version"), version_label),
for row_num, items in enumerate(rows): (_("Language"), language_label),
for col_num, item in enumerate(items): ])
widget = item if isinstance(item, QWidget) else QLabel(item)
layout.addWidget(widget, row_num, col_num) advanced_tab = QWidget()
advanced_layout = QGridLayout(advanced_tab)
dialog = WindowModalDialog(window, _("%s Settings") % self.device) wipe_device_button = QPushButton(_("Wipe Device"))
wipe_device_button.clicked.connect(wipe_device)
add_rows_to_layout(advanced_layout, [
(wipe_device_button, ),
])
dialog = WindowModalDialog(window, _("%s Settings") % device)
vbox = QVBoxLayout() vbox = QVBoxLayout()
tabs = QTabWidget() tabs = QTabWidget()
tabs.addTab(info_tab, _("Information")) tabs.addTab(info_tab, _("Information"))
tabs.addTab(QWidget(), _("Advanced")) tabs.addTab(advanced_tab, _("Advanced"))
vbox.addWidget(tabs) vbox.addWidget(tabs)
vbox.addStretch(1) vbox.addStretch(1)
vbox.addLayout(Buttons(CloseButton(dialog))) vbox.addLayout(Buttons(CloseButton(dialog)))
refresh()
dialog.setLayout(vbox) dialog.setLayout(vbox)
handler.exec_dialog(dialog) handler.exec_dialog(dialog)

Loading…
Cancel
Save