Bitcoin Core 28.99.0
P2P Digital Currency
askpassphrasedialog.cpp
Go to the documentation of this file.
1// Copyright (c) 2011-2021 The Bitcoin 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
6#include <qt/forms/ui_askpassphrasedialog.h>
7
8#include <qt/guiconstants.h>
9#include <qt/guiutil.h>
10#include <qt/walletmodel.h>
11
13
14#include <QKeyEvent>
15#include <QMessageBox>
16#include <QPushButton>
17
18AskPassphraseDialog::AskPassphraseDialog(Mode _mode, QWidget *parent, SecureString* passphrase_out) :
19 QDialog(parent, GUIUtil::dialog_flags),
20 ui(new Ui::AskPassphraseDialog),
21 mode(_mode),
22 m_passphrase_out(passphrase_out)
23{
24 ui->setupUi(this);
25
26 ui->passEdit1->setMinimumSize(ui->passEdit1->sizeHint());
27 ui->passEdit2->setMinimumSize(ui->passEdit2->sizeHint());
28 ui->passEdit3->setMinimumSize(ui->passEdit3->sizeHint());
29
30 ui->passEdit1->setMaxLength(MAX_PASSPHRASE_SIZE);
31 ui->passEdit2->setMaxLength(MAX_PASSPHRASE_SIZE);
32 ui->passEdit3->setMaxLength(MAX_PASSPHRASE_SIZE);
33
34 // Setup Caps Lock detection.
35 ui->passEdit1->installEventFilter(this);
36 ui->passEdit2->installEventFilter(this);
37 ui->passEdit3->installEventFilter(this);
38
39 switch(mode)
40 {
41 case Encrypt: // Ask passphrase x2
42 ui->warningLabel->setText(tr("Enter the new passphrase for the wallet.<br/>Please use a passphrase of <b>ten or more random characters</b>, or <b>eight or more words</b>."));
43 ui->passLabel1->hide();
44 ui->passEdit1->hide();
45 setWindowTitle(tr("Encrypt wallet"));
46 break;
47 case UnlockMigration:
48 case Unlock: // Ask passphrase
49 ui->warningLabel->setText(tr("This operation needs your wallet passphrase to unlock the wallet."));
50 ui->passLabel2->hide();
51 ui->passEdit2->hide();
52 ui->passLabel3->hide();
53 ui->passEdit3->hide();
54 setWindowTitle(tr("Unlock wallet"));
55 break;
56 case ChangePass: // Ask old passphrase + new passphrase x2
57 setWindowTitle(tr("Change passphrase"));
58 ui->warningLabel->setText(tr("Enter the old passphrase and new passphrase for the wallet."));
59 break;
60 }
62 connect(ui->toggleShowPasswordButton, &QPushButton::toggled, this, &AskPassphraseDialog::toggleShowPassword);
63 connect(ui->passEdit1, &QLineEdit::textChanged, this, &AskPassphraseDialog::textChanged);
64 connect(ui->passEdit2, &QLineEdit::textChanged, this, &AskPassphraseDialog::textChanged);
65 connect(ui->passEdit3, &QLineEdit::textChanged, this, &AskPassphraseDialog::textChanged);
66
68}
69
71{
73 delete ui;
74}
75
77{
78 this->model = _model;
79}
80
82{
83 SecureString oldpass, newpass1, newpass2;
84 if (!model && mode != Encrypt && mode != UnlockMigration)
85 return;
86 oldpass.reserve(MAX_PASSPHRASE_SIZE);
87 newpass1.reserve(MAX_PASSPHRASE_SIZE);
88 newpass2.reserve(MAX_PASSPHRASE_SIZE);
89
90 oldpass.assign(std::string_view{ui->passEdit1->text().toStdString()});
91 newpass1.assign(std::string_view{ui->passEdit2->text().toStdString()});
92 newpass2.assign(std::string_view{ui->passEdit3->text().toStdString()});
93
95
96 switch(mode)
97 {
98 case Encrypt: {
99 if(newpass1.empty() || newpass2.empty())
100 {
101 // Cannot encrypt with empty passphrase
102 break;
103 }
104 QMessageBox msgBoxConfirm(QMessageBox::Question,
105 tr("Confirm wallet encryption"),
106 tr("Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR BITCOINS</b>!") + "<br><br>" + tr("Are you sure you wish to encrypt your wallet?"),
107 QMessageBox::Cancel | QMessageBox::Yes, this);
108 msgBoxConfirm.button(QMessageBox::Yes)->setText(tr("Continue"));
109 msgBoxConfirm.button(QMessageBox::Cancel)->setText(tr("Back"));
110 msgBoxConfirm.setDefaultButton(QMessageBox::Cancel);
111 QMessageBox::StandardButton retval = (QMessageBox::StandardButton)msgBoxConfirm.exec();
112 if(retval == QMessageBox::Yes)
113 {
114 if(newpass1 == newpass2)
115 {
116 QString encryption_reminder = tr("Remember that encrypting your wallet cannot fully protect "
117 "your bitcoins from being stolen by malware infecting your computer.");
118 if (m_passphrase_out) {
119 m_passphrase_out->assign(newpass1);
120 QMessageBox msgBoxWarning(QMessageBox::Warning,
121 tr("Wallet to be encrypted"),
122 "<qt>" +
123 tr("Your wallet is about to be encrypted. ") + encryption_reminder + " " +
124 tr("Are you sure you wish to encrypt your wallet?") +
125 "</b></qt>",
126 QMessageBox::Cancel | QMessageBox::Yes, this);
127 msgBoxWarning.setDefaultButton(QMessageBox::Cancel);
128 QMessageBox::StandardButton retval = (QMessageBox::StandardButton)msgBoxWarning.exec();
129 if (retval == QMessageBox::Cancel) {
130 QDialog::reject();
131 return;
132 }
133 } else {
134 assert(model != nullptr);
135 if (model->setWalletEncrypted(newpass1)) {
136 QMessageBox::warning(this, tr("Wallet encrypted"),
137 "<qt>" +
138 tr("Your wallet is now encrypted. ") + encryption_reminder +
139 "<br><br><b>" +
140 tr("IMPORTANT: Any previous backups you have made of your wallet file "
141 "should be replaced with the newly generated, encrypted wallet file. "
142 "For security reasons, previous backups of the unencrypted wallet file "
143 "will become useless as soon as you start using the new, encrypted wallet.") +
144 "</b></qt>");
145 } else {
146 QMessageBox::critical(this, tr("Wallet encryption failed"),
147 tr("Wallet encryption failed due to an internal error. Your wallet was not encrypted."));
148 }
149 }
150 QDialog::accept(); // Success
151 }
152 else
153 {
154 QMessageBox::critical(this, tr("Wallet encryption failed"),
155 tr("The supplied passphrases do not match."));
156 }
157 }
158 } break;
159 case Unlock:
160 try {
161 if (!model->setWalletLocked(false, oldpass)) {
162 // Check if the passphrase has a null character (see #27067 for details)
163 if (oldpass.find('\0') == std::string::npos) {
164 QMessageBox::critical(this, tr("Wallet unlock failed"),
165 tr("The passphrase entered for the wallet decryption was incorrect."));
166 } else {
167 QMessageBox::critical(this, tr("Wallet unlock failed"),
168 tr("The passphrase entered for the wallet decryption is incorrect. "
169 "It contains a null character (ie - a zero byte). "
170 "If the passphrase was set with a version of this software prior to 25.0, "
171 "please try again with only the characters up to — but not including — "
172 "the first null character. If this is successful, please set a new "
173 "passphrase to avoid this issue in the future."));
174 }
175 } else {
176 if (m_passphrase_out) {
177 m_passphrase_out->assign(oldpass);
178 }
179 QDialog::accept(); // Success
180 }
181 } catch (const std::runtime_error& e) {
182 QMessageBox::critical(this, tr("Wallet unlock failed"), e.what());
183 }
184 break;
185 case UnlockMigration:
186 Assume(m_passphrase_out)->assign(oldpass);
187 QDialog::accept();
188 break;
189 case ChangePass:
190 if(newpass1 == newpass2)
191 {
192 if(model->changePassphrase(oldpass, newpass1))
193 {
194 QMessageBox::information(this, tr("Wallet encrypted"),
195 tr("Wallet passphrase was successfully changed."));
196 QDialog::accept(); // Success
197 }
198 else
199 {
200 // Check if the old passphrase had a null character (see #27067 for details)
201 if (oldpass.find('\0') == std::string::npos) {
202 QMessageBox::critical(this, tr("Passphrase change failed"),
203 tr("The passphrase entered for the wallet decryption was incorrect."));
204 } else {
205 QMessageBox::critical(this, tr("Passphrase change failed"),
206 tr("The old passphrase entered for the wallet decryption is incorrect. "
207 "It contains a null character (ie - a zero byte). "
208 "If the passphrase was set with a version of this software prior to 25.0, "
209 "please try again with only the characters up to — but not including — "
210 "the first null character."));
211 }
212 }
213 }
214 else
215 {
216 QMessageBox::critical(this, tr("Wallet encryption failed"),
217 tr("The supplied passphrases do not match."));
218 }
219 break;
220 }
221}
222
224{
225 // Validate input, set Ok button to enabled when acceptable
226 bool acceptable = false;
227 switch(mode)
228 {
229 case Encrypt: // New passphrase x2
230 acceptable = !ui->passEdit2->text().isEmpty() && !ui->passEdit3->text().isEmpty();
231 break;
232 case UnlockMigration:
233 case Unlock: // Old passphrase x1
234 acceptable = !ui->passEdit1->text().isEmpty();
235 break;
236 case ChangePass: // Old passphrase x1, new passphrase x2
237 acceptable = !ui->passEdit1->text().isEmpty() && !ui->passEdit2->text().isEmpty() && !ui->passEdit3->text().isEmpty();
238 break;
239 }
240 ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(acceptable);
241}
242
243bool AskPassphraseDialog::event(QEvent *event)
244{
245 // Detect Caps Lock key press.
246 if (event->type() == QEvent::KeyPress) {
247 QKeyEvent *ke = static_cast<QKeyEvent *>(event);
248 if (ke->key() == Qt::Key_CapsLock) {
250 }
251 if (fCapsLock) {
252 ui->capsLabel->setText(tr("Warning: The Caps Lock key is on!"));
253 } else {
254 ui->capsLabel->clear();
255 }
256 }
257 return QWidget::event(event);
258}
259
261{
262 ui->toggleShowPasswordButton->setDown(show);
263 const auto mode = show ? QLineEdit::Normal : QLineEdit::Password;
264 ui->passEdit1->setEchoMode(mode);
265 ui->passEdit2->setEchoMode(mode);
266 ui->passEdit3->setEchoMode(mode);
267}
268
269bool AskPassphraseDialog::eventFilter(QObject *object, QEvent *event)
270{
271 /* Detect Caps Lock.
272 * There is no good OS-independent way to check a key state in Qt, but we
273 * can detect Caps Lock by checking for the following condition:
274 * Shift key is down and the result is a lower case character, or
275 * Shift key is not down and the result is an upper case character.
276 */
277 if (event->type() == QEvent::KeyPress) {
278 QKeyEvent *ke = static_cast<QKeyEvent *>(event);
279 QString str = ke->text();
280 if (str.length() != 0) {
281 const QChar *psz = str.unicode();
282 bool fShift = (ke->modifiers() & Qt::ShiftModifier) != 0;
283 if ((fShift && *psz >= 'a' && *psz <= 'z') || (!fShift && *psz >= 'A' && *psz <= 'Z')) {
284 fCapsLock = true;
285 ui->capsLabel->setText(tr("Warning: The Caps Lock key is on!"));
286 } else if (psz->isLetter()) {
287 fCapsLock = false;
288 ui->capsLabel->clear();
289 }
290 }
291 }
292 return QDialog::eventFilter(object, event);
293}
294
295static void SecureClearQLineEdit(QLineEdit* edit)
296{
297 // Attempt to overwrite text so that they do not linger around in memory
298 edit->setText(QString(" ").repeated(edit->text().size()));
299 edit->clear();
300}
301
303{
304 SecureClearQLineEdit(ui->passEdit1);
305 SecureClearQLineEdit(ui->passEdit2);
306 SecureClearQLineEdit(ui->passEdit3);
307}
static void SecureClearQLineEdit(QLineEdit *edit)
#define Assume(val)
Assume is the identity function.
Definition: check.h:97
Multifunctional dialog to ask for passphrases.
SecureString * m_passphrase_out
AskPassphraseDialog(Mode mode, QWidget *parent, SecureString *passphrase_out=nullptr)
void setModel(WalletModel *model)
bool eventFilter(QObject *object, QEvent *event) override
bool event(QEvent *event) override
@ Unlock
Ask passphrase and unlock.
@ Encrypt
Ask passphrase twice and encrypt.
@ ChangePass
Ask old passphrase + new passphrase twice.
@ UnlockMigration
Ask passphrase for unlocking during migration.
Ui::AskPassphraseDialog * ui
Interface to Bitcoin wallet from Qt view code.
Definition: walletmodel.h:48
bool setWalletEncrypted(const SecureString &passphrase)
bool changePassphrase(const SecureString &oldPass, const SecureString &newPass)
bool setWalletLocked(bool locked, const SecureString &passPhrase=SecureString())
static const int MAX_PASSPHRASE_SIZE
Definition: guiconstants.h:20
Utility functions used by the Bitcoin Qt UI.
Definition: bitcoingui.h:58
void handleCloseWindowShortcut(QWidget *w)
Definition: guiutil.cpp:431
constexpr auto dialog_flags
Definition: guiutil.h:60
Warning
Definition: warning.h:9
std::basic_string< char, std::char_traits< char >, secure_allocator< char > > SecureString
Definition: secure.h:58
assert(!tx.IsCoinBase())