diff --git a/src/qt/askpassphrasedialog.cpp b/src/qt/askpassphrasedialog.cpp index b85483643..95eca7115 100644 --- a/src/qt/askpassphrasedialog.cpp +++ b/src/qt/askpassphrasedialog.cpp @@ -1,280 +1,291 @@ // Copyright (c) 2011-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) #include #endif #include #include #include #include #include #include #include #include -AskPassphraseDialog::AskPassphraseDialog(Mode _mode, QWidget *parent) +AskPassphraseDialog::AskPassphraseDialog(Mode _mode, QWidget *parent, + SecureString *passphrase_out) : QDialog(parent), ui(new Ui::AskPassphraseDialog), mode(_mode), - model(nullptr), fCapsLock(false) { + model(nullptr), fCapsLock(false), m_passphrase_out(passphrase_out) { ui->setupUi(this); ui->passEdit1->setMinimumSize(ui->passEdit1->sizeHint()); ui->passEdit2->setMinimumSize(ui->passEdit2->sizeHint()); ui->passEdit3->setMinimumSize(ui->passEdit3->sizeHint()); ui->passEdit1->setMaxLength(MAX_PASSPHRASE_SIZE); ui->passEdit2->setMaxLength(MAX_PASSPHRASE_SIZE); ui->passEdit3->setMaxLength(MAX_PASSPHRASE_SIZE); // Setup Caps Lock detection. ui->passEdit1->installEventFilter(this); ui->passEdit2->installEventFilter(this); ui->passEdit3->installEventFilter(this); switch (mode) { case Encrypt: // Ask passphrase x2 ui->warningLabel->setText( tr("Enter the new passphrase to the wallet.
Please use a " "passphrase of ten or more random characters, or " "eight or more words.")); ui->passLabel1->hide(); ui->passEdit1->hide(); setWindowTitle(tr("Encrypt wallet")); break; case Unlock: // Ask passphrase ui->warningLabel->setText(tr("This operation needs your wallet " "passphrase to unlock the wallet.")); ui->passLabel2->hide(); ui->passEdit2->hide(); ui->passLabel3->hide(); ui->passEdit3->hide(); setWindowTitle(tr("Unlock wallet")); break; case Decrypt: // Ask passphrase ui->warningLabel->setText(tr("This operation needs your wallet " "passphrase to decrypt the wallet.")); ui->passLabel2->hide(); ui->passEdit2->hide(); ui->passLabel3->hide(); ui->passEdit3->hide(); setWindowTitle(tr("Decrypt wallet")); break; case ChangePass: // Ask old passphrase + new passphrase x2 setWindowTitle(tr("Change passphrase")); ui->warningLabel->setText(tr( "Enter the old passphrase and new passphrase to the wallet.")); break; } textChanged(); connect(ui->toggleShowPasswordButton, &QPushButton::toggled, this, &AskPassphraseDialog::toggleShowPassword); connect(ui->passEdit1, &QLineEdit::textChanged, this, &AskPassphraseDialog::textChanged); connect(ui->passEdit2, &QLineEdit::textChanged, this, &AskPassphraseDialog::textChanged); connect(ui->passEdit3, &QLineEdit::textChanged, this, &AskPassphraseDialog::textChanged); } AskPassphraseDialog::~AskPassphraseDialog() { secureClearPassFields(); delete ui; } void AskPassphraseDialog::setModel(WalletModel *_model) { this->model = _model; } void AskPassphraseDialog::accept() { SecureString oldpass, newpass1, newpass2; - if (!model) { + if (!model && mode != Encrypt) { return; } oldpass.reserve(MAX_PASSPHRASE_SIZE); newpass1.reserve(MAX_PASSPHRASE_SIZE); newpass2.reserve(MAX_PASSPHRASE_SIZE); // TODO: get rid of this .c_str() by implementing // SecureString::operator=(std::string) // Alternately, find a way to make this input mlock()'d to begin with. oldpass.assign(ui->passEdit1->text().toStdString().c_str()); newpass1.assign(ui->passEdit2->text().toStdString().c_str()); newpass2.assign(ui->passEdit3->text().toStdString().c_str()); secureClearPassFields(); switch (mode) { case Encrypt: { if (newpass1.empty() || newpass2.empty()) { // Cannot encrypt with empty passphrase break; } QMessageBox::StandardButton retval = QMessageBox::question( this, tr("Confirm wallet encryption"), tr("Warning: If you encrypt your wallet and lose your " "passphrase, you will LOSE ALL OF YOUR BITCOINS!") + "

" + tr("Are you sure you wish to encrypt your wallet?"), QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel); if (retval == QMessageBox::Yes) { if (newpass1 == newpass2) { - if (model->setWalletEncrypted(true, newpass1)) { + QString encryption_reminder = + tr("Remember that encrypting your wallet cannot fully " + "protect your bitcoins from being stolen by malware " + "infecting your computer."); + if (m_passphrase_out) { + m_passphrase_out->assign(newpass1); QMessageBox::warning( - this, tr("Wallet encrypted"), + this, tr("Wallet to be encrypted"), "" + - tr("Your wallet is now encrypted. " - "Remember that encrypting your wallet " - "cannot fully protect your bitcoins from " - "being stolen by malware infecting your " - "computer.") + - "

" + - tr("IMPORTANT: Any previous backups you have " - "made of your wallet file should be " - "replaced with the newly generated, " - "encrypted wallet file. " - "For security reasons, previous backups of " - "the unencrypted wallet file will become " - "useless as soon as you start using the " - "new, encrypted wallet.") + - "
"); + tr("Your wallet is about to be encrypted. ") + + encryption_reminder + "
"); } else { - QMessageBox::critical( - this, tr("Wallet encryption failed"), - tr("Wallet encryption failed due to an internal " - "error. Your wallet was not encrypted.")); + assert(model != nullptr); + if (model->setWalletEncrypted(true, newpass1)) { + QMessageBox::warning( + this, tr("Wallet encrypted"), + "" + tr("Your wallet is now encrypted. ") + + encryption_reminder + "

" + + tr("IMPORTANT: Any previous backups you " + "have made of your wallet file should " + "be replaced with the newly generated, " + "encrypted wallet file. For security " + "reasons, previous backups of the " + "unencrypted wallet file will become " + "useless as soon as you start using the " + "new, encrypted wallet.") + + "
"); + } else { + QMessageBox::critical( + this, tr("Wallet encryption failed"), + tr("Wallet encryption failed due to an " + "internal error. Your wallet was not " + "encrypted.")); + } } QDialog::accept(); // Success } else { QMessageBox::critical( this, tr("Wallet encryption failed"), tr("The supplied passphrases do not match.")); } } else { QDialog::reject(); // Cancelled } } break; case Unlock: if (!model->setWalletLocked(false, oldpass)) { QMessageBox::critical(this, tr("Wallet unlock failed"), tr("The passphrase entered for the " "wallet decryption was incorrect.")); } else { QDialog::accept(); // Success } break; case Decrypt: if (!model->setWalletEncrypted(false, oldpass)) { QMessageBox::critical(this, tr("Wallet decryption failed"), tr("The passphrase entered for the " "wallet decryption was incorrect.")); } else { QDialog::accept(); // Success } break; case ChangePass: if (newpass1 == newpass2) { if (model->changePassphrase(oldpass, newpass1)) { QMessageBox::information( this, tr("Wallet encrypted"), tr("Wallet passphrase was successfully changed.")); QDialog::accept(); // Success } else { QMessageBox::critical( this, tr("Wallet encryption failed"), tr("The passphrase entered for the wallet decryption " "was incorrect.")); } } else { QMessageBox::critical( this, tr("Wallet encryption failed"), tr("The supplied passphrases do not match.")); } break; } } void AskPassphraseDialog::textChanged() { // Validate input, set Ok button to enabled when acceptable bool acceptable = false; switch (mode) { case Encrypt: // New passphrase x2 acceptable = !ui->passEdit2->text().isEmpty() && !ui->passEdit3->text().isEmpty(); break; case Unlock: // Old passphrase x1 case Decrypt: acceptable = !ui->passEdit1->text().isEmpty(); break; case ChangePass: // Old passphrase x1, new passphrase x2 acceptable = !ui->passEdit1->text().isEmpty() && !ui->passEdit2->text().isEmpty() && !ui->passEdit3->text().isEmpty(); break; } ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(acceptable); } bool AskPassphraseDialog::event(QEvent *event) { // Detect Caps Lock key press. if (event->type() == QEvent::KeyPress) { QKeyEvent *ke = static_cast(event); if (ke->key() == Qt::Key_CapsLock) { fCapsLock = !fCapsLock; } if (fCapsLock) { ui->capsLabel->setText(tr("Warning: The Caps Lock key is on!")); } else { ui->capsLabel->clear(); } } return QWidget::event(event); } void AskPassphraseDialog::toggleShowPassword(bool show) { ui->toggleShowPasswordButton->setDown(show); const auto renderingMode = show ? QLineEdit::Normal : QLineEdit::Password; ui->passEdit1->setEchoMode(renderingMode); ui->passEdit2->setEchoMode(renderingMode); ui->passEdit3->setEchoMode(renderingMode); } bool AskPassphraseDialog::eventFilter(QObject *object, QEvent *event) { /* Detect Caps Lock. - * There is no good OS-independent way to check a key state in Qt, but we - * can detect Caps Lock by checking for the following condition: + * There is no good OS-independent way to check a key state in Qt, but + * we can detect Caps Lock by checking for the following condition: * Shift key is down and the result is a lower case character, or * Shift key is not down and the result is an upper case character. */ if (event->type() == QEvent::KeyPress) { QKeyEvent *ke = static_cast(event); QString str = ke->text(); if (str.length() != 0) { const QChar *psz = str.unicode(); bool fShift = (ke->modifiers() & Qt::ShiftModifier) != 0; if ((fShift && *psz >= 'a' && *psz <= 'z') || (!fShift && *psz >= 'A' && *psz <= 'Z')) { fCapsLock = true; ui->capsLabel->setText(tr("Warning: The Caps Lock key is on!")); } else if (psz->isLetter()) { fCapsLock = false; ui->capsLabel->clear(); } } } return QDialog::eventFilter(object, event); } static void SecureClearQLineEdit(QLineEdit *edit) { // Attempt to overwrite text so that they do not linger around in memory edit->setText(QString(" ").repeated(edit->text().size())); edit->clear(); } void AskPassphraseDialog::secureClearPassFields() { SecureClearQLineEdit(ui->passEdit1); SecureClearQLineEdit(ui->passEdit2); SecureClearQLineEdit(ui->passEdit3); } diff --git a/src/qt/askpassphrasedialog.h b/src/qt/askpassphrasedialog.h index 29bf2589f..0012053c8 100644 --- a/src/qt/askpassphrasedialog.h +++ b/src/qt/askpassphrasedialog.h @@ -1,53 +1,57 @@ // Copyright (c) 2011-2015 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_QT_ASKPASSPHRASEDIALOG_H #define BITCOIN_QT_ASKPASSPHRASEDIALOG_H #include +#include + class WalletModel; namespace Ui { class AskPassphraseDialog; } /** Multifunctional dialog to ask for passphrases. Used for encryption, * unlocking, and changing the passphrase. */ class AskPassphraseDialog : public QDialog { Q_OBJECT public: enum Mode { Encrypt, /**< Ask passphrase twice and encrypt */ Unlock, /**< Ask passphrase and unlock */ ChangePass, /**< Ask old passphrase + new passphrase twice */ Decrypt /**< Ask passphrase and decrypt wallet */ }; - explicit AskPassphraseDialog(Mode mode, QWidget *parent); + explicit AskPassphraseDialog(Mode mode, QWidget *parent, + SecureString *passphrase_out = nullptr); ~AskPassphraseDialog(); void accept() override; void setModel(WalletModel *model); private: Ui::AskPassphraseDialog *ui; Mode mode; WalletModel *model; bool fCapsLock; + SecureString *m_passphrase_out; private Q_SLOTS: void textChanged(); void secureClearPassFields(); void toggleShowPassword(bool); protected: bool event(QEvent *event) override; bool eventFilter(QObject *object, QEvent *event) override; }; #endif // BITCOIN_QT_ASKPASSPHRASEDIALOG_H