Line data Source code
1 : // Copyright (c) 2011-2019 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 :
5 : #if defined(HAVE_CONFIG_H)
6 : #include <config/bitcoin-config.h>
7 : #endif
8 :
9 : #include <qt/askpassphrasedialog.h>
10 : #include <qt/forms/ui_askpassphrasedialog.h>
11 :
12 : #include <qt/guiconstants.h>
13 : #include <qt/guiutil.h>
14 : #include <qt/walletmodel.h>
15 :
16 : #include <support/allocators/secure.h>
17 :
18 : #include <QKeyEvent>
19 : #include <QMessageBox>
20 : #include <QPushButton>
21 :
22 0 : AskPassphraseDialog::AskPassphraseDialog(Mode _mode, QWidget *parent, SecureString* passphrase_out) :
23 0 : QDialog(parent),
24 0 : ui(new Ui::AskPassphraseDialog),
25 0 : mode(_mode),
26 0 : model(nullptr),
27 0 : fCapsLock(false),
28 0 : m_passphrase_out(passphrase_out)
29 0 : {
30 0 : ui->setupUi(this);
31 :
32 0 : ui->passEdit1->setMinimumSize(ui->passEdit1->sizeHint());
33 0 : ui->passEdit2->setMinimumSize(ui->passEdit2->sizeHint());
34 0 : ui->passEdit3->setMinimumSize(ui->passEdit3->sizeHint());
35 :
36 0 : ui->passEdit1->setMaxLength(MAX_PASSPHRASE_SIZE);
37 0 : ui->passEdit2->setMaxLength(MAX_PASSPHRASE_SIZE);
38 0 : ui->passEdit3->setMaxLength(MAX_PASSPHRASE_SIZE);
39 :
40 : // Setup Caps Lock detection.
41 0 : ui->passEdit1->installEventFilter(this);
42 0 : ui->passEdit2->installEventFilter(this);
43 0 : ui->passEdit3->installEventFilter(this);
44 :
45 0 : switch(mode)
46 : {
47 : case Encrypt: // Ask passphrase x2
48 0 : 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>."));
49 0 : ui->passLabel1->hide();
50 0 : ui->passEdit1->hide();
51 0 : setWindowTitle(tr("Encrypt wallet"));
52 0 : break;
53 : case Unlock: // Ask passphrase
54 0 : ui->warningLabel->setText(tr("This operation needs your wallet passphrase to unlock the wallet."));
55 0 : ui->passLabel2->hide();
56 0 : ui->passEdit2->hide();
57 0 : ui->passLabel3->hide();
58 0 : ui->passEdit3->hide();
59 0 : setWindowTitle(tr("Unlock wallet"));
60 0 : break;
61 : case Decrypt: // Ask passphrase
62 0 : ui->warningLabel->setText(tr("This operation needs your wallet passphrase to decrypt the wallet."));
63 0 : ui->passLabel2->hide();
64 0 : ui->passEdit2->hide();
65 0 : ui->passLabel3->hide();
66 0 : ui->passEdit3->hide();
67 0 : setWindowTitle(tr("Decrypt wallet"));
68 0 : break;
69 : case ChangePass: // Ask old passphrase + new passphrase x2
70 0 : setWindowTitle(tr("Change passphrase"));
71 0 : ui->warningLabel->setText(tr("Enter the old passphrase and new passphrase for the wallet."));
72 0 : break;
73 : }
74 0 : textChanged();
75 0 : connect(ui->toggleShowPasswordButton, &QPushButton::toggled, this, &AskPassphraseDialog::toggleShowPassword);
76 0 : connect(ui->passEdit1, &QLineEdit::textChanged, this, &AskPassphraseDialog::textChanged);
77 0 : connect(ui->passEdit2, &QLineEdit::textChanged, this, &AskPassphraseDialog::textChanged);
78 0 : connect(ui->passEdit3, &QLineEdit::textChanged, this, &AskPassphraseDialog::textChanged);
79 :
80 0 : GUIUtil::handleCloseWindowShortcut(this);
81 0 : }
82 :
83 0 : AskPassphraseDialog::~AskPassphraseDialog()
84 0 : {
85 0 : secureClearPassFields();
86 0 : delete ui;
87 0 : }
88 :
89 0 : void AskPassphraseDialog::setModel(WalletModel *_model)
90 : {
91 0 : this->model = _model;
92 0 : }
93 :
94 0 : void AskPassphraseDialog::accept()
95 : {
96 0 : SecureString oldpass, newpass1, newpass2;
97 0 : if (!model && mode != Encrypt)
98 0 : return;
99 0 : oldpass.reserve(MAX_PASSPHRASE_SIZE);
100 0 : newpass1.reserve(MAX_PASSPHRASE_SIZE);
101 0 : newpass2.reserve(MAX_PASSPHRASE_SIZE);
102 : // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
103 : // Alternately, find a way to make this input mlock()'d to begin with.
104 0 : oldpass.assign(ui->passEdit1->text().toStdString().c_str());
105 0 : newpass1.assign(ui->passEdit2->text().toStdString().c_str());
106 0 : newpass2.assign(ui->passEdit3->text().toStdString().c_str());
107 :
108 0 : secureClearPassFields();
109 :
110 0 : switch(mode)
111 : {
112 : case Encrypt: {
113 0 : if(newpass1.empty() || newpass2.empty())
114 : {
115 : // Cannot encrypt with empty passphrase
116 : break;
117 : }
118 0 : QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm wallet encryption"),
119 0 : 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?"),
120 0 : QMessageBox::Yes|QMessageBox::Cancel,
121 : QMessageBox::Cancel);
122 0 : if(retval == QMessageBox::Yes)
123 : {
124 0 : if(newpass1 == newpass2)
125 : {
126 0 : QString encryption_reminder = tr("Remember that encrypting your wallet cannot fully protect "
127 : "your bitcoins from being stolen by malware infecting your computer.");
128 0 : if (m_passphrase_out) {
129 0 : m_passphrase_out->assign(newpass1);
130 0 : QMessageBox::warning(this, tr("Wallet to be encrypted"),
131 0 : "<qt>" +
132 0 : tr("Your wallet is about to be encrypted. ") + encryption_reminder +
133 : "</b></qt>");
134 0 : } else {
135 0 : assert(model != nullptr);
136 0 : if(model->setWalletEncrypted(true, newpass1))
137 : {
138 0 : QMessageBox::warning(this, tr("Wallet encrypted"),
139 0 : "<qt>" +
140 0 : tr("Your wallet is now encrypted. ") + encryption_reminder +
141 0 : "<br><br><b>" +
142 0 : tr("IMPORTANT: Any previous backups you have made of your wallet file "
143 : "should be replaced with the newly generated, encrypted wallet file. "
144 : "For security reasons, previous backups of the unencrypted wallet file "
145 0 : "will become useless as soon as you start using the new, encrypted wallet.") +
146 : "</b></qt>");
147 0 : }
148 : else
149 : {
150 0 : QMessageBox::critical(this, tr("Wallet encryption failed"),
151 0 : tr("Wallet encryption failed due to an internal error. Your wallet was not encrypted."));
152 : }
153 : }
154 0 : QDialog::accept(); // Success
155 0 : }
156 : else
157 : {
158 0 : QMessageBox::critical(this, tr("Wallet encryption failed"),
159 0 : tr("The supplied passphrases do not match."));
160 : }
161 : }
162 : else
163 : {
164 0 : QDialog::reject(); // Cancelled
165 : }
166 0 : } break;
167 : case Unlock:
168 : try {
169 0 : if (!model->setWalletLocked(false, oldpass)) {
170 0 : QMessageBox::critical(this, tr("Wallet unlock failed"),
171 0 : tr("The passphrase entered for the wallet decryption was incorrect."));
172 0 : } else {
173 0 : QDialog::accept(); // Success
174 : }
175 0 : } catch (const std::runtime_error& e) {
176 0 : QMessageBox::critical(this, tr("Wallet unlock failed"), e.what());
177 0 : }
178 : break;
179 : case Decrypt:
180 0 : if(!model->setWalletEncrypted(false, oldpass))
181 : {
182 0 : QMessageBox::critical(this, tr("Wallet decryption failed"),
183 0 : tr("The passphrase entered for the wallet decryption was incorrect."));
184 0 : }
185 : else
186 : {
187 0 : QDialog::accept(); // Success
188 : }
189 : break;
190 : case ChangePass:
191 0 : if(newpass1 == newpass2)
192 : {
193 0 : if(model->changePassphrase(oldpass, newpass1))
194 : {
195 0 : QMessageBox::information(this, tr("Wallet encrypted"),
196 0 : tr("Wallet passphrase was successfully changed."));
197 0 : QDialog::accept(); // Success
198 : }
199 : else
200 : {
201 0 : QMessageBox::critical(this, tr("Wallet encryption failed"),
202 0 : tr("The passphrase entered for the wallet decryption was incorrect."));
203 : }
204 : }
205 : else
206 : {
207 0 : QMessageBox::critical(this, tr("Wallet encryption failed"),
208 0 : tr("The supplied passphrases do not match."));
209 : }
210 : break;
211 : }
212 0 : }
213 :
214 0 : void AskPassphraseDialog::textChanged()
215 : {
216 : // Validate input, set Ok button to enabled when acceptable
217 : bool acceptable = false;
218 0 : switch(mode)
219 : {
220 : case Encrypt: // New passphrase x2
221 0 : acceptable = !ui->passEdit2->text().isEmpty() && !ui->passEdit3->text().isEmpty();
222 0 : break;
223 : case Unlock: // Old passphrase x1
224 : case Decrypt:
225 0 : acceptable = !ui->passEdit1->text().isEmpty();
226 0 : break;
227 : case ChangePass: // Old passphrase x1, new passphrase x2
228 0 : acceptable = !ui->passEdit1->text().isEmpty() && !ui->passEdit2->text().isEmpty() && !ui->passEdit3->text().isEmpty();
229 0 : break;
230 : }
231 0 : ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(acceptable);
232 0 : }
233 :
234 0 : bool AskPassphraseDialog::event(QEvent *event)
235 : {
236 : // Detect Caps Lock key press.
237 0 : if (event->type() == QEvent::KeyPress) {
238 0 : QKeyEvent *ke = static_cast<QKeyEvent *>(event);
239 0 : if (ke->key() == Qt::Key_CapsLock) {
240 0 : fCapsLock = !fCapsLock;
241 0 : }
242 0 : if (fCapsLock) {
243 0 : ui->capsLabel->setText(tr("Warning: The Caps Lock key is on!"));
244 0 : } else {
245 0 : ui->capsLabel->clear();
246 : }
247 0 : }
248 0 : return QWidget::event(event);
249 0 : }
250 :
251 0 : void AskPassphraseDialog::toggleShowPassword(bool show)
252 : {
253 0 : ui->toggleShowPasswordButton->setDown(show);
254 0 : const auto mode = show ? QLineEdit::Normal : QLineEdit::Password;
255 0 : ui->passEdit1->setEchoMode(mode);
256 0 : ui->passEdit2->setEchoMode(mode);
257 0 : ui->passEdit3->setEchoMode(mode);
258 0 : }
259 :
260 0 : bool AskPassphraseDialog::eventFilter(QObject *object, QEvent *event)
261 : {
262 : /* Detect Caps Lock.
263 : * There is no good OS-independent way to check a key state in Qt, but we
264 : * can detect Caps Lock by checking for the following condition:
265 : * Shift key is down and the result is a lower case character, or
266 : * Shift key is not down and the result is an upper case character.
267 : */
268 0 : if (event->type() == QEvent::KeyPress) {
269 0 : QKeyEvent *ke = static_cast<QKeyEvent *>(event);
270 0 : QString str = ke->text();
271 0 : if (str.length() != 0) {
272 0 : const QChar *psz = str.unicode();
273 0 : bool fShift = (ke->modifiers() & Qt::ShiftModifier) != 0;
274 0 : if ((fShift && *psz >= 'a' && *psz <= 'z') || (!fShift && *psz >= 'A' && *psz <= 'Z')) {
275 0 : fCapsLock = true;
276 0 : ui->capsLabel->setText(tr("Warning: The Caps Lock key is on!"));
277 0 : } else if (psz->isLetter()) {
278 0 : fCapsLock = false;
279 0 : ui->capsLabel->clear();
280 : }
281 0 : }
282 0 : }
283 0 : return QDialog::eventFilter(object, event);
284 0 : }
285 :
286 0 : static void SecureClearQLineEdit(QLineEdit* edit)
287 : {
288 : // Attempt to overwrite text so that they do not linger around in memory
289 0 : edit->setText(QString(" ").repeated(edit->text().size()));
290 0 : edit->clear();
291 0 : }
292 :
293 0 : void AskPassphraseDialog::secureClearPassFields()
294 : {
295 0 : SecureClearQLineEdit(ui->passEdit1);
296 0 : SecureClearQLineEdit(ui->passEdit2);
297 0 : SecureClearQLineEdit(ui->passEdit3);
298 0 : }
|