Browse Source

Trezor: Matrix recovery support

New Trezor firmware has matrix recovery support, which is a new
recovery method that doesn't leak the entered words.
master
Jochen Hoenicke 10 years ago committed by SomberNight
parent
commit
6dd5161729
No known key found for this signature in database
GPG Key ID: B33B5F232C6271E9
  1. 9
      plugins/trezor/clientbase.py
  2. 86
      plugins/trezor/qt.py

9
plugins/trezor/clientbase.py

@ -84,6 +84,15 @@ class GuiMixin(object):
return self.proto.PassphraseStateAck() return self.proto.PassphraseStateAck()
def callback_WordRequest(self, msg): def callback_WordRequest(self, msg):
if (msg.type is not None
and msg.type in (self.types.WordRequestType_Matrix9,
self.types.WordRequestType_Matrix6)):
num = 9 if msg.type == self.types.WordRequestType_Matrix9 else 6
char = self.handler.get_matrix(num)
if (char == 'x'):
return self.proto.Cancel()
return self.proto.WordAck(word=char)
self.step += 1 self.step += 1
msg = _("Step {}/24. Enter seed word as explained on " msg = _("Step {}/24. Enter seed word as explained on "
"your {}:").format(self.step, self.device) "your {}:").format(self.step, self.device)

86
plugins/trezor/qt.py

@ -30,16 +30,86 @@ PASSPHRASE_NOT_PIN = _(
"If you forget a passphrase you will be unable to access any " "If you forget a passphrase you will be unable to access any "
"bitcoins in the wallet behind it. A passphrase is not a PIN. " "bitcoins in the wallet behind it. A passphrase is not a PIN. "
"Only change this if you are sure you understand it.") "Only change this if you are sure you understand it.")
MATRIX_RECOVERY = (
"Enter the recovery words by pressing the buttons according to what "
"the device shows on its display. You can also use your NUMPAD.\n"
"Press BACKSPACE to go back a choice or word.\n")
class MatrixDialog(WindowModalDialog):
def __init__(self, parent):
super(MatrixDialog, self).__init__(parent)
self.setWindowTitle(_("Trezor Matrix Recovery"))
self.num = 9
self.loop = QEventLoop()
vbox = QVBoxLayout(self)
vbox.addWidget(WWLabel(MATRIX_RECOVERY))
grid = QGridLayout()
grid.setSpacing(0)
self.char_buttons = [];
for y in range(3):
for x in range(3):
button = QPushButton('?')
button.clicked.connect(partial(self.process_key, ord('1') + y * 3 + x))
grid.addWidget(button, 3 - y, x)
self.char_buttons.append(button)
vbox.addLayout(grid)
hbox = QHBoxLayout()
self.backspace_button = QPushButton(_("<="))
self.backspace_button.clicked.connect(partial(self.process_key, Qt.Key_Backspace))
self.cancel_button = QPushButton(_("Cancel"))
self.cancel_button.clicked.connect(partial(self.process_key, Qt.Key_Escape))
buttons = Buttons(self.backspace_button, self.cancel_button)
vbox.addSpacing(40)
vbox.addLayout(buttons)
self.refresh()
self.show()
def refresh(self):
for y in range(3):
self.char_buttons[3 * y + 1].setEnabled(self.num == 9)
def is_valid(self, key):
return key >= ord('1') and key <= ord('9')
def process_key(self, key):
self.data = None
if key == Qt.Key_Backspace:
self.data = '\010'
elif key == Qt.Key_Escape:
self.data = 'x'
elif self.is_valid(key):
self.char_buttons[key - ord('1')].setFocus()
self.data = '%c' % key;
if self.data:
self.loop.exit(0)
def keyPressEvent(self, event):
self.process_key(event.key())
if not self.data:
QDialog.keyPressEvent(self, event)
def get_matrix(self, num):
self.num = num
self.refresh()
self.loop.exec_()
class QtHandler(QtHandlerBase): class QtHandler(QtHandlerBase):
pin_signal = pyqtSignal(object) pin_signal = pyqtSignal(object)
matrix_signal = pyqtSignal(object)
def __init__(self, win, pin_matrix_widget_class, device): def __init__(self, win, pin_matrix_widget_class, device):
super(QtHandler, self).__init__(win, device) super(QtHandler, self).__init__(win, device)
self.pin_signal.connect(self.pin_dialog) self.pin_signal.connect(self.pin_dialog)
self.pin_matrix_widget_class = pin_matrix_widget_class self.pin_matrix_widget_class = pin_matrix_widget_class
self.matrix_signal.connect(self.matrix_recovery_dialog)
self.matrix_dialog = None
def get_pin(self, msg): def get_pin(self, msg):
self.done.clear() self.done.clear()
@ -47,6 +117,16 @@ class QtHandler(QtHandlerBase):
self.done.wait() self.done.wait()
return self.response return self.response
def get_matrix(self, msg):
self.done.clear()
self.matrix_signal.emit(msg)
self.done.wait()
data = self.matrix_dialog.data
if data == 'x':
self.matrix_dialog.accept()
self.matrix_dialog = None
return data
def pin_dialog(self, msg): def pin_dialog(self, msg):
# Needed e.g. when resetting a device # Needed e.g. when resetting a device
self.clear_dialog() self.clear_dialog()
@ -61,6 +141,12 @@ class QtHandler(QtHandlerBase):
self.response = str(matrix.get_value()) self.response = str(matrix.get_value())
self.done.set() self.done.set()
def matrix_recovery_dialog(self, msg):
if not self.matrix_dialog:
self.matrix_dialog = MatrixDialog(self.top_level_window())
self.matrix_dialog.get_matrix(msg)
self.done.set()
class QtPlugin(QtPluginBase): class QtPlugin(QtPluginBase):
# Derived classes must provide the following class-static variables: # Derived classes must provide the following class-static variables:

Loading…
Cancel
Save