Trezor: Add wipe device functionality

Also add a chicken box for PIN removal.
This commit is contained in:
Neil Booth 2015-12-28 00:12:26 +09:00
parent 9aae66a9d2
commit a7028176cd
6 changed files with 122 additions and 56 deletions

View File

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

View File

@ -8,7 +8,7 @@ import qrcode
import electrum
from electrum import bmp
from electrum.i18n import _
from util import WindowModalDialog, MessageBoxMixin
from util import WindowModalDialog
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):
WindowModalDialog.__init__(self, parent, title)

View File

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

View File

@ -154,7 +154,8 @@ def trezor_client_class(protocol_mixin, base_client, proto):
cls = TrezorClient
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)))
return cls

View File

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

View File

@ -145,8 +145,36 @@ def qt_plugin_class(base_plugin_class):
lambda: self.show_address(wallet, addrs[0]))
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():
title = _("Set Device Label")
@ -154,64 +182,91 @@ def qt_plugin_class(base_plugin_class):
response = QInputDialog().getText(dialog, title, msg)
if not response[1]:
return
new_label = str(response[0])
client.change_label(new_label)
device_label.setText(new_label)
client().change_label(str(response[0]))
refresh()
def update_pin_info():
features = client.features
pin_label.setText(noyes[features.pin_protection])
pin_button.setText(_("Change") if features.pin_protection
else _("Set"))
clear_pin_button.setVisible(features.pin_protection)
def set_pin():
client().set_pin(remove=False)
refresh()
def set_pin(remove):
client.set_pin(remove=remove)
update_pin_info()
def clear_pin():
title = _("Confirm Clear PIN")
msg = _("WARNING: if your clear your PIN, anyone with physical "
"access to your %s device can spend your bitcoins.\n\n"
"Are you certain you want to remove your PIN?") % device
if not dialog.question(msg, title=title):
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()
layout = QGridLayout(info_tab)
device_label = QLabel(features.label)
info_layout = QGridLayout(info_tab)
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.clicked.connect(rename)
pin_label = QLabel()
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.clicked.connect(partial(set_pin, True))
update_pin_info()
clear_pin_button.clicked.connect(clear_pin)
version = "%d.%d.%d" % (features.major_version,
features.minor_version,
features.patch_version)
rows = [
(_("Bootloader Hash"), bl_hash),
(_("Device ID"), features.device_id),
add_rows_to_layout(info_layout, [
(_("Device Label"), device_label, rename_button),
(_("Firmware Version"), version),
(_("Language"), features.language),
(_("Has Passphrase"), noyes[features.passphrase_protection]),
(_("Has PIN"), pin_label, pin_button, clear_pin_button)
]
(_("Has Passphrase"), passphrase_label),
(_("Has PIN"), pin_label, pin_button, clear_pin_button),
(_("Initialized"), initialized_label),
(_("Device ID"), device_id_label),
(_("Bootloader Hash"), bl_hash_label),
(_("Firmware Version"), version_label),
(_("Language"), language_label),
])
for row_num, items in enumerate(rows):
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)
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") % self.device)
dialog = WindowModalDialog(window, _("%s Settings") % device)
vbox = QVBoxLayout()
tabs = QTabWidget()
tabs.addTab(info_tab, _("Information"))
tabs.addTab(QWidget(), _("Advanced"))
tabs.addTab(advanced_tab, _("Advanced"))
vbox.addWidget(tabs)
vbox.addStretch(1)
vbox.addLayout(Buttons(CloseButton(dialog)))
refresh()
dialog.setLayout(vbox)
handler.exec_dialog(dialog)