What changed, and why it matters
This change fixes a bug in the Monero wallet setup wizard. Previously, when restoring or creating a wallet from seed/keys, the wallet was first saved with a blank or temporary password before the user's chosen password was applied. If applying the real password failed, the wallet file could remain on disk with a weak or empty password. The patch makes the wallet creation functions accept the intended password directly, and aborts the save if the password cannot be set.
Treat this as a security fix and include it in release notes. Users who created or restored wallets with affected versions should verify that their wallet files are protected by their intended password and consider rotating passwords or re-creating wallet files if there is any concern that an empty-password wallet file was persisted.
Security signals we found
Wallet file created with empty/blank password before user password is applied
Failure to set user password does not prevent wallet file from being saved
Sensitive on-disk artifact (wallet file) may be protected by weaker credentials than intended
Patch adds explicit error handling and abort path for password-setting failure
Evidence from the diff
The patch modifies WalletManager::recoveryWallet and WalletManager::createWalletFromKeys to accept a password parameter and pass it to the underlying Monero wallet implementation, instead of hardcoding an empty string. In WizardController.qml, it now calls setPassword() before storeAsync() and aborts the save (returning early and re-enabling UI buttons) if setPassword returns false. Previously, the wallet was created with an empty password, stored, and only then setPassword was attempted, leaving a recoverable wallet file behind on failure.
Changed components
src/libwalletqt/WalletManager.cppsrc/libwalletqt/WalletManager.hwizard/WizardController.qmlMonero GUI wallet creation/restoration wizardInspect captured patch +14 / −9
### src/libwalletqt/WalletManager.cpp
@@ -142,19 +142,19 @@ void WalletManager::openWalletAsync(const QString &path, const QString &password
}
-Wallet *WalletManager::recoveryWallet(const QString &path, const QString &seed, const QString &seed_offset, NetworkType::Type nettype, quint64 restoreHeight, quint64 kdfRounds)
+Wallet *WalletManager::recoveryWallet(const QString &path, const QString &password, const QString &seed, const QString &seed_offset, NetworkType::Type nettype, quint64 restoreHeight, quint64 kdfRounds)
{
QMutexLocker locker(&m_mutex);
if (m_currentWallet) {
qDebug() << "Closing open m_currentWallet" << m_currentWallet;
delete m_currentWallet;
}
- Monero::Wallet * w = m_pimpl->recoveryWallet(path.toStdString(), "", seed.toStdString(), static_cast<Monero::NetworkType>(nettype), restoreHeight, kdfRounds, seed_offset.toStdString());
+ Monero::Wallet * w = m_pimpl->recoveryWallet(path.toStdString(), password.toStdString(), seed.toStdString(), static_cast<Monero::NetworkType>(nettype), restoreHeight, kdfRounds, seed_offset.toStdString());
m_currentWallet = new Wallet(w);
return m_currentWallet;
}
-Wallet *WalletManager::createWalletFromKeys(const QString &path, const QString &language, NetworkType::Type nettype,
+Wallet *WalletManager::createWalletFromKeys(const QString &path, const QString &password, const QString &language, NetworkType::Type nettype,
const QString &address, const QString &viewkey, const QString &spendkey,
quint64 restoreHeight, quint64 kdfRounds)
{
@@ -164,7 +164,7 @@ Wallet *WalletManager::createWalletFromKeys(const QString &path, const QString &
delete m_currentWallet;
m_currentWallet = NULL;
}
- Monero::Wallet * w = m_pimpl->createWalletFromKeys(path.toStdString(), "", language.toStdString(), static_cast<Monero::NetworkType>(nettype), restoreHeight,
+ Monero::Wallet * w = m_pimpl->createWalletFromKeys(path.toStdString(), password.toStdString(), language.toStdString(), static_cast<Monero::NetworkType>(nettype), restoreHeight,
address.toStdString(), viewkey.toStdString(), spendkey.toStdString(), kdfRounds);
m_currentWallet = new Wallet(w);
return m_currentWallet;
### src/libwalletqt/WalletManager.h
@@ -85,11 +85,11 @@ class WalletManager : public QObject, public PassprasePrompter
*/
Q_INVOKABLE void openWalletAsync(const QString &path, const QString &password, NetworkType::Type nettype = NetworkType::MAINNET, quint64 kdfRounds = 1);
- // wizard: recoveryWallet path; hint: internally it recorvers wallet and set password = ""
- Q_INVOKABLE Wallet * recoveryWallet(const QString &path, const QString &seed, const QString &seed_offset,
+ Q_INVOKABLE Wallet * recoveryWallet(const QString &path, const QString &password, const QString &seed, const QString &seed_offset,
NetworkType::Type nettype = NetworkType::MAINNET, quint64 restoreHeight = 0, quint64 kdfRounds = 1);
Q_INVOKABLE Wallet * createWalletFromKeys(const QString &path,
+ const QString &password,
const QString &language,
NetworkType::Type nettype,
const QString &address,
### wizard/WizardController.qml
@@ -388,7 +388,12 @@ Rectangle {
new_wallet_filename = appWindow.accountsDir + new_wallet_filename;
}
console.log("saving new wallet to", new_wallet_filename);
- wizardController.m_wallet.setPassword(wizardController.walletOptionsPassword);
+ if (!wizardController.m_wallet.setPassword(wizardController.walletOptionsPassword)) {
+ appWindow.showStatusMessage(qsTr("Failed to set the wallet password"), 3);
+ wizardStateView.wizardRestoreWallet4View.wizardNav.btnNext.enabled = true;
+ wizardStateView.wizardCreateWallet4View.wizardNav.btnNext.enabled = true;
+ return;
+ }
wizardController.m_wallet.storeAsync(handler, new_wallet_filename);
}
@@ -404,9 +409,9 @@ Rectangle {
var wallet = ''
// From seed or keys
if(wizardController.walletRestoreMode === 'seed')
- wallet = walletManager.recoveryWallet('', wizardController.walletOptionsSeed, wizardController.walletOptionsSeedOffset, nettype, restoreHeight, kdfRounds);
+ wallet = walletManager.recoveryWallet('', oshelper.randomPassword(), wizardController.walletOptionsSeed, wizardController.walletOptionsSeedOffset, nettype, restoreHeight, kdfRounds);
else
- wallet = walletManager.createWalletFromKeys('', persistentSettings.language_wallet, nettype,
+ wallet = walletManager.createWalletFromKeys('', oshelper.randomPassword(), persistentSettings.language_wallet, nettype,
wizardController.walletOptionsRecoverAddress, wizardController.walletOptionsRecoverViewkey,
wizardController.walletOptionsRecoverSpendkey, restoreHeight, kdfRounds)
Why this scored 59/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.