diff --git a/lib/core/backup_service.dart b/lib/core/backup_service.dart index 385598060..cb36072fe 100644 --- a/lib/core/backup_service.dart +++ b/lib/core/backup_service.dart @@ -251,6 +251,22 @@ class $BackupService { await importWalletKeychainInfo(info); }); + for (var key in (keychainJSON['_all'] as Map).keys) { + try { + if (!key.startsWith('MONERO_WALLET_')) continue; + final decodedPassword = decodeWalletPassword(password: keychainJSON['_all'][key].toString()); + final walletName = key.split('_WALLET_')[1]; + final walletType = key.split('_WALLET_')[0].toLowerCase(); + await importWalletKeychainInfo({ + 'name': walletName, + 'type': "WalletType.$walletType", + 'password': decodedPassword, + }); + } catch (e) { + printV('Error importing wallet ($key) password: $e'); + } + } + keychainDumpFile.deleteSync(); } diff --git a/lib/di.dart b/lib/di.dart index 60a0f240d..067fd7c4f 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -33,12 +33,14 @@ import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart'; import 'package:cake_wallet/haven/cw_haven.dart'; import 'package:cake_wallet/src/screens/dev/monero_background_sync.dart'; import 'package:cake_wallet/src/screens/dev/moneroc_call_profiler.dart'; +import 'package:cake_wallet/src/screens/dev/secure_preferences_page.dart'; import 'package:cake_wallet/src/screens/dev/shared_preferences_page.dart'; import 'package:cake_wallet/src/screens/settings/background_sync_page.dart'; import 'package:cake_wallet/src/screens/wallet_connect/services/bottom_sheet_service.dart'; import 'package:cake_wallet/src/screens/wallet_connect/services/key_service/wallet_connect_key_service.dart'; import 'package:cake_wallet/src/screens/wallet_connect/services/walletkit_service.dart'; import 'package:cake_wallet/view_model/dev/monero_background_sync.dart'; +import 'package:cake_wallet/view_model/dev/secure_preferences.dart'; import 'package:cake_wallet/view_model/dev/shared_preferences.dart'; import 'package:cake_wallet/view_model/link_view_model.dart'; import 'package:cake_wallet/tron/tron.dart'; @@ -920,6 +922,8 @@ Future setup({ getIt.registerFactory(() => DevSharedPreferences()); + getIt.registerFactory(() => DevSecurePreferences()); + getIt.registerFactoryParam((bool isWalletCreated, _) => WalletSeedPage(getIt.get(), isNewWalletCreated: isWalletCreated)); @@ -1463,6 +1467,8 @@ Future setup({ getIt.registerFactory(() => DevMoneroCallProfilerPage()); getIt.registerFactory(() => DevSharedPreferencesPage(getIt.get())); + + getIt.registerFactory(() => DevSecurePreferencesPage(getIt.get())); getIt.registerFactory(() => BackgroundSyncLogsViewModel()); diff --git a/lib/router.dart b/lib/router.dart index dfd163122..322c79904 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -36,6 +36,7 @@ import 'package:cake_wallet/src/screens/dashboard/pages/transactions_page.dart'; import 'package:cake_wallet/src/screens/dashboard/sign_page.dart'; import 'package:cake_wallet/src/screens/dev/monero_background_sync.dart'; import 'package:cake_wallet/src/screens/dev/moneroc_call_profiler.dart'; +import 'package:cake_wallet/src/screens/dev/secure_preferences_page.dart'; import 'package:cake_wallet/src/screens/dev/shared_preferences_page.dart'; import 'package:cake_wallet/src/screens/dev/background_sync_logs_page.dart'; import 'package:cake_wallet/src/screens/disclaimer/disclaimer_page.dart'; @@ -853,6 +854,11 @@ Route createRoute(RouteSettings settings) { builder: (_) => getIt.get(), ); + case Routes.devSecurePreferences: + return MaterialPageRoute( + builder: (_) => getIt.get(), + ); + default: return MaterialPageRoute( builder: (_) => Scaffold( diff --git a/lib/routes.dart b/lib/routes.dart index a9a637c75..73fe8f9b5 100644 --- a/lib/routes.dart +++ b/lib/routes.dart @@ -115,6 +115,7 @@ class Routes { static const devMoneroBackgroundSync = '/dev/monero_background_sync'; static const devMoneroCallProfiler = '/dev/monero_call_profiler'; static const devSharedPreferences = '/dev/shared_preferences'; + static const devSecurePreferences = '/dev/secure_preferences'; static const devBackgroundSyncLogs = '/dev/background_sync_logs'; static const signPage = '/sign_page'; diff --git a/lib/src/screens/dev/secure_preferences_page.dart b/lib/src/screens/dev/secure_preferences_page.dart new file mode 100644 index 000000000..23ab0c9df --- /dev/null +++ b/lib/src/screens/dev/secure_preferences_page.dart @@ -0,0 +1,63 @@ +import 'package:cake_wallet/src/screens/base_page.dart'; +import 'package:cake_wallet/view_model/dev/secure_preferences.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; + +class DevSecurePreferencesPage extends BasePage { + final DevSecurePreferences viewModel; + + DevSecurePreferencesPage(this.viewModel); + + @override + String? get title => "[dev] secure preferences"; + + @override + Widget body(BuildContext context) { + return Observer( + builder: (_) { + if (viewModel.values.isEmpty) { + return Center(child: Text("No secure preferences found")); + } + final keys = viewModel.keys; + Map values = {}; + for (final key in keys) { + values[key] = viewModel.get(key); + } + Map types = {}; + for (final key in keys) { + types[key] = viewModel.getPreferenceType(key); + } + return ListView.builder( + itemCount: keys.length, + itemBuilder: (context, index) { + final key = keys[index]; + final type = types[key]!; + return ListTile( + onTap: () { + Clipboard.setData(ClipboardData(text: key + ": " + values[key].toString())); + }, + title: switch (type) { + PreferenceType.bool => Text(key, style: TextStyle(color: Colors.blue)), + PreferenceType.int => Text(key, style: TextStyle(color: Colors.green)), + PreferenceType.double => Text(key, style: TextStyle(color: Colors.yellow)), + PreferenceType.listString => Text(key, style: TextStyle(color: Colors.purple)), + PreferenceType.string => Text(key), + PreferenceType.unknown => Text(key), + }, + subtitle: switch (type) { + PreferenceType.bool => Text("bool: ${values[key]}"), + PreferenceType.int => Text("int: ${values[key]}"), + PreferenceType.double => Text("double: ${values[key]}"), + PreferenceType.listString => values[key].isEmpty as bool ? Text("listString: []") : Text("listString:\n- ${values[key].join("\n- ")}"), + PreferenceType.string => Text("string: ${values[key]}"), + PreferenceType.unknown => Text("UNKNOWN(${values[key].runtimeType}): ${values[key]}"), + }, + ); + }, + ); + }, + ); + } + +} \ No newline at end of file diff --git a/lib/src/screens/settings/other_settings_page.dart b/lib/src/screens/settings/other_settings_page.dart index 730699286..a52f9ae06 100644 --- a/lib/src/screens/settings/other_settings_page.dart +++ b/lib/src/screens/settings/other_settings_page.dart @@ -81,6 +81,12 @@ class OtherSettingsPage extends BasePage { handler: (BuildContext context) => Navigator.of(context).pushNamed(Routes.devSharedPreferences), ), + if (FeatureFlag.hasDevOptions) + SettingsCellWithArrow( + title: '[dev] secure storage preferences', + handler: (BuildContext context) => + Navigator.of(context).pushNamed(Routes.devSecurePreferences), + ), if (FeatureFlag.hasDevOptions) SettingsCellWithArrow( title: '[dev] background sync logs', diff --git a/lib/view_model/dev/secure_preferences.dart b/lib/view_model/dev/secure_preferences.dart new file mode 100644 index 000000000..379022f89 --- /dev/null +++ b/lib/view_model/dev/secure_preferences.dart @@ -0,0 +1,75 @@ +import 'package:cake_wallet/core/secure_storage.dart'; +import 'package:cake_wallet/entities/encrypt.dart'; +import 'package:mobx/mobx.dart'; + +part 'secure_preferences.g.dart'; + +class DevSecurePreferences = DevSecurePreferencesBase with _$DevSecurePreferences; + +enum PreferenceType { + unknown, + string, + int, + double, + bool, + listString +} + +abstract class DevSecurePreferencesBase with Store { + DevSecurePreferencesBase() { + secureStorageShared.readAll().then((value) { + values = value; + }); + } + + @observable + Map values = {}; + + @computed + List get keys => values.keys.toList()..sort(); + + @action + Future delete(String key) async { + + } + + dynamic get(String key) { + if (!values.containsKey(key)) { + return null; + } + if (!key.startsWith("MONERO_WALLET_")) return values[key]!; + try { + final decodedPassword = decodeWalletPassword(password: values[key]!); + return values[key]! + "\n\nDecoded: $decodedPassword"; + } catch (e) { + return values[key]! +"\n$e"; + } + } + + Future set(String key, PreferenceType type, dynamic value) async { + + } + + PreferenceType getPreferenceType(String key) { + if (!values.containsKey(key)) { + return PreferenceType.unknown; + } + final value = values[key]; + if (value is String) { + return PreferenceType.string; + } + if (value is bool) { + return PreferenceType.bool; + } + if (value is int) { + return PreferenceType.int; + } + if (value is double) { + return PreferenceType.double; + } + if (value is List) { + return PreferenceType.listString; + } + return PreferenceType.unknown; + } +}