Remove SecretStore class for Backup viewmodels (fix for backup password not restoring) (#2723)
What changed, and why it matters
This commit fixes a bug where the backup password was not being restored or applied correctly in Cake Wallet. It removes a separate in-memory 'SecretStore' class and instead has the backup screen and backup-password editing screen share one view-model directly. The change also adds a guard so you cannot export a backup when no password is set. There is no direct evidence in the commit of a security vulnerability being exploited; it reads as a reliability/bug-fix, though a missing backup password could weaken backup security.
Treat as a functional bug fix with minor security hygiene improvement. Review whether any previously created backups were exported with an empty or unintended password due to the state-sync bug, and consider adding an explicit user-facing warning when attempting backup export without a password. No urgent security patch is indicated by the diff alone.
Security signals we found
Removal of intermediate SecretStore that cached backup password in an observable map
Commented FIXME in removed SecretStore.load() suggests prior uncertainty about empty/default password handling
Added empty-password guard before backup export
Backup password state now shared reactively between viewmodels instead of being duplicated
No explicit security advisory, CVE, or researcher attribution in commit
Evidence from the diff
The patch removes SecretStore (an ObservableMap-backed MobX store loaded from SecureStorage) from the backup flow. BackupViewModel and EditBackupPasswordViewModel now reactively share backupPassword through direct MobX reactions and a shared EditBackupPasswordViewModel singleton. SecretStore’s load() had a commented ‘FIX-ME: backupPassword ?? ???’ indicating uncertainty about default-empty handling. The backup_page.dart now returns early from onExportBackup if backupPassword.isEmpty. The change reduces indirection and fixes state-synchronization between the password editor and backup exporter.
Changed components
lib/di.dartlib/src/screens/backup/backup_page.dartlib/src/screens/backup/edit_backup_password_page.dartlib/store/secret_store.dartlib/view_model/backup_view_model.dartlib/view_model/edit_backup_password_view_model.dartInspect captured patch +32 / −50
diff --git a/lib/di.dart b/lib/di.dart
index 800399b6..276a470e 100644
--- a/lib/di.dart
+++ b/lib/di.dart
@@ -224,7 +224,6 @@ import 'package:cake_wallet/store/dashboard/trade_filter_store.dart';
import 'package:cake_wallet/store/dashboard/trades_store.dart';
import 'package:cake_wallet/store/dashboard/transaction_filter_store.dart';
import 'package:cake_wallet/store/node_list_store.dart';
-import 'package:cake_wallet/store/secret_store.dart';
import 'package:cake_wallet/store/seed_settings_store.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/store/templates/exchange_template_store.dart';
@@ -417,9 +416,6 @@ Future<void> setup({
appName: "Cake Wallet"));
getIt.registerLazySingleton(() => TrezorViewModel(getIt<TrezorConnect>()));
- final secretStore = await SecretStoreBase.load(getIt.get<SecureStorage>());
-
- getIt.registerSingleton<SecretStore>(secretStore);
getIt.registerFactory<KeyService>(() => KeyService(getIt.get<SecureStorage>()));
@@ -1354,13 +1350,15 @@ Future<void> setup({
_transactionDescriptionBox,
getIt.get<KeyService>(), getIt.get<SharedPreferences>()));
- getIt.registerFactory(() => BackupViewModel(
- getIt.get<SecureStorage>(), getIt.get<SecretStore>(), getIt.get<BackupServiceV3>()));
getIt.registerFactory(() => BackupPage(getIt.get<BackupViewModel>()));
- getIt.registerFactory(
- () => EditBackupPasswordViewModel(getIt.get<SecureStorage>(), getIt.get<SecretStore>()));
+ getIt.registerSingleton<EditBackupPasswordViewModel>(
+ EditBackupPasswordViewModel(getIt.get<SecureStorage>()),
+ );
+
+ getIt.registerFactory(() => BackupViewModel(
+ getIt.get<SecureStorage>(),getIt.get<BackupServiceV3>(), getIt.get<EditBackupPasswordViewModel>()));
getIt.registerFactory(() => EditBackupPasswordPage(getIt.get<EditBackupPasswordViewModel>()));
diff --git a/lib/src/screens/backup/backup_page.dart b/lib/src/screens/backup/backup_page.dart
index b4889e7b..b682f747 100644
--- a/lib/src/screens/backup/backup_page.dart
+++ b/lib/src/screens/backup/backup_page.dart
@@ -105,6 +105,7 @@ class BackupPage extends BasePage {
}
void onExportBackup(BuildContext context) {
+ if(backupViewModelBase.backupPassword.isEmpty) return;
showPopUp<void>(
context: context,
builder: (dialogContext) {
diff --git a/lib/src/screens/backup/edit_backup_password_page.dart b/lib/src/screens/backup/edit_backup_password_page.dart
index 30ce91a2..50d720b6 100644
--- a/lib/src/screens/backup/edit_backup_password_page.dart
+++ b/lib/src/screens/backup/edit_backup_password_page.dart
@@ -7,6 +7,7 @@ import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/view_model/edit_backup_password_view_model.dart';
+import 'package:mobx/mobx.dart';
class EditBackupPasswordPage extends BasePage {
EditBackupPasswordPage(this.editBackupPasswordViewModel)
@@ -14,6 +15,12 @@ class EditBackupPasswordPage extends BasePage {
textEditingController.text = editBackupPasswordViewModel.backupPassword;
textEditingController
.addListener(() => editBackupPasswordViewModel.backupPassword = textEditingController.text);
+
+ reaction((_) => editBackupPasswordViewModel.backupPassword, (_) {
+ if (textEditingController.text != editBackupPasswordViewModel.backupPassword) {
+ textEditingController.text = editBackupPasswordViewModel.backupPassword;
+ }
+ });
}
final EditBackupPasswordViewModel editBackupPasswordViewModel;
diff --git a/lib/store/secret_store.dart b/lib/store/secret_store.dart
deleted file mode 100644
index aa185ae3..00000000
--- a/lib/store/secret_store.dart
+++ /dev/null
@@ -1,28 +0,0 @@
-import 'package:cake_wallet/core/secure_storage.dart';
-import 'package:cake_wallet/entities/secret_store_key.dart';
-import 'package:mobx/mobx.dart';
-
-part 'secret_store.g.dart';
-
-class SecretStore = SecretStoreBase with _$SecretStore;
-
-abstract class SecretStoreBase with Store {
- static Future<SecretStore> load(SecureStorage storage) async {
- final secretStore = SecretStore();
- final backupPasswordKey = generateStoreKeyFor(key: SecretStoreKey.backupPassword);
- final backupPassword = await storage.read(key: backupPasswordKey);
- // FIX-ME: backupPassword ?? '' ???
- secretStore.write(key: backupPasswordKey, value: backupPassword ?? '');
-
- return secretStore;
- }
-
- SecretStoreBase() : values = ObservableMap<String, String>();
-
- ObservableMap values;
-
- String read(String key) => values[key] as String;
-
- String write({required String key, required String value}) =>
- values[key] = value;
-}
diff --git a/lib/view_model/backup_view_model.dart b/lib/view_model/backup_view_model.dart
index f8900a24..d9adb1b6 100644
--- a/lib/view_model/backup_view_model.dart
+++ b/lib/view_model/backup_view_model.dart
@@ -4,7 +4,7 @@ import 'package:cake_wallet/core/backup_service_v3.dart';
import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/core/secure_storage.dart';
import 'package:cake_wallet/entities/secret_store_key.dart';
-import 'package:cake_wallet/store/secret_store.dart';
+import 'package:cake_wallet/view_model/edit_backup_password_view_model.dart';
import 'package:cw_core/root_dir.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:flutter/foundation.dart';
@@ -25,21 +25,28 @@ class BackupExportFile {
class BackupViewModel = BackupViewModelBase with _$BackupViewModel;
abstract class BackupViewModelBase with Store {
- BackupViewModelBase(this.secureStorage, this.secretStore, this.backupService)
+ BackupViewModelBase(this.secureStorage, this.backupService, this.editBackupPasswordViewModel)
: isBackupPasswordVisible = false,
backupPassword = '',
state = InitialExecutionState() {
- final key = generateStoreKeyFor(key: SecretStoreKey.backupPassword);
- secretStore.values.observe((change) {
- if (change.key == key) {
- backupPassword = secretStore.read(key);
+ init();
+
+ reaction((_) => editBackupPasswordViewModel.backupPassword, (value) {
+ if(value != backupPassword) {
+ backupPassword = value;
+ }
+ });
+
+ reaction((_)=>backupPassword, (value){
+ if(value != editBackupPasswordViewModel.backupPassword) {
+ editBackupPasswordViewModel.backupPassword = value;
}
- }, fireImmediately: true);
+ });
}
final SecureStorage secureStorage;
- final SecretStore secretStore;
final BackupServiceV3 backupService;
+ final EditBackupPasswordViewModel editBackupPasswordViewModel;
@observable
ExecutionState state;
diff --git a/lib/view_model/edit_backup_password_view_model.dart b/lib/view_model/edit_backup_password_view_model.dart
index 64c6c166..2f775897 100644
--- a/lib/view_model/edit_backup_password_view_model.dart
+++ b/lib/view_model/edit_backup_password_view_model.dart
@@ -1,7 +1,6 @@
import 'package:cake_wallet/core/secure_storage.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/entities/secret_store_key.dart';
-import 'package:cake_wallet/store/secret_store.dart';
part 'edit_backup_password_view_model.g.dart';
@@ -9,12 +8,11 @@ class EditBackupPasswordViewModel = EditBackupPasswordViewModelBase
with _$EditBackupPasswordViewModel;
abstract class EditBackupPasswordViewModelBase with Store {
- EditBackupPasswordViewModelBase(this.secureStorage, this.secretStore)
- : backupPassword = secretStore.read(generateStoreKeyFor(key: SecretStoreKey.backupPassword)),
- _originalPassword = '';
+ EditBackupPasswordViewModelBase(this.secureStorage,)
+ : backupPassword = "",
+ _originalPassword = ''{init();}
final SecureStorage secureStorage;
- final SecretStore secretStore;
@observable
String backupPassword;
@@ -38,6 +36,5 @@ abstract class EditBackupPasswordViewModelBase with Store {
Future<void> save() async {
final key = generateStoreKeyFor(key: SecretStoreKey.backupPassword);
await secureStorage.write(key: key, value: backupPassword);
- secretStore.write(key: key, value: backupPassword);
}
}
Why this scored 30/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.