Skip to content

Commit 9531028

Browse files
security: Store mnemonic words in secure memory
Replace QStringList with std::vector<SecureString> for m_words member to ensure mnemonic seed words are stored in secure memory that: - Locks memory to prevent paging to disk - Automatically zeros memory on deallocation - Uses secure allocators throughout the lifetime of sensitive data This addresses a security concern where mnemonic words were stored in non-secure memory (QStringList) which does not guarantee secure memory allocation or zeroing on deallocation.
1 parent 5024589 commit 9531028

File tree

2 files changed

+43
-15
lines changed

2 files changed

+43
-15
lines changed

src/qt/mnemonicverificationdialog.cpp

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -309,11 +309,18 @@ bool MnemonicVerificationDialog::validateWord(const QString& word, int position)
309309
// Parse words on-demand for validation (minimizes exposure time)
310310
// Words are kept in memory during step 2 (verification) and step 1 (when revealed)
311311
// They are only cleared when explicitly hiding in step 1 or on dialog destruction
312-
QStringList words = parseWords();
313-
if (position < 1 || position > words.size()) {
312+
std::vector<SecureString> words = parseWords();
313+
if (position < 1 || position > static_cast<int>(words.size())) {
314314
return false;
315315
}
316-
return word == words[position - 1].toLower();
316+
// Convert SecureString to QString temporarily for comparison
317+
QString secureWord = QString::fromStdString(std::string(words[position - 1].begin(), words[position - 1].end()));
318+
bool result = word == secureWord.toLower();
319+
// Clear temporary QString immediately
320+
secureWord.fill(QChar(0));
321+
secureWord.clear();
322+
secureWord.squeeze();
323+
return result;
317324
}
318325

319326
void MnemonicVerificationDialog::updateWordValidation()
@@ -364,47 +371,61 @@ void MnemonicVerificationDialog::clearMnemonic()
364371
m_mnemonic.assign(m_mnemonic.size(), 0);
365372
}
366373

367-
QStringList MnemonicVerificationDialog::parseWords()
374+
std::vector<SecureString> MnemonicVerificationDialog::parseWords()
368375
{
369376
// If words are already parsed, reuse them (for step 2 validation or step 1 display)
370-
if (!m_words.isEmpty()) {
377+
if (!m_words.empty()) {
371378
return m_words;
372379
}
373380

374381
// Parse words from secure mnemonic string
375382
QString mnemonicStr = QString::fromStdString(std::string(m_mnemonic.begin(), m_mnemonic.end()));
376-
m_words = mnemonicStr.split(QRegularExpression("\\s+"), Qt::SkipEmptyParts);
383+
QStringList wordList = mnemonicStr.split(QRegularExpression("\\s+"), Qt::SkipEmptyParts);
384+
385+
// Convert to SecureString vector for secure storage
386+
m_words.clear();
387+
m_words.reserve(wordList.size());
388+
for (const QString& word : wordList) {
389+
std::string wordStd = word.toStdString();
390+
SecureString secureWord;
391+
secureWord.assign(std::string_view{wordStd});
392+
m_words.push_back(secureWord);
393+
// Clear temporary std::string
394+
wordStd.assign(wordStd.size(), 0);
395+
}
377396

378397
// Clear the temporary QString immediately after parsing
398+
mnemonicStr.fill(QChar(0));
379399
mnemonicStr.clear();
380400
mnemonicStr.squeeze(); // Release memory
401+
wordList.clear();
381402

382403
return m_words;
383404
}
384405

385406
void MnemonicVerificationDialog::clearWordsSecurely()
386407
{
387408
// Securely clear each word string by overwriting before clearing
388-
for (QString& word : m_words) {
409+
for (SecureString& word : m_words) {
389410
// Overwrite with zeros before clearing
390-
word.fill(QChar(0));
411+
word.assign(word.size(), 0);
391412
word.clear();
392-
word.squeeze(); // Release memory
393413
}
394414
m_words.clear();
395415
}
396416

397417
int MnemonicVerificationDialog::getWordCount() const
398418
{
399-
// Count words without parsing them into QStringList
419+
// Count words without parsing them into vector
400420
// This avoids storing words in non-secure memory unnecessarily
401-
if (m_words.isEmpty()) {
421+
if (m_words.empty()) {
402422
QString mnemonicStr = QString::fromStdString(std::string(m_mnemonic.begin(), m_mnemonic.end()));
403423
QStringList words = mnemonicStr.split(QRegularExpression("\\s+"), Qt::SkipEmptyParts);
404424
int count = words.size();
405425
// Clear immediately
406426
mnemonicStr.clear();
407427
mnemonicStr.squeeze();
428+
words.clear();
408429
return count;
409430
}
410431
return m_words.size();
@@ -421,7 +442,7 @@ void MnemonicVerificationDialog::buildMnemonicGrid(bool reveal)
421442
}
422443

423444
// Parse words only when revealing (when needed for display)
424-
QStringList words;
445+
std::vector<SecureString> words;
425446
if (reveal) {
426447
words = parseWords();
427448
} else {
@@ -462,10 +483,16 @@ void MnemonicVerificationDialog::buildMnemonicGrid(bool reveal)
462483
for (int r = 0; r < rows; ++r) {
463484
for (int c = 0; c < columns; ++c) {
464485
int idx = r * columns + c; if (idx >= n) break;
465-
const QString text = QString("%1. %2").arg(idx + 1, 2).arg(words[idx]);
486+
// Convert SecureString to QString temporarily for display
487+
QString wordStr = QString::fromStdString(std::string(words[idx].begin(), words[idx].end()));
488+
const QString text = QString("%1. %2").arg(idx + 1, 2).arg(wordStr);
466489
QLabel* lbl = new QLabel(text);
467490
lbl->setTextInteractionFlags(Qt::TextSelectableByMouse);
468491
m_gridLayout->addWidget(lbl, r, c);
492+
// Clear temporary QString immediately after use
493+
wordStr.fill(QChar(0));
494+
wordStr.clear();
495+
wordStr.squeeze();
469496
}
470497
}
471498

src/qt/mnemonicverificationdialog.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <QStackedWidget>
1010

1111
#include <support/allocators/secure.h>
12+
#include <vector>
1213

1314
namespace Ui {
1415
class MnemonicVerificationDialog;
@@ -41,13 +42,13 @@ private Q_SLOTS:
4142
bool validateWord(const QString& word, int position);
4243
void clearMnemonic();
4344
void buildMnemonicGrid(bool reveal);
44-
QStringList parseWords();
45+
std::vector<SecureString> parseWords();
4546
void clearWordsSecurely();
4647
int getWordCount() const;
4748

4849
Ui::MnemonicVerificationDialog *ui;
4950
SecureString m_mnemonic;
50-
QStringList m_words;
51+
std::vector<SecureString> m_words;
5152
QList<int> m_selected_positions;
5253
bool m_mnemonic_revealed;
5354
bool m_has_ever_revealed{false};

0 commit comments

Comments
 (0)