PIVX Core  5.6.99
P2P Digital Currency
askpassphrasedialog.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2014 The Bitcoin developers
2 // Copyright (c) 2014-2015 The Dash developers
3 // Copyright (c) 2015-2022 The PIVX Core developers
4 // Distributed under the MIT/X11 software license, see the accompanying
5 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
6 
7 #if defined(HAVE_CONFIG_H)
8 #include "config/pivx-config.h"
9 #endif
10 
11 #include "askpassphrasedialog.h"
12 #include "ui_askpassphrasedialog.h"
13 
14 #include "defaultdialog.h"
15 #include "guiconstants.h"
16 #include "guiutil.h"
17 #include "loadingdialog.h"
18 #include "qtutils.h"
19 #include "walletmodel.h"
20 
21 #include <QGraphicsDropShadowEffect>
22 #include <QKeyEvent>
23 #include <QMessageBox>
24 #include <QPushButton>
25 #include <QWidget>
26 
27 AskPassphraseDialog::AskPassphraseDialog(Mode mode, QWidget* parent, WalletModel* model, Context context) : QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint),
28  ui(new Ui::AskPassphraseDialog),
29  mode(mode),
30  model(model),
31  context(context),
32  fCapsLock(false),
33  btnWatch(new QCheckBox())
34 {
35  ui->setupUi(this);
36  this->setStyleSheet(GUIUtil::loadStyleSheet());
37 
38  ui->left->setProperty("cssClass", "container-dialog");
39 
40  ui->labelTitle->setText("Change passphrase");
41  ui->labelTitle->setProperty("cssClass", "text-title-screen");
42 
43  ui->warningLabel->setProperty("cssClass", "text-subtitle");
44 
45  ui->btnEsc->setText("");
46  ui->btnEsc->setProperty("cssClass", "ic-close");
47 
48  ui->pushButtonOk->setText("OK");
49  ui->pushButtonOk->setProperty("cssClass", "btn-primary");
50 
51  initCssEditLine(ui->passEdit1);
52  initCssEditLine(ui->passEdit2);
53  initCssEditLine(ui->passEdit3);
54 
55  ui->passLabel1->setText("Current passphrase");
56  ui->passLabel1->setProperty("cssClass", "text-title");
57 
58  ui->passLabel2->setText("New passphrase");
59  ui->passLabel2->setProperty("cssClass", "text-title");
60 
61  ui->passLabel3->setText("Repeat passphrase");
62  ui->passLabel3->setProperty("cssClass", "text-title");
63 
64  setCssProperty(ui->passWarningLabel, "text-warning-small");
65  ui->passWarningLabel->setVisible(false);
66 
67  ui->passEdit1->setMinimumSize(ui->passEdit1->sizeHint());
68  ui->passEdit2->setMinimumSize(ui->passEdit2->sizeHint());
69  ui->passEdit3->setMinimumSize(ui->passEdit3->sizeHint());
70 
71  ui->passEdit1->setMaxLength(MAX_PASSPHRASE_SIZE);
72  ui->passEdit2->setMaxLength(MAX_PASSPHRASE_SIZE);
73  ui->passEdit3->setMaxLength(MAX_PASSPHRASE_SIZE);
74 
75  setShadow(ui->layoutEdit);
76  setShadow(ui->layoutEdit2);
77 
78  // Setup Caps Lock detection.
79  ui->passEdit1->installEventFilter(this);
80  ui->passEdit2->installEventFilter(this);
81  ui->passEdit3->installEventFilter(this);
82 
83  this->model = model;
84 
85  QString title;
86  switch (mode) {
87  case Mode::Encrypt: // Ask passphrase x2
88  ui->warningLabel->setText(tr("Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>ten or more random characters</b>, or <b>eight or more words</b>."));
89  ui->passLabel1->hide();
90  ui->passEdit1->hide();
91  ui->layoutEdit->hide();
92  title = tr("Encrypt wallet");
93  initWatch(ui->layoutEdit2);
94  break;
96  ui->warningLabel->setText(tr("This operation needs your wallet passphrase to unlock the wallet."));
97  ui->passLabel2->hide();
98  ui->passEdit2->hide();
99  ui->layoutEdit2->hide();
100  ui->passLabel3->hide();
101  ui->passEdit3->hide();
102  title = tr("Unlock wallet\nfor staking");
103  initWatch(ui->layoutEdit);
104  break;
105  case Mode::Unlock: // Ask passphrase
106  ui->warningLabel->setText(tr("This operation needs your wallet passphrase to unlock the wallet."));
107  ui->passLabel2->hide();
108  ui->passEdit2->hide();
109  ui->layoutEdit2->hide();
110  ui->passLabel3->hide();
111  ui->passEdit3->hide();
112  title = tr("Unlock wallet");
113  initWatch(ui->layoutEdit);
114  break;
115  case Mode::Decrypt: // Ask passphrase
116  ui->warningLabel->setText(tr("This operation needs your wallet passphrase to decrypt the wallet."));
117  ui->passLabel2->hide();
118  ui->passEdit2->hide();
119  ui->layoutEdit2->hide();
120  ui->passLabel3->hide();
121  ui->passEdit3->hide();
122  title = tr("Decrypt wallet");
123  initWatch(ui->layoutEdit);
124  break;
125  case Mode::ChangePass: // Ask old passphrase + new passphrase x2
126  title = tr("Change passphrase");
127  ui->warningLabel->setText(tr("Enter the old and new passphrase to the wallet."));
128  initWatch(ui->layoutEdit);
129  break;
130  }
131 
132  ui->labelTitle->setText(title);
133 
134  textChanged();
135  connect(btnWatch, &QCheckBox::clicked, this, &AskPassphraseDialog::onWatchClicked);
136  connect(ui->passEdit1, &QLineEdit::textChanged, this, &AskPassphraseDialog::textChanged);
137  connect(ui->passEdit2, &QLineEdit::textChanged, this, &AskPassphraseDialog::textChanged);
138  connect(ui->passEdit3, &QLineEdit::textChanged, this, &AskPassphraseDialog::textChanged);
139  connect(ui->pushButtonOk, &QPushButton::clicked, this, &AskPassphraseDialog::accept);
140  connect(ui->btnEsc, &QPushButton::clicked, this, &AskPassphraseDialog::close);
141 }
142 
144 {
145  int state = btnWatch->checkState();
146  ui->passEdit3->setEchoMode(state == Qt::Checked ? QLineEdit::Normal : QLineEdit::Password );
147  ui->passEdit2->setEchoMode(state== Qt::Checked ? QLineEdit::Normal : QLineEdit::Password );
148  ui->passEdit1->setEchoMode(state == Qt::Checked ? QLineEdit::Normal : QLineEdit::Password );
149 }
150 
152 {
153  // Attempt to overwrite text so that they do not linger around in memory
154  ui->passEdit1->setText(QString(" ").repeated(ui->passEdit1->text().size()));
155  ui->passEdit2->setText(QString(" ").repeated(ui->passEdit2->text().size()));
156  ui->passEdit3->setText(QString(" ").repeated(ui->passEdit3->text().size()));
157  delete ui;
158 }
159 
160 void AskPassphraseDialog::showEvent(QShowEvent *event)
161 {
162  if (mode == Mode::Encrypt && ui->passEdit2) ui->passEdit2->setFocus();
163  else if (ui->passEdit1) ui->passEdit1->setFocus();
164 }
165 
167 {
168  SecureString oldpass, newpass1, newpass2;
169  if (!model)
170  return;
171  oldpass.reserve(MAX_PASSPHRASE_SIZE);
172  newpass1.reserve(MAX_PASSPHRASE_SIZE);
173  newpass2.reserve(MAX_PASSPHRASE_SIZE);
174  // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
175  // Alternately, find a way to make this input mlock()'d to begin with.
176  oldpass.assign(ui->passEdit1->text().toStdString().c_str());
177  newpass1.assign(ui->passEdit2->text().toStdString().c_str());
178  newpass2.assign(ui->passEdit3->text().toStdString().c_str());
179 
180  switch (mode) {
181  case Mode::Encrypt: {
182  if (newpass1.empty() || newpass2.empty()) {
183  // Cannot encrypt with empty passphrase
184  break;
185  }
186  hide();
187  bool ret = openStandardDialog(
188  tr("Confirm wallet encryption"),
189  "<b>" + tr("WARNING") + ":</b> " + tr("If you encrypt your wallet and lose your passphrase, you will") +
190  " <b>" + tr("LOSE ALL OF YOUR COINS") + "</b>!<br><br>" + tr("Are you sure you wish to encrypt your wallet?"),
191  tr("ENCRYPT"), tr("CANCEL")
192  );
193  if (ret) {
194  newpassCache = newpass1;
195  PIVXGUI* window = static_cast<PIVXGUI*>(parentWidget());
196  LoadingDialog *dialog = new LoadingDialog(window);
197  dialog->execute(this, 1);
199  } else {
200  QDialog::reject(); // Cancelled
201  }
202  } break;
204  if (!model->setWalletLocked(false, oldpass, true)) {
205  QMessageBox::critical(this, tr("Wallet unlock failed"),
206  tr("The passphrase entered for the wallet decryption was incorrect."));
207  } else {
208  QDialog::accept(); // Success
209  }
210  break;
211  case Mode::Unlock:
212  if (!model->setWalletLocked(false, oldpass, false)) {
213  QMessageBox::critical(this, tr("Wallet unlock failed"),
214  tr("The passphrase entered for the wallet decryption was incorrect."));
215  } else {
216  QDialog::accept(); // Success
217  }
218  break;
219  case Mode::Decrypt:
220  if (!model->setWalletEncrypted(false, oldpass)) {
221  QMessageBox::critical(this, tr("Wallet decryption failed"),
222  tr("The passphrase entered for the wallet decryption was incorrect."));
223  } else {
224  QDialog::accept(); // Success
225  }
226  break;
227  case Mode::ChangePass:
228  if (newpass1 == newpass2) {
229  if (model->changePassphrase(oldpass, newpass1)) {
230  hide();
231  openStandardDialog(tr("Wallet encrypted"),tr("Wallet passphrase was successfully changed."));
232  QDialog::accept(); // Success
233  } else {
234  QMessageBox::critical(this, tr("Wallet encryption failed"),
235  tr("The passphrase entered for the wallet decryption was incorrect."));
236  }
237  } else {
238  QMessageBox::critical(this, tr("Wallet encryption failed"),
239  tr("The supplied passphrases do not match."));
240  }
241  break;
242  }
243 }
244 
246 {
247  // Validate input, set Ok button to enabled when acceptable
248  bool acceptable = false;
249  switch (mode) {
250  case Mode::Encrypt: // New passphrase x2
251  acceptable = !ui->passEdit2->text().isEmpty() && !ui->passEdit3->text().isEmpty() && // Passphrases are not empty
252  ui->passEdit2->text() == ui->passEdit3->text(); // Passphrases match each other
253  break;
254  case Mode::UnlockAnonymize: // Old passphrase x1
255  case Mode::Unlock: // Old passphrase x1
256  case Mode::Decrypt:
257  acceptable = !ui->passEdit1->text().isEmpty();
258  break;
259  case Mode::ChangePass: // Old passphrase x1, new passphrase x2
260  acceptable = !ui->passEdit2->text().isEmpty() && !ui->passEdit3->text().isEmpty() && // New passphrases are not empty
261  ui->passEdit2->text() == ui->passEdit3->text() && // New passphrases match each other
262  !ui->passEdit1->text().isEmpty(); // Old passphrase is not empty
263  break;
264  }
265  ui->pushButtonOk->setEnabled(acceptable);
266 }
267 
268 bool AskPassphraseDialog::event(QEvent* event)
269 {
270  if (event->type() == QEvent::KeyPress) {
271  QKeyEvent* ke = static_cast<QKeyEvent*>(event);
272  // Detect Caps Lock key press.
273  if (ke->key() == Qt::Key_CapsLock) {
274  fCapsLock = !fCapsLock;
275  }
276 
278 
279  // Detect Enter key press
280  if ((ke->key() == Qt::Key_Enter || ke->key() == Qt::Key_Return) && ui->pushButtonOk->isEnabled()) {
281  accept();
282  }
283  }
284  return QDialog::event(event);
285 }
286 
287 bool AskPassphraseDialog::eventFilter(QObject* object, QEvent* event)
288 {
289  /* Detect Caps Lock.
290  * There is no good OS-independent way to check a key state in Qt, but we
291  * can detect Caps Lock by checking for the following condition:
292  * Shift key is down and the result is a lower case character, or
293  * Shift key is not down and the result is an upper case character.
294  */
295  if (event->type() == QEvent::KeyPress) {
296  QKeyEvent* ke = static_cast<QKeyEvent*>(event);
297  QString str = ke->text();
298  if (str.length() != 0) {
299  const QChar* psz = str.unicode();
300  bool fShift = (ke->modifiers() & Qt::ShiftModifier) != 0;
301  if ((fShift && *psz >= 'a' && *psz <= 'z') || (!fShift && *psz >= 'A' && *psz <= 'Z')) {
302  fCapsLock = true;
303  } else if (psz->isLetter()) {
304  fCapsLock = false;
305  }
306  }
307  }
309 
310  return QDialog::eventFilter(object, event);
311 }
312 
313 bool AskPassphraseDialog::openStandardDialog(QString title, QString body, QString okBtn, QString cancelBtn)
314 {
315  PIVXGUI* gui = static_cast<PIVXGUI*>(parentWidget());
316  DefaultDialog *confirmDialog = new DefaultDialog(gui);
317  confirmDialog->setText(title, body, okBtn, cancelBtn);
318  confirmDialog->adjustSize();
319  openDialogWithOpaqueBackground(confirmDialog, gui);
320  bool ret = confirmDialog->isOk;
321  confirmDialog->deleteLater();
322  return ret;
323 }
324 
326 {
327  // Merge warning labels together if there's two warnings
328  bool validPassphrases = false;
329  validPassphrases = ui->passEdit2->text() == ui->passEdit3->text();
330  QString warningStr = "";
331  if (fCapsLock || !validPassphrases) warningStr += tr("WARNING:") + "<br>";
332  if (fCapsLock) warningStr += "* " + tr("The caps lock key is on!");
333  if (fCapsLock && !validPassphrases) warningStr += "<br>";
334  if (!validPassphrases) warningStr += "* " + tr("Passphrases do not match!");
335 
336  if (warningStr.isEmpty()) {
337  ui->passWarningLabel->clear();
338  ui->passWarningLabel->setVisible(false);
339  } else {
340  ui->passWarningLabel->setText(warningStr);
341  ui->passWarningLabel->setVisible(true);
342  }
343 }
344 
346 {
347  hide();
348  static_cast<PIVXGUI*>(parentWidget())->showHide(true);
350  tr("Wallet encrypted"),
351  "<qt>" +
352  tr("Your wallet is now encrypted. "
353  "Remember that encrypting your wallet cannot fully protect "
354  "your PIVs from being stolen by malware infecting your computer.") +
355  "<br><br><b>" +
356  tr("IMPORTANT: Any previous backups you have made of your wallet file "
357  "should be replaced with the newly generated, encrypted wallet file. "
358  "For security reasons, previous backups of the unencrypted wallet file "
359  "will become useless as soon as you start using the new, encrypted wallet.") +
360  "</b></qt>",
361  tr("OK")
362  );
363 }
364 
366 {
367  QMessageBox::critical(this, tr("Wallet encryption failed"),
368  tr("Wallet encryption failed due to an internal error. Your wallet was not encrypted."));
369 }
370 
372 {
373  if (type == 1) {
374  if (!newpassCache.empty()) {
375  QMetaObject::invokeMethod(this, "hide", Qt::QueuedConnection);
376  if (model->setWalletEncrypted(true, newpassCache)) {
377  QMetaObject::invokeMethod(this, "warningMessage", Qt::QueuedConnection);
378  } else {
379  QMetaObject::invokeMethod(this, "errorEncryptingWallet", Qt::QueuedConnection);
380  }
381  newpassCache.clear();
382  QDialog::accept(); // Success
383  }
384  }
385 }
386 void AskPassphraseDialog::onError(QString error, int type)
387 {
388  newpassCache.clear();
389  LogPrintf("Error encrypting wallet, %s\n", error.toStdString());
390  QMetaObject::invokeMethod(this, "errorEncryptingWallet", Qt::QueuedConnection);
391 }
392 
393 void AskPassphraseDialog::initWatch(QWidget *parent)
394 {
395  btnWatch = new QCheckBox(parent);
396  setCssProperty(btnWatch, "btn-watch-password");
397  btnWatch->setChecked(false);
398  QSize BUTTON_CONTACT_SIZE = QSize(24, 24);
399  btnWatch->setMinimumSize(BUTTON_CONTACT_SIZE);
400  btnWatch->setMaximumSize(BUTTON_CONTACT_SIZE);
401  btnWatch->show();
402  btnWatch->raise();
403 
404  int posYY = 8;
405  btnWatch->move(450, posYY);
406 }
false
Definition: bls_dkg.cpp:151
Multifunctional dialog to ask for passphrases.
bool openStandardDialog(QString title="", QString body="", QString okBtn="OK", QString cancelBtn="")
bool eventFilter(QObject *object, QEvent *event) override
void onError(QString error, int type) override
bool event(QEvent *event) override
void run(int type) override
void initWatch(QWidget *parent)
@ ChangePass
Ask old passphrase + new passphrase twice.
@ Encrypt
Ask passphrase twice and encrypt.
@ UnlockAnonymize
Ask passphrase and unlock only for anonymization.
@ Decrypt
Ask passphrase and decrypt wallet.
@ Unlock
Ask passphrase and unlock.
AskPassphraseDialog(Mode mode, QWidget *parent, WalletModel *model, Context context)
Ui::AskPassphraseDialog * ui
void showEvent(QShowEvent *event) override
void setText(const QString &title="", const QString &message="", const QString &okBtnText="", const QString &cancelBtnText="")
void execute(Runnable *runnable, int type, std::unique_ptr< WalletModel::UnlockContext > pctx=nullptr)
PIVX GUI main class.
Definition: pivxgui.h:46
Interface to PIVX wallet from Qt view code.
Definition: walletmodel.h:109
bool setWalletEncrypted(bool encrypted, const SecureString &passphrase)
bool changePassphrase(const SecureString &oldPass, const SecureString &newPass)
bool setWalletLocked(bool locked, const SecureString &passPhrase=SecureString(), bool stakingOnly=false)
QString loadStyleSheet()
Load global CSS theme.
Definition: guiutil.cpp:639
bool openDialogWithOpaqueBackgroundFullScreen(QDialog *widget, PIVXGUI *gui)
Definition: qtutils.cpp:81
void initCssEditLine(QLineEdit *edit, bool isDialog)
Definition: qtutils.cpp:272
bool openDialogWithOpaqueBackground(QDialog *widget, PIVXGUI *gui, double posX)
Definition: qtutils.cpp:76
void setShadow(QWidget *edit)
Definition: qtutils.cpp:292
void setCssProperty(std::initializer_list< QWidget * > args, const QString &value)
Definition: qtutils.cpp:334
std::basic_string< char, std::char_traits< char >, secure_allocator< char > > SecureString
Definition: secure.h:61
bool error(const char *fmt, const Args &... args)
Definition: system.h:77