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.
3.2.x
Jochen Hoenicke 8 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