From 7f3de8241c3856c2e873c5f6e9fa513f454e5d9b Mon Sep 17 00:00:00 2001 From: SomberNight Date: Thu, 31 Jan 2019 20:58:04 +0100 Subject: [PATCH] qt/hww: temporarily bundle our own version of safetlib.qt.pinmatrix until safetlib releases a new version that includes https://github.com/archos-safe-t/python-safet/commit/b1eab3dba4c04fdfc1fcf17b66662c28c5f2380e closes #4960 --- contrib/build-wine/deterministic.spec | 5 + contrib/osx/osx.spec | 5 + electrum/plugins/safe_t/pinmatrix.py | 138 ++++++++++++++++++++++++++ electrum/plugins/safe_t/qt.py | 5 +- 4 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 electrum/plugins/safe_t/pinmatrix.py diff --git a/contrib/build-wine/deterministic.spec b/contrib/build-wine/deterministic.spec index 3429bc7be..2c461dd9d 100644 --- a/contrib/build-wine/deterministic.spec +++ b/contrib/build-wine/deterministic.spec @@ -23,6 +23,11 @@ hiddenimports += collect_submodules('keepkeylib') hiddenimports += collect_submodules('websocket') hiddenimports += collect_submodules('ckcc') +# safetlib imports PyQt5.Qt. We use a local updated copy of pinmatrix.py until they +# release a new version that includes https://github.com/archos-safe-t/python-safet/commit/b1eab3dba4c04fdfc1fcf17b66662c28c5f2380e +hiddenimports.remove('safetlib.qt.pinmatrix') + + # Add libusb binary binaries = [(PYHOME+"/libusb-1.0.dll", ".")] diff --git a/contrib/osx/osx.spec b/contrib/osx/osx.spec index 887ee01d1..c86ee58ed 100644 --- a/contrib/osx/osx.spec +++ b/contrib/osx/osx.spec @@ -66,6 +66,11 @@ hiddenimports += collect_submodules('keepkeylib') hiddenimports += collect_submodules('websocket') hiddenimports += collect_submodules('ckcc') +# safetlib imports PyQt5.Qt. We use a local updated copy of pinmatrix.py until they +# release a new version that includes https://github.com/archos-safe-t/python-safet/commit/b1eab3dba4c04fdfc1fcf17b66662c28c5f2380e +hiddenimports.remove('safetlib.qt.pinmatrix') + + datas = [ (electrum + PYPKG + '/*.json', PYPKG), (electrum + PYPKG + '/wordlist/english.txt', PYPKG + '/wordlist'), diff --git a/electrum/plugins/safe_t/pinmatrix.py b/electrum/plugins/safe_t/pinmatrix.py new file mode 100644 index 000000000..803d98078 --- /dev/null +++ b/electrum/plugins/safe_t/pinmatrix.py @@ -0,0 +1,138 @@ +# copy of https://raw.githubusercontent.com/archos-safe-t/python-safet/b1eab3dba4c04fdfc1fcf17b66662c28c5f2380e/trezorlib/qt/pinmatrix.py + +from __future__ import print_function +import sys +import math + +try: + from PyQt4.QtGui import (QPushButton, QLineEdit, QSizePolicy, QRegExpValidator, QLabel, + QApplication, QWidget, QGridLayout, QVBoxLayout, QHBoxLayout) + from PyQt4.QtCore import QObject, SIGNAL, QRegExp, Qt, QT_VERSION_STR +except: + from PyQt5.QtWidgets import (QPushButton, QLineEdit, QSizePolicy, QLabel, + QApplication, QWidget, QGridLayout, QVBoxLayout, QHBoxLayout) + from PyQt5.QtGui import QRegExpValidator + from PyQt5.QtCore import QRegExp, Qt, QT_VERSION_STR + + +class PinButton(QPushButton): + def __init__(self, password, encoded_value): + super(PinButton, self).__init__('?') + self.password = password + self.encoded_value = encoded_value + + if QT_VERSION_STR >= '5': + self.clicked.connect(self._pressed) + elif QT_VERSION_STR >= '4': + QObject.connect(self, SIGNAL('clicked()'), self._pressed) + else: + raise RuntimeError('Unsupported Qt version') + + def _pressed(self): + self.password.setText(self.password.text() + str(self.encoded_value)) + self.password.setFocus() + + +class PinMatrixWidget(QWidget): + ''' + Displays widget with nine blank buttons and password box. + Encodes button clicks into sequence of numbers for passing + into PinAck messages of TREZOR. + + show_strength=True may be useful for entering new PIN + ''' + def __init__(self, show_strength=True, parent=None): + super(PinMatrixWidget, self).__init__(parent) + + self.password = QLineEdit() + self.password.setValidator(QRegExpValidator(QRegExp('[1-9]+'), None)) + self.password.setEchoMode(QLineEdit.Password) + + if QT_VERSION_STR >= '5': + self.password.textChanged.connect(self._password_changed) + elif QT_VERSION_STR >= '4': + QObject.connect(self.password, SIGNAL('textChanged(QString)'), self._password_changed) + else: + raise RuntimeError('Unsupported Qt version') + + self.strength = QLabel() + self.strength.setMinimumWidth(75) + self.strength.setAlignment(Qt.AlignCenter) + self._set_strength(0) + + grid = QGridLayout() + grid.setSpacing(0) + for y in range(3)[::-1]: + for x in range(3): + button = PinButton(self.password, x + y * 3 + 1) + button.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) + button.setFocusPolicy(Qt.NoFocus) + grid.addWidget(button, 3 - y, x) + + hbox = QHBoxLayout() + hbox.addWidget(self.password) + if show_strength: + hbox.addWidget(self.strength) + + vbox = QVBoxLayout() + vbox.addLayout(grid) + vbox.addLayout(hbox) + self.setLayout(vbox) + + def _set_strength(self, strength): + if strength < 3000: + self.strength.setText('weak') + self.strength.setStyleSheet("QLabel { color : #d00; }") + elif strength < 60000: + self.strength.setText('fine') + self.strength.setStyleSheet("QLabel { color : #db0; }") + elif strength < 360000: + self.strength.setText('strong') + self.strength.setStyleSheet("QLabel { color : #0a0; }") + else: + self.strength.setText('ULTIMATE') + self.strength.setStyleSheet("QLabel { color : #000; font-weight: bold;}") + + def _password_changed(self, password): + self._set_strength(self.get_strength()) + + def get_strength(self): + digits = len(set(str(self.password.text()))) + strength = math.factorial(9) / math.factorial(9 - digits) + return strength + + def get_value(self): + return self.password.text() + + +if __name__ == '__main__': + ''' + Demo application showing PinMatrix widget in action + ''' + app = QApplication(sys.argv) + + matrix = PinMatrixWidget() + + def clicked(): + print("PinMatrix value is", matrix.get_value()) + print("Possible button combinations:", matrix.get_strength()) + sys.exit() + + ok = QPushButton('OK') + if QT_VERSION_STR >= '5': + ok.clicked.connect(clicked) + elif QT_VERSION_STR >= '4': + QObject.connect(ok, SIGNAL('clicked()'), clicked) + else: + raise RuntimeError('Unsupported Qt version') + + vbox = QVBoxLayout() + vbox.addWidget(matrix) + vbox.addWidget(ok) + + w = QWidget() + w.setLayout(vbox) + w.move(100, 100) + w.show() + + app.exec_() diff --git a/electrum/plugins/safe_t/qt.py b/electrum/plugins/safe_t/qt.py index f7d51f259..40e81fb10 100644 --- a/electrum/plugins/safe_t/qt.py +++ b/electrum/plugins/safe_t/qt.py @@ -174,7 +174,10 @@ class Plugin(SafeTPlugin, QtPlugin): @classmethod def pin_matrix_widget_class(self): - from safetlib.qt.pinmatrix import PinMatrixWidget + # We use a local updated copy of pinmatrix.py until safetlib + # releases a new version that includes https://github.com/archos-safe-t/python-safet/commit/b1eab3dba4c04fdfc1fcf17b66662c28c5f2380e + # from safetlib.qt.pinmatrix import PinMatrixWidget + from .pinmatrix import PinMatrixWidget return PinMatrixWidget