diff --git a/electrum/plugins/hw_wallet/trezor_qt_pinmatrix.py b/electrum/plugins/hw_wallet/trezor_qt_pinmatrix.py new file mode 100644 index 000000000..3458c38f3 --- /dev/null +++ b/electrum/plugins/hw_wallet/trezor_qt_pinmatrix.py @@ -0,0 +1,115 @@ +# from https://github.com/trezor/trezor-firmware/blob/3f1d2059ca140788dab8726778f05cedbea20bc4/python/src/trezorlib/qt/pinmatrix.py +# +# This file is part of the Trezor project. +# +# Copyright (C) 2012-2022 SatoshiLabs and contributors +# +# This library is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# as published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the License along with this library. +# If not, see . + +import math +from typing import Any + +from PyQt6.QtCore import QRegularExpression, Qt +from PyQt6.QtGui import QRegularExpressionValidator +from PyQt6.QtWidgets import ( + QGridLayout, + QHBoxLayout, + QLabel, + QLineEdit, + QPushButton, + QSizePolicy, + QVBoxLayout, + QWidget, +) + + + +class PinButton(QPushButton): + def __init__(self, password: QLineEdit, encoded_value: int) -> None: + super(PinButton, self).__init__("?") + self.password = password + self.encoded_value = encoded_value + + self.clicked.connect(self._pressed) + + def _pressed(self) -> None: + 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: bool = True, parent: Any = None) -> None: + super(PinMatrixWidget, self).__init__(parent) + + self.password = QLineEdit() + self.password.setValidator(QRegularExpressionValidator(QRegularExpression("[1-9]+"), None)) + self.password.setEchoMode(QLineEdit.EchoMode.Password) + + self.password.textChanged.connect(self._password_changed) + + self.strength = QLabel() + self.strength.setMinimumWidth(75) + self.strength.setAlignment(Qt.AlignmentFlag.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.Policy.Expanding, QSizePolicy.Policy.Expanding) + button.setFocusPolicy(Qt.FocusPolicy.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: float) -> None: + 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: Any) -> None: + self._set_strength(self.get_strength()) + + def get_strength(self) -> float: + digits = len(set(str(self.password.text()))) + strength = math.factorial(9) / math.factorial(9 - digits) + return strength + + def get_value(self) -> str: + return self.password.text() diff --git a/electrum/plugins/keepkey/qt.py b/electrum/plugins/keepkey/qt.py index e162ee138..5ef9aa79b 100644 --- a/electrum/plugins/keepkey/qt.py +++ b/electrum/plugins/keepkey/qt.py @@ -15,8 +15,10 @@ from electrum.i18n import _ from electrum.plugin import hook from electrum.logging import Logger -from ..hw_wallet.qt import QtHandlerBase, QtPluginBase -from ..hw_wallet.plugin import only_hook_if_libraries_available +from electrum.plugins.hw_wallet.qt import QtHandlerBase, QtPluginBase +from electrum.plugins.hw_wallet.trezor_qt_pinmatrix import PinMatrixWidget +from electrum.plugins.hw_wallet.plugin import only_hook_if_libraries_available + from .keepkey import KeepKeyPlugin, TIM_NEW, TIM_RECOVER, TIM_MNEMONIC, TIM_PRIVKEY from electrum.gui.qt.wizard.wallet import WCScriptAndDerivation, WCHWUnlock, WCHWXPub, WalletWizardComponent @@ -318,7 +320,6 @@ class Plugin(KeepKeyPlugin, QtPlugin): @classmethod def pin_matrix_widget_class(self): - from keepkeylib.qt.pinmatrix import PinMatrixWidget return PinMatrixWidget @hook diff --git a/electrum/plugins/safe_t/qt.py b/electrum/plugins/safe_t/qt.py index 34860da7d..dcba42f2d 100644 --- a/electrum/plugins/safe_t/qt.py +++ b/electrum/plugins/safe_t/qt.py @@ -15,8 +15,10 @@ from electrum.i18n import _ from electrum.plugin import hook from electrum.logging import Logger -from ..hw_wallet.qt import QtHandlerBase, QtPluginBase -from ..hw_wallet.plugin import only_hook_if_libraries_available +from electrum.plugins.hw_wallet.qt import QtHandlerBase, QtPluginBase +from electrum.plugins.hw_wallet.trezor_qt_pinmatrix import PinMatrixWidget +from electrum.plugins.hw_wallet.plugin import only_hook_if_libraries_available + from .safe_t import SafeTPlugin, TIM_NEW, TIM_RECOVER, TIM_MNEMONIC, TIM_PRIVKEY from electrum.gui.qt.wizard.wallet import WCScriptAndDerivation, WCHWUnlock, WCHWXPub, WalletWizardComponent @@ -194,7 +196,6 @@ class Plugin(SafeTPlugin, QtPlugin): @classmethod def pin_matrix_widget_class(self): - from safetlib.qt.pinmatrix import PinMatrixWidget return PinMatrixWidget @hook diff --git a/electrum/plugins/trezor/qt.py b/electrum/plugins/trezor/qt.py index 9dba1f5c5..cdd983ab9 100644 --- a/electrum/plugins/trezor/qt.py +++ b/electrum/plugins/trezor/qt.py @@ -14,6 +14,7 @@ from electrum.plugin import hook from electrum.keystore import ScriptTypeNotSupported from electrum.plugins.hw_wallet.qt import QtHandlerBase, QtPluginBase +from electrum.plugins.hw_wallet.trezor_qt_pinmatrix import PinMatrixWidget from electrum.plugins.hw_wallet.plugin import only_hook_if_libraries_available, OutdatedHwFirmwareException from electrum.gui.qt.util import (WindowModalDialog, WWLabel, Buttons, CancelButton, @@ -462,7 +463,6 @@ class Plugin(TrezorPlugin, QtPlugin): @classmethod def pin_matrix_widget_class(self): - from trezorlib.qt.pinmatrix import PinMatrixWidget return PinMatrixWidget @hook