WizardController: abort saving the wallet if setPassword fails
What changed, and why it matters
This commit fixes a flaw in the Monero GUI wallet creation and restore wizard. Previously, when restoring or creating a wallet from keys, the temporary wallet was created with a blank password. If setting the user's chosen password later failed, the wizard would still save the wallet—leaving it protected by the blank password instead of the intended one. The change ensures the temporary wallet gets a random password and that the wizard aborts saving if the real password cannot be set.
Treat this as a security fix and include it in the next release. Users who created or restored wallets through the wizard in affected versions should be advised to verify that their wallets are protected by their intended password and, if uncertain, to create a new wallet with a confirmed password and transfer funds.
Security signals we found
Hardcoded empty password in wallet creation path removed
Return value of setPassword() now checked before persisting wallet
Failure to set password now aborts storeAsync() and surfaces an error
Temporary restored wallet now initialized with a random password
Evidence from the diff
The patch modifies WalletManager::recoveryWallet and createWalletFromKeys to accept a password parameter instead of hardcoding an empty string. WizardController.qml now passes oshelper.randomPassword() for the temporary wallet and checks the return value of setPassword(). If setPassword() fails, it shows an error, re-enables the Next button, and returns before calling storeAsync(). This prevents a wallet from being persisted with no password when password assignment fails.
Changed components
src/libwalletqt/WalletManager.cppsrc/libwalletqt/WalletManager.hwizard/WizardController.qmlMonero GUI wallet restore/create 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.