1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-12-22 06:18:07 +00:00
trezor-firmware/python/trezorlib/qt/pinmatrix.py

174 lines
5.2 KiB
Python
Raw Normal View History

# This file is part of the Trezor project.
#
# Copyright (C) 2012-2018 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 <https://www.gnu.org/licenses/lgpl-3.0.html>.
2014-07-09 22:02:34 +00:00
import math
2018-08-13 16:21:24 +00:00
import sys
2016-11-28 16:56:32 +00:00
try:
from PyQt5.QtWidgets import (
2018-08-13 16:21:24 +00:00
QPushButton,
QLineEdit,
QSizePolicy,
QLabel,
QApplication,
QWidget,
QGridLayout,
QVBoxLayout,
QHBoxLayout,
)
from PyQt5.QtGui import QRegExpValidator
2018-12-26 03:12:11 +00:00
from PyQt5.QtCore import QRegExp, Qt, QT_VERSION_STR
except Exception:
from PyQt4.QtGui import (
2018-08-13 16:21:24 +00:00
QPushButton,
QLineEdit,
QSizePolicy,
QRegExpValidator,
2018-08-13 16:21:24 +00:00
QLabel,
QApplication,
QWidget,
QGridLayout,
QVBoxLayout,
QHBoxLayout,
)
from PyQt4.QtCore import QObject, SIGNAL, QRegExp, Qt, QT_VERSION_STR
2014-07-09 22:02:34 +00:00
2017-06-23 19:31:42 +00:00
2014-07-09 22:02:34 +00:00
class PinButton(QPushButton):
def __init__(self, password, encoded_value):
2018-08-13 16:21:24 +00:00
super(PinButton, self).__init__("?")
2014-07-09 22:02:34 +00:00
self.password = password
self.encoded_value = encoded_value
2018-08-13 16:21:24 +00:00
if QT_VERSION_STR >= "5":
2016-11-28 16:56:32 +00:00
self.clicked.connect(self._pressed)
2018-08-13 16:21:24 +00:00
elif QT_VERSION_STR >= "4":
QObject.connect(self, SIGNAL("clicked()"), self._pressed)
2016-11-28 16:56:32 +00:00
else:
2018-08-13 16:21:24 +00:00
raise RuntimeError("Unsupported Qt version")
2014-07-09 22:02:34 +00:00
def _pressed(self):
self.password.setText(self.password.text() + str(self.encoded_value))
self.password.setFocus()
2017-06-23 19:31:42 +00:00
2014-07-09 22:02:34 +00:00
class PinMatrixWidget(QWidget):
2018-08-13 16:21:24 +00:00
"""
2014-07-09 22:02:34 +00:00
Displays widget with nine blank buttons and password box.
Encodes button clicks into sequence of numbers for passing
2016-02-10 15:46:58 +00:00
into PinAck messages of TREZOR.
2014-07-09 22:02:34 +00:00
show_strength=True may be useful for entering new PIN
2018-08-13 16:21:24 +00:00
"""
2014-07-09 22:02:34 +00:00
def __init__(self, show_strength=True, parent=None):
super(PinMatrixWidget, self).__init__(parent)
2016-01-12 23:17:38 +00:00
2014-07-09 22:02:34 +00:00
self.password = QLineEdit()
2018-08-13 16:21:24 +00:00
self.password.setValidator(QRegExpValidator(QRegExp("[1-9]+"), None))
2014-07-09 22:02:34 +00:00
self.password.setEchoMode(QLineEdit.Password)
2016-11-28 16:56:32 +00:00
2018-08-13 16:21:24 +00:00
if QT_VERSION_STR >= "5":
2016-11-28 16:56:32 +00:00
self.password.textChanged.connect(self._password_changed)
2018-08-13 16:21:24 +00:00
elif QT_VERSION_STR >= "4":
QObject.connect(
self.password, SIGNAL("textChanged(QString)"), self._password_changed
)
2016-11-28 16:56:32 +00:00
else:
2018-08-13 16:21:24 +00:00
raise RuntimeError("Unsupported Qt version")
2014-07-09 22:02:34 +00:00
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:
2018-08-13 16:21:24 +00:00
self.strength.setText("weak")
2014-07-09 22:02:34 +00:00
self.strength.setStyleSheet("QLabel { color : #d00; }")
elif strength < 60000:
2018-08-13 16:21:24 +00:00
self.strength.setText("fine")
2014-07-09 22:02:34 +00:00
self.strength.setStyleSheet("QLabel { color : #db0; }")
elif strength < 360000:
2018-08-13 16:21:24 +00:00
self.strength.setText("strong")
2014-07-09 22:02:34 +00:00
self.strength.setStyleSheet("QLabel { color : #0a0; }")
else:
2018-08-13 16:21:24 +00:00
self.strength.setText("ULTIMATE")
2014-07-09 22:02:34 +00:00
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()
2017-06-23 19:31:42 +00:00
2018-08-13 16:21:24 +00:00
if __name__ == "__main__":
"""
2014-07-09 22:02:34 +00:00
Demo application showing PinMatrix widget in action
2018-08-13 16:21:24 +00:00
"""
2016-11-28 16:56:32 +00:00
app = QApplication(sys.argv)
2014-07-09 22:02:34 +00:00
matrix = PinMatrixWidget()
def clicked():
2016-05-20 11:36:17 +00:00
print("PinMatrix value is", matrix.get_value())
print("Possible button combinations:", matrix.get_strength())
2014-07-09 22:02:34 +00:00
sys.exit()
2018-08-13 16:21:24 +00:00
ok = QPushButton("OK")
if QT_VERSION_STR >= "5":
2016-11-28 16:56:32 +00:00
ok.clicked.connect(clicked)
2018-08-13 16:21:24 +00:00
elif QT_VERSION_STR >= "4":
QObject.connect(ok, SIGNAL("clicked()"), clicked)
2016-11-28 16:56:32 +00:00
else:
2018-08-13 16:21:24 +00:00
raise RuntimeError("Unsupported Qt version")
2014-07-09 22:02:34 +00:00
vbox = QVBoxLayout()
vbox.addWidget(matrix)
vbox.addWidget(ok)
w = QWidget()
w.setLayout(vbox)
w.move(100, 100)
w.show()
2016-11-28 16:56:32 +00:00
app.exec_()