Skip to content

Commit 1a461a0

Browse files
Merge dashpay#7127: feat: tidy-up mnemonic dialog by removing useless code and reducing exposure of sensitive data
b847bae fix: restore eager clearing of parsed words and improve secure memory handling (UdjinM6) b8b0c1c fmt: fix formatting for mnemonicverificatinodialog (Konstantin Akimov) 1edec05 fix: add mnemonicverificationdialog to non-backported list (Konstantin Akimov) f5d8b74 fix: avoid extra allocation of secured data in temporary std::string for mnemonic (Konstantin Akimov) c7e3b46 refactor: SecureString already does zeroeing, remove duplicated code (Konstantin Akimov) Pull request description: ## Issue being fixed or feature implemented Firstly, current implementation of mnemonic dialog has zeroing of SecureString's objects with std::fill which is useless, and most likely even removed by optimizing compiler. For reference, `SecureString`'s implementation of it, see [src/support/cleanse.cpp](https://github.com/dashpay/dash/blob/develop/src/support/cleanse.cpp) for details: void deallocate(T* p, std::size_t n) { if (p != nullptr) { memory_cleanse(p, sizeof(T) * n); // <- safe memory cleaning } LockedPoolManager::Instance().free(p); } Secondly, current implementation causes creating extra temporary object with sensitive data: QString mnemonicStr = QString::fromStdString(std::string(m_mnemonic.begin(), m_mnemonic.end())); This std::string object maybe omitted, see PR ## What was done? This PR tidy-up a bit mnemonic dialog by fixing these issues and some minor improvements for formatting. Though, using `memory_cleanse` should be considered to use for QStrings. This PR conflicts to dashpay#7126 because the same function is changed; I will prefer dashpay#7126 to be merged first because 7126 is meant to be backported. ## How Has This Been Tested? Tested as an extra changes to dashpay#7126 locally in the same branch, splitted to 2 PR after that. ## Breaking Changes N/A ## Checklist: - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [ ] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only) ACKs for top commit: UdjinM6: utACK b847bae Tree-SHA512: c843a39b49f76c6647d004e1044fd4d24ecc4a82ccc311c50d4a88989bfcd62ac1cec09079c3ac40e5efd9e3790115625e65eedb10fc03bfd6475272cb0df72e
2 parents 3124895 + b847bae commit 1a461a0

File tree

3 files changed

+16
-41
lines changed

3 files changed

+16
-41
lines changed

src/qt/mnemonicverificationdialog.cpp

Lines changed: 15 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ MnemonicVerificationDialog::MnemonicVerificationDialog(const SecureString& mnemo
7878

7979
// Connections
8080
connect(ui->showMnemonicButton, &QPushButton::clicked, this, &MnemonicVerificationDialog::onShowMnemonicClicked);
81-
connect(ui->hideMnemonicButton, &QPushButton::clicked, this, &MnemonicVerificationDialog::onHideMnemonicClicked);
81+
connect(ui->hideMnemonicButton, &QPushButton::clicked, this, &MnemonicVerificationDialog::onHideMnemonicClicked);
8282

8383
if (!m_view_only) {
8484
connect(ui->writtenDownCheckbox, &QCheckBox::toggled, this, [this](bool checked) {
@@ -96,8 +96,6 @@ MnemonicVerificationDialog::MnemonicVerificationDialog(const SecureString& mnemo
9696

9797
MnemonicVerificationDialog::~MnemonicVerificationDialog()
9898
{
99-
clearWordsSecurely();
100-
clearMnemonic();
10199
delete ui;
102100
}
103101

@@ -278,16 +276,15 @@ void MnemonicVerificationDialog::onHideMnemonicClicked()
278276
ui->hideMnemonicButton->hide();
279277
ui->showMnemonicButton->show();
280278
m_mnemonic_revealed = false;
281-
// Clear words from non-secure memory immediately when hiding
282-
clearWordsSecurely();
279+
// Clear parsed words from memory immediately when hiding
280+
m_words.clear();
283281
}
284282

285283
void MnemonicVerificationDialog::reject()
286284
{
287-
// Clear words when going back to step 1 (unless mnemonic is revealed)
288-
if (!m_mnemonic_revealed) {
289-
clearWordsSecurely();
290-
}
285+
// Eagerly clear parsed words to minimize exposure time in memory.
286+
// They will be re-parsed on demand via parseWords() if needed.
287+
m_words.clear();
291288
// close dialog for step-1; return back to step-1 for step-2
292289
if (ui->stackedWidget->currentIndex() == 0) {
293290
QDialog::reject();
@@ -310,8 +307,8 @@ bool MnemonicVerificationDialog::validateWord(const QString& word, int position)
310307
return false;
311308
}
312309
// Convert SecureString to QString temporarily for comparison
313-
QString secureWord = QString::fromStdString(std::string(words[position - 1].begin(), words[position - 1].end()));
314-
bool result = word == secureWord.toLower();
310+
QString secureWord{QString::fromUtf8(words[position - 1].data(), words[position - 1].size()).toLower()};
311+
const bool result{word == secureWord};
315312
// Clear temporary QString immediately
316313
secureWord.fill(QChar(0));
317314
secureWord.clear();
@@ -361,12 +358,6 @@ void MnemonicVerificationDialog::accept()
361358
QDialog::accept();
362359
}
363360

364-
void MnemonicVerificationDialog::clearMnemonic()
365-
{
366-
clearWordsSecurely();
367-
m_mnemonic.assign(m_mnemonic.size(), 0);
368-
}
369-
370361
std::vector<SecureString> MnemonicVerificationDialog::parseWords()
371362
{
372363
// If words are already parsed, reuse them (for step 2 validation or step 1 display)
@@ -375,19 +366,18 @@ std::vector<SecureString> MnemonicVerificationDialog::parseWords()
375366
}
376367

377368
// Parse words from secure mnemonic string
378-
QString mnemonicStr = QString::fromStdString(std::string(m_mnemonic.begin(), m_mnemonic.end()));
369+
QString mnemonicStr{QString::fromUtf8(m_mnemonic.data(), m_mnemonic.size())};
379370
QStringList wordList = mnemonicStr.split(QRegularExpression("\\s+"), Qt::SkipEmptyParts);
380371

381372
// Convert to SecureString vector for secure storage
382373
m_words.clear();
383374
m_words.reserve(wordList.size());
384375
for (const QString& word : wordList) {
385-
std::string wordStd = word.toStdString();
376+
QByteArray utf8 = word.toUtf8();
386377
SecureString secureWord;
387-
secureWord.assign(std::string_view{wordStd});
388-
m_words.push_back(secureWord);
389-
// Clear temporary std::string
390-
wordStd.assign(wordStd.size(), 0);
378+
secureWord.assign(utf8.constData(), utf8.size());
379+
m_words.push_back(std::move(secureWord));
380+
utf8.fill(0);
391381
}
392382

393383
// Clear the temporary QString immediately after parsing
@@ -399,23 +389,12 @@ std::vector<SecureString> MnemonicVerificationDialog::parseWords()
399389
return m_words;
400390
}
401391

402-
void MnemonicVerificationDialog::clearWordsSecurely()
403-
{
404-
// Securely clear each word string by overwriting before clearing
405-
for (SecureString& word : m_words) {
406-
// Overwrite with zeros before clearing
407-
word.assign(word.size(), 0);
408-
word.clear();
409-
}
410-
m_words.clear();
411-
}
412-
413392
int MnemonicVerificationDialog::getWordCount() const
414393
{
415394
// Count words without parsing them into vector
416395
// This avoids storing words in non-secure memory unnecessarily
417396
if (m_words.empty()) {
418-
QString mnemonicStr = QString::fromStdString(std::string(m_mnemonic.begin(), m_mnemonic.end()));
397+
QString mnemonicStr{QString::fromUtf8(m_mnemonic.data(), m_mnemonic.size())};
419398
QStringList words = mnemonicStr.split(QRegularExpression("\\s+"), Qt::SkipEmptyParts);
420399
int count = words.size();
421400
// Clear immediately
@@ -480,7 +459,7 @@ void MnemonicVerificationDialog::buildMnemonicGrid(bool reveal)
480459
for (int c = 0; c < columns; ++c) {
481460
int idx = r * columns + c; if (idx >= n) break;
482461
// Convert SecureString to QString temporarily for display
483-
QString wordStr = QString::fromStdString(std::string(words[idx].begin(), words[idx].end()));
462+
QString wordStr{QString::fromUtf8(words[idx].data(), words[idx].size())};
484463
const QString text = QString("%1. %2").arg(idx + 1, 2).arg(wordStr);
485464
QLabel* lbl = new QLabel(text);
486465
lbl->setTextInteractionFlags(Qt::TextSelectableByMouse);
@@ -494,5 +473,3 @@ void MnemonicVerificationDialog::buildMnemonicGrid(bool reveal)
494473

495474
m_gridLayout->setRowMinimumHeight(rows, 12);
496475
}
497-
498-

src/qt/mnemonicverificationdialog.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,8 @@ private Q_SLOTS:
4141
void generateRandomPositions();
4242
void updateWordValidation();
4343
bool validateWord(const QString& word, int position);
44-
void clearMnemonic();
4544
void buildMnemonicGrid(bool reveal);
4645
std::vector<SecureString> parseWords();
47-
void clearWordsSecurely();
4846
int getWordCount() const;
4947

5048
Ui::MnemonicVerificationDialog *ui;
@@ -59,4 +57,3 @@ private Q_SLOTS:
5957
};
6058

6159
#endif // BITCOIN_QT_MNEMONICVERIFICATIONDIALOG_H
62-

test/util/data/non-backported.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ src/qt/governancelist.*
3333
src/qt/guiutil_font.*
3434
src/qt/masternodelist.*
3535
src/qt/masternodemodel.*
36+
src/qt/mnemonicverificationdialog.*
3637
src/qt/proposalmodel.*
3738
src/qt/proposalwizard.*
3839
src/rpc/coinjoin.cpp

0 commit comments

Comments
 (0)