Skip to content

Commit a10dea3

Browse files
Add wallet app locker
Co-Authored-By: Jenova7 <[email protected]>
1 parent bc2a9fe commit a10dea3

File tree

6 files changed

+411
-15
lines changed

6 files changed

+411
-15
lines changed

src/Makefile.qt.include

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ include Makefile.qt_locale.include
1515

1616
QT_FORMS_UI = \
1717
qt/forms/addressbookpage.ui \
18+
qt/forms/applocker.ui \
1819
qt/forms/askpassphrasedialog.ui \
1920
qt/forms/coincontroldialog.ui \
2021
qt/forms/createwalletdialog.ui \
@@ -37,6 +38,7 @@ QT_FORMS_UI = \
3738
QT_MOC_CPP = \
3839
qt/moc_addressbookpage.cpp \
3940
qt/moc_addresstablemodel.cpp \
41+
qt/moc_applocker.cpp \
4042
qt/moc_askpassphrasedialog.cpp \
4143
qt/moc_createwalletdialog.cpp \
4244
qt/moc_bantablemodel.cpp \
@@ -106,6 +108,7 @@ QT_QRC_LOCALE = qt/xep_locale.qrc
106108
BITCOIN_QT_H = \
107109
qt/addressbookpage.h \
108110
qt/addresstablemodel.h \
111+
qt/applocker.h \
109112
qt/askpassphrasedialog.h \
110113
qt/bantablemodel.h \
111114
qt/xepaddressvalidator.h \
@@ -242,6 +245,7 @@ BITCOIN_QT_WINDOWS_CPP = qt/winshutdownmonitor.cpp
242245
BITCOIN_QT_WALLET_CPP = \
243246
qt/addressbookpage.cpp \
244247
qt/addresstablemodel.cpp \
248+
qt/applocker.cpp \
245249
qt/askpassphrasedialog.cpp \
246250
qt/coincontroldialog.cpp \
247251
qt/coincontroltreewidget.cpp \

src/qt/applocker.cpp

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
// Copyright (c) 2021 The XEP Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include <qt/applocker.h>
6+
#include <qt/forms/ui_applocker.h>
7+
8+
#include <crypto/pbkdf2_hmac.h>
9+
10+
AppLocker::AppLocker(QWidget *parent) :
11+
QWidget(parent),
12+
ui(new Ui::AppLocker)
13+
{
14+
ui->setupUi(this);
15+
this->setWindowTitle(tr("Wallet locker"));
16+
this->setWindowModality(Qt::ApplicationModal);
17+
QRegExpValidator *validatorReg = new QRegExpValidator(QRegExp("[1-9]\\d{5,9}"), this);
18+
19+
// Lock view (index 1)
20+
ui->stackedWidget->setCurrentIndex(1);
21+
ui->headLabel->setText(tr("Set a PIN to lock your wallet:") + "<br>");
22+
ui->messageLabel->setText("<br>- " + tr("Your PIN must be at least 6 digits long.") +
23+
"<br>- " + tr("The PIN is only valid for this session."));
24+
ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Lock"));
25+
ui->buttonBox->button(QDialogButtonBox::Ok)->setDefault(true);
26+
ui->buttonBox->button(QDialogButtonBox::Cancel)->setAutoDefault(true);
27+
ui->pinLineEdit->setValidator(validatorReg);
28+
ui->pinLineEdit->setEchoMode(QLineEdit::Password);
29+
ui->confirmLineEdit->setValidator(validatorReg);
30+
ui->confirmLineEdit->setEchoMode(QLineEdit::Password);
31+
32+
// Unlock view
33+
ui->lockLabel->setText(tr("Your wallet is locked.") + "<br>");
34+
ui->unlocklabel->setText(tr("PIN"));
35+
ui->unlockLineEdit->setValidator(validatorReg);
36+
ui->unlockLineEdit->setEchoMode(QLineEdit::Password);
37+
38+
connect(ui->unlockLineEdit, &QLineEdit::textChanged, [this] {
39+
if (ui->unlockLineEdit->text().size() >= 6) {
40+
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
41+
} else if (ui->stackedWidget->currentIndex() == 0) {
42+
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
43+
}
44+
});
45+
connect(ui->pinLineEdit, &QLineEdit::returnPressed, ui->buttonBox, &QDialogButtonBox::accepted);
46+
connect(ui->confirmLineEdit, &QLineEdit::returnPressed, ui->buttonBox, &QDialogButtonBox::accepted);
47+
connect(ui->unlockLineEdit, &QLineEdit::returnPressed, ui->buttonBox, &QDialogButtonBox::accepted);
48+
connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &AppLocker::setLock);
49+
connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &AppLocker::close);
50+
}
51+
52+
AppLocker::~AppLocker()
53+
{
54+
delete ui;
55+
}
56+
57+
void AppLocker::setLock()
58+
{
59+
switch (ui->stackedWidget->currentIndex()) {
60+
case 0:
61+
if (pbkdf2_hmac_sha256_time_check(reinterpret_cast<const unsigned char*>(ui->unlockLineEdit->text().toUtf8().constData()), ui->unlockLineEdit->text().size(), reinterpret_cast<const unsigned char*>(salt.constData()), salt.size(), 3, pinHash)) {
62+
walletLocked = false;
63+
secureClearPinFields();
64+
ui->stackedWidget->setCurrentIndex(1);
65+
ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Lock"));
66+
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
67+
ui->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(true);
68+
ui->buttonBox->button(QDialogButtonBox::Cancel)->setVisible(true);
69+
ui->pinLineEdit->setFocus();
70+
Q_EMIT lockingApp(false);
71+
} else {
72+
QMessageBox::warning(this, tr("Error"), tr("The entered PIN is incorrect."), QMessageBox::Ok);
73+
}
74+
break;
75+
case 1:
76+
if (ui->pinLineEdit->text().isEmpty() || ui->confirmLineEdit->text().isEmpty()) {
77+
QMessageBox::information(this, tr("Error"), tr("Please enter and confirm your PIN."), QMessageBox::Ok);
78+
} else if (ui->pinLineEdit->text().size() < 6 || ui->confirmLineEdit->text().size() < 6) {
79+
QMessageBox::information(this, tr("Error"), tr("Your PIN must be at least 6 digits long."), QMessageBox::Ok);
80+
} else if (ui->pinLineEdit->text() != ui->confirmLineEdit->text()) {
81+
QMessageBox::warning(this, tr("Error"), tr("The entered PINs don't match, please try again."), QMessageBox::Ok);
82+
} else {
83+
walletLocked = true;
84+
salt = QString::number(QDateTime::currentMSecsSinceEpoch()).toUtf8();
85+
// pinHash = QPasswordDigestor::deriveKeyPbkdf2(QCryptographicHash::Sha256, ui->pinLineEdit->text().toUtf8(), salt, 10000, quint64(32));
86+
pbkdf2_hmac_sha256_time(reinterpret_cast<const unsigned char*>(ui->pinLineEdit->text().toUtf8().constData()), ui->pinLineEdit->text().size(), reinterpret_cast<const unsigned char*>(salt.constData()), salt.size(), 1, pinHash);
87+
secureClearPinFields();
88+
ui->stackedWidget->setCurrentIndex(0); // move to unlock view
89+
ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Unlock"));
90+
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
91+
ui->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(false);
92+
ui->buttonBox->button(QDialogButtonBox::Cancel)->setVisible(false);
93+
ui->unlockLineEdit->setFocus();
94+
Q_EMIT lockingApp(true);
95+
}
96+
break;
97+
}
98+
}
99+
100+
void AppLocker::showLocker()
101+
{
102+
this->move(QGuiApplication::primaryScreen()->geometry().center() - this->rect().center());
103+
if (ui->stackedWidget->currentIndex() == 1) {
104+
ui->pinLineEdit->setFocus();
105+
}
106+
this->show();
107+
this->setFixedSize(this->size());
108+
}
109+
110+
void AppLocker::closeEvent(QCloseEvent *event)
111+
{
112+
if (walletLocked) {
113+
int ret = -1;
114+
if (!forceClose) {
115+
ret = QMessageBox::warning(this, tr("Warning"), tr("The wallet application will exit, would you like to continue?"), QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Cancel);
116+
if (ret == QMessageBox::Cancel) {
117+
event->ignore();
118+
}
119+
}
120+
if (forceClose || ret == QMessageBox::Ok) {
121+
// Clear memory being used by app locker
122+
secureClearPinFields();
123+
Q_EMIT quitAppFromWalletLocker();
124+
event->accept();
125+
}
126+
} else if (ui->stackedWidget->currentIndex() == 1) {
127+
// Clear memory being used by app locker
128+
secureClearPinFields();
129+
event->accept();
130+
} else {
131+
event->ignore();
132+
}
133+
}
134+
135+
void AppLocker::secureClearPinFields()
136+
{
137+
// Overwrite text so that it does not remain in memory
138+
ui->unlockLineEdit->setText(QString(" ").repeated(ui->unlockLineEdit->text().size()));
139+
ui->pinLineEdit->setText(QString(" ").repeated(ui->pinLineEdit->text().size()));
140+
ui->confirmLineEdit->setText(QString(" ").repeated(ui->confirmLineEdit->text().size()));
141+
ui->unlockLineEdit->clear();
142+
ui->pinLineEdit->clear();
143+
ui->confirmLineEdit->clear();
144+
}

src/qt/applocker.h

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright (c) 2021 The XEP Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#ifndef APPLOCKER_H
6+
#define APPLOCKER_H
7+
8+
#include <QCloseEvent>
9+
#include <QDateTime>
10+
#include <QDesktopWidget>
11+
#include <QMessageBox>
12+
//#include <QPasswordDigestor>
13+
#include <QPushButton>
14+
#include <QRegExpValidator>
15+
#include <QScreen>
16+
#include <QWidget>
17+
18+
QT_BEGIN_NAMESPACE
19+
namespace Ui {
20+
class AppLocker;
21+
}
22+
QT_END_NAMESPACE
23+
24+
class AppLocker : public QWidget
25+
{
26+
Q_OBJECT
27+
28+
public:
29+
AppLocker(QWidget *parent);
30+
bool isWalletLocked() { return walletLocked; }
31+
void forceShutdown() { forceClose = true; }
32+
~AppLocker();
33+
34+
private:
35+
Ui::AppLocker *ui;
36+
unsigned char pinHash[32]; // A SHA256 hash requires 32 bytes to store
37+
QByteArray salt;
38+
39+
bool walletLocked = false;
40+
bool forceClose = false;
41+
void setLock();
42+
void secureClearPinFields();
43+
44+
Q_SIGNALS:
45+
void lockingApp(bool);
46+
void quitAppFromWalletLocker();
47+
48+
public Q_SLOTS:
49+
void showLocker();
50+
51+
protected:
52+
void closeEvent(QCloseEvent *event) override;
53+
};
54+
55+
#endif // APPLOCKER_H

src/qt/forms/applocker.ui

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<ui version="4.0">
3+
<class>AppLocker</class>
4+
<widget class="QWidget" name="AppLocker">
5+
<property name="geometry">
6+
<rect>
7+
<x>0</x>
8+
<y>0</y>
9+
<width>338</width>
10+
<height>156</height>
11+
</rect>
12+
</property>
13+
<property name="windowTitle">
14+
<string>LockView</string>
15+
</property>
16+
<layout class="QVBoxLayout" name="verticalLayout">
17+
<item>
18+
<widget class="QStackedWidget" name="stackedWidget">
19+
<property name="currentIndex">
20+
<number>0</number>
21+
</property>
22+
<widget class="QWidget" name="unlockStack">
23+
<layout class="QVBoxLayout" name="verticalLayout_3">
24+
<item>
25+
<widget class="QLabel" name="lockLabel">
26+
<property name="text">
27+
<string>TextLabel</string>
28+
</property>
29+
<property name="alignment">
30+
<set>Qt::AlignCenter</set>
31+
</property>
32+
</widget>
33+
</item>
34+
<item>
35+
<layout class="QHBoxLayout" name="horizontalLayout">
36+
<item>
37+
<spacer name="horizontalSpacer">
38+
<property name="orientation">
39+
<enum>Qt::Horizontal</enum>
40+
</property>
41+
<property name="sizeHint" stdset="0">
42+
<size>
43+
<width>40</width>
44+
<height>20</height>
45+
</size>
46+
</property>
47+
</spacer>
48+
</item>
49+
<item>
50+
<widget class="QLabel" name="unlocklabel">
51+
<property name="text">
52+
<string>TextLabel</string>
53+
</property>
54+
</widget>
55+
</item>
56+
<item>
57+
<widget class="QLineEdit" name="unlockLineEdit">
58+
<property name="minimumSize">
59+
<size>
60+
<width>0</width>
61+
<height>0</height>
62+
</size>
63+
</property>
64+
<property name="maximumSize">
65+
<size>
66+
<width>100</width>
67+
<height>16777215</height>
68+
</size>
69+
</property>
70+
</widget>
71+
</item>
72+
<item>
73+
<spacer name="horizontalSpacer_2">
74+
<property name="orientation">
75+
<enum>Qt::Horizontal</enum>
76+
</property>
77+
<property name="sizeHint" stdset="0">
78+
<size>
79+
<width>40</width>
80+
<height>20</height>
81+
</size>
82+
</property>
83+
</spacer>
84+
</item>
85+
</layout>
86+
</item>
87+
</layout>
88+
</widget>
89+
<widget class="QWidget" name="lockStack">
90+
<layout class="QVBoxLayout" name="verticalLayout_2">
91+
<item>
92+
<widget class="QLabel" name="headLabel">
93+
<property name="text">
94+
<string>TextLabel</string>
95+
</property>
96+
</widget>
97+
</item>
98+
<item>
99+
<layout class="QFormLayout" name="formLayout">
100+
<item row="0" column="0">
101+
<widget class="QLabel" name="passwordLabel">
102+
<property name="text">
103+
<string>Enter PIN</string>
104+
</property>
105+
</widget>
106+
</item>
107+
<item row="0" column="1">
108+
<widget class="QLineEdit" name="pinLineEdit">
109+
<property name="font">
110+
<font>
111+
<underline>false</underline>
112+
<kerning>true</kerning>
113+
</font>
114+
</property>
115+
</widget>
116+
</item>
117+
<item row="1" column="0">
118+
<widget class="QLabel" name="confirmPasswordLabel">
119+
<property name="text">
120+
<string>Confirm PIN</string>
121+
</property>
122+
</widget>
123+
</item>
124+
<item row="1" column="1">
125+
<widget class="QLineEdit" name="confirmLineEdit"/>
126+
</item>
127+
</layout>
128+
</item>
129+
<item>
130+
<widget class="QLabel" name="messageLabel">
131+
<property name="font">
132+
<font>
133+
<weight>50</weight>
134+
<italic>false</italic>
135+
<bold>false</bold>
136+
<underline>false</underline>
137+
</font>
138+
</property>
139+
<property name="text">
140+
<string/>
141+
</property>
142+
</widget>
143+
</item>
144+
</layout>
145+
</widget>
146+
</widget>
147+
</item>
148+
<item>
149+
<widget class="QDialogButtonBox" name="buttonBox">
150+
<property name="standardButtons">
151+
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
152+
</property>
153+
</widget>
154+
</item>
155+
</layout>
156+
</widget>
157+
<resources/>
158+
<connections/>
159+
</ui>

0 commit comments

Comments
 (0)