You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

askpassphrasedialog.cpp 10KB

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