From bfb78eded98085b8f0a65f233b332490d8e4e5a7 Mon Sep 17 00:00:00 2001 From: Adegoke David <64401859+Blazebrain@users.noreply.github.com> Date: Wed, 8 May 2024 21:23:27 +0100 Subject: [PATCH] CW-599-Extract-Secure-Storage (#1353) * feat: Modify app to depend on secure storage abstraction instead of the direct package * chore: Revert command * Update configure.dart [skip ci] * Update configure.dart * Fix conflicts * clean up and fixes * minor fix --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> --- .gitignore | 1 + lib/core/auth_service.dart | 14 +-- lib/core/backup_service.dart | 19 ++- lib/core/key_service.dart | 19 ++- lib/core/secure_storage.dart | 38 ------ lib/core/wallet_creation_service.dart | 4 +- lib/di.dart | 50 ++++++-- lib/entities/default_settings_migration.dart | 15 +-- lib/entities/fs_migration.dart | 66 ++++------ lib/entities/get_encryption_key.dart | 5 +- lib/entities/preferences_key.dart | 6 +- lib/entities/secret_store_key.dart | 8 +- lib/ionia/ionia_service.dart | 4 +- lib/main.dart | 9 +- .../support_chat/support_chat_page.dart | 4 +- .../support_chat/widgets/chatwoot_widget.dart | 5 +- lib/store/secret_store.dart | 5 +- lib/store/settings_store.dart | 49 ++++---- lib/store/yat/yat_store.dart | 4 +- lib/view_model/backup_view_model.dart | 5 +- .../edit_backup_password_view_model.dart | 5 +- pubspec_base.yaml | 6 - tool/configure.dart | 117 ++++++++++++++++++ 23 files changed, 259 insertions(+), 199 deletions(-) delete mode 100644 lib/core/secure_storage.dart diff --git a/.gitignore b/.gitignore index 24b7291f8..d0952ca98 100644 --- a/.gitignore +++ b/.gitignore @@ -156,6 +156,7 @@ assets/images/app_logo.png macos/Runner/Info.plist macos/Runner/DebugProfile.entitlements macos/Runner/Release.entitlements +lib/core/secure_storage.dart macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png diff --git a/lib/core/auth_service.dart b/lib/core/auth_service.dart index 66943bb7f..791701395 100644 --- a/lib/core/auth_service.dart +++ b/lib/core/auth_service.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:io'; import 'package:cake_wallet/core/secure_storage.dart'; import 'package:cake_wallet/core/totp_request_details.dart'; @@ -7,7 +6,6 @@ import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/src/screens/auth/auth_page.dart'; import 'package:flutter/material.dart'; import 'package:mobx/mobx.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/entities/secret_store_key.dart'; @@ -35,14 +33,14 @@ class AuthService with Store { Routes.restoreOptions, ]; - final FlutterSecureStorage secureStorage; + final SecureStorage secureStorage; final SharedPreferences sharedPreferences; final SettingsStore settingsStore; Future<void> setPassword(String password) async { final key = generateStoreKeyFor(key: SecretStoreKey.pinCodePassword); final encodedPassword = encodedPinCode(pin: password); - await writeSecureStorage(secureStorage, key: key, value: encodedPassword); + await secureStorage.write(key: key, value: encodedPassword); } Future<bool> canAuthenticate() async { @@ -61,7 +59,7 @@ class AuthService with Store { Future<bool> authenticate(String pin) async { final key = generateStoreKeyFor(key: SecretStoreKey.pinCodePassword); - final encodedPin = await readSecureStorage(secureStorage, key); + final encodedPin = await secureStorage.read(key: key); final decodedPin = decodedPinCode(pin: encodedPin!); return decodedPin == pin; @@ -69,11 +67,7 @@ class AuthService with Store { void saveLastAuthTime() { int timestamp = DateTime.now().millisecondsSinceEpoch; - writeSecureStorage( - secureStorage, - key: SecureKey.lastAuthTimeMilliseconds, - value: timestamp.toString(), - ); + secureStorage.write(key: SecureKey.lastAuthTimeMilliseconds, value: timestamp.toString()); } Future<bool> requireAuth() async { diff --git a/lib/core/backup_service.dart b/lib/core/backup_service.dart index d1092b024..d0d9a8a26 100644 --- a/lib/core/backup_service.dart +++ b/lib/core/backup_service.dart @@ -7,7 +7,6 @@ import 'package:cake_wallet/utils/device_info.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:flutter/foundation.dart'; import 'package:hive/hive.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:path_provider/path_provider.dart'; import 'package:cryptography/cryptography.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -25,7 +24,7 @@ import 'package:cake_backup/backup.dart' as cake_backup; class BackupService { BackupService( - this._flutterSecureStorage, this._walletInfoSource, this._keyService, this._sharedPreferences) + this._secureStorage, this._walletInfoSource, this._keyService, this._sharedPreferences) : _cipher = Cryptography.instance.chacha20Poly1305Aead(), _correctWallets = <WalletInfo>[]; @@ -35,7 +34,7 @@ class BackupService { static const _v2 = 2; final Cipher _cipher; - final FlutterSecureStorage _flutterSecureStorage; + final SecureStorage _secureStorage; final SharedPreferences _sharedPreferences; final Box<WalletInfo> _walletInfoSource; final KeyService _keyService; @@ -374,15 +373,14 @@ class BackupService { final backupPasswordKey = generateStoreKeyFor(key: SecretStoreKey.backupPassword); final backupPassword = keychainJSON[backupPasswordKey] as String; - await writeSecureStorage(_flutterSecureStorage, key: backupPasswordKey, value: backupPassword); + await _secureStorage.write(key: backupPasswordKey, value: backupPassword); keychainWalletsInfo.forEach((dynamic rawInfo) async { final info = rawInfo as Map<String, dynamic>; await importWalletKeychainInfo(info); }); - await writeSecureStorage(_flutterSecureStorage, - key: pinCodeKey, value: encodedPinCode(pin: decodedPin)); + await _secureStorage.write(key: pinCodeKey, value: encodedPinCode(pin: decodedPin)); keychainDumpFile.deleteSync(); } @@ -401,15 +399,14 @@ class BackupService { final backupPasswordKey = generateStoreKeyFor(key: SecretStoreKey.backupPassword); final backupPassword = keychainJSON[backupPasswordKey] as String; - await writeSecureStorage(_flutterSecureStorage, key: backupPasswordKey, value: backupPassword); + await _secureStorage.write(key: backupPasswordKey, value: backupPassword); keychainWalletsInfo.forEach((dynamic rawInfo) async { final info = rawInfo as Map<String, dynamic>; await importWalletKeychainInfo(info); }); - await writeSecureStorage(_flutterSecureStorage, - key: pinCodeKey, value: encodedPinCode(pin: decodedPin)); + await _secureStorage.write(key: pinCodeKey, value: encodedPinCode(pin: decodedPin)); keychainDumpFile.deleteSync(); } @@ -429,7 +426,7 @@ class BackupService { Future<Uint8List> _exportKeychainDumpV2(String password, {String keychainSalt = secrets.backupKeychainSalt}) async { final key = generateStoreKeyFor(key: SecretStoreKey.pinCodePassword); - final encodedPin = await _flutterSecureStorage.read(key: key); + final encodedPin = await _secureStorage.read(key: key); final decodedPin = decodedPinCode(pin: encodedPin!); final wallets = await Future.wait(_walletInfoSource.values.map((walletInfo) async { return { @@ -439,7 +436,7 @@ class BackupService { }; })); final backupPasswordKey = generateStoreKeyFor(key: SecretStoreKey.backupPassword); - final backupPassword = await _flutterSecureStorage.read(key: backupPasswordKey); + final backupPassword = await _secureStorage.read(key: backupPasswordKey); final data = utf8.encode( json.encode({'pin': decodedPin, 'wallets': wallets, backupPasswordKey: backupPassword})); final encrypted = await _encryptV2(Uint8List.fromList(data), '$keychainSalt$password'); diff --git a/lib/core/key_service.dart b/lib/core/key_service.dart index ba2da4de6..71fb5a4fc 100644 --- a/lib/core/key_service.dart +++ b/lib/core/key_service.dart @@ -1,31 +1,30 @@ import 'package:cake_wallet/core/secure_storage.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:cake_wallet/entities/secret_store_key.dart'; import 'package:cake_wallet/entities/encrypt.dart'; class KeyService { KeyService(this._secureStorage); - final FlutterSecureStorage _secureStorage; + final SecureStorage _secureStorage; Future<String> getWalletPassword({required String walletName}) async { - final key = generateStoreKeyFor( - key: SecretStoreKey.moneroWalletPassword, walletName: walletName); - final encodedPassword = await readSecureStorage(_secureStorage, key); + final key = + generateStoreKeyFor(key: SecretStoreKey.moneroWalletPassword, walletName: walletName); + final encodedPassword = await _secureStorage.read(key: key); return decodeWalletPassword(password: encodedPassword!); } Future<void> saveWalletPassword({required String walletName, required String password}) async { - final key = generateStoreKeyFor( - key: SecretStoreKey.moneroWalletPassword, walletName: walletName); + final key = + generateStoreKeyFor(key: SecretStoreKey.moneroWalletPassword, walletName: walletName); final encodedPassword = encodeWalletPassword(password: password); - await writeSecureStorage(_secureStorage, key: key, value: encodedPassword); + await _secureStorage.write(key: key, value: encodedPassword); } Future<void> deleteWalletPassword({required String walletName}) async { - final key = generateStoreKeyFor( - key: SecretStoreKey.moneroWalletPassword, walletName: walletName); + final key = + generateStoreKeyFor(key: SecretStoreKey.moneroWalletPassword, walletName: walletName); await _secureStorage.delete(key: key); } diff --git a/lib/core/secure_storage.dart b/lib/core/secure_storage.dart deleted file mode 100644 index 5afb36d29..000000000 --- a/lib/core/secure_storage.dart +++ /dev/null @@ -1,38 +0,0 @@ -import 'dart:async'; -import 'dart:io'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; -// For now, we can create a utility function to handle this. -// -// However, we could look into abstracting the entire FlutterSecureStorage package -// so the app doesn't depend on the package directly but an absraction. -// It'll make these kind of modifications to read/write come from a single point. - -Future<String?> readSecureStorage(FlutterSecureStorage secureStorage, String key) async { - String? result; - const maxWait = Duration(seconds: 3); - const checkInterval = Duration(milliseconds: 200); - - DateTime start = DateTime.now(); - - while (result == null && DateTime.now().difference(start) < maxWait) { - result = await secureStorage.read(key: key); - - if (result != null) { - break; - } - - await Future.delayed(checkInterval); - } - - return result; -} - -Future<void> writeSecureStorage(FlutterSecureStorage secureStorage, - {required String key, required String value}) async { - // delete the value before writing on macOS because of a weird bug - // https://github.com/mogol/flutter_secure_storage/issues/581 - if (Platform.isMacOS) { - await secureStorage.delete(key: key); - } - await secureStorage.write(key: key, value: value); -} diff --git a/lib/core/wallet_creation_service.dart b/lib/core/wallet_creation_service.dart index 646e47537..a55e9ee3f 100644 --- a/lib/core/wallet_creation_service.dart +++ b/lib/core/wallet_creation_service.dart @@ -1,8 +1,8 @@ +import 'package:cake_wallet/core/secure_storage.dart'; import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/store/settings_store.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cake_wallet/entities/preferences_key.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:hive/hive.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:cake_wallet/core/key_service.dart'; @@ -25,7 +25,7 @@ class WalletCreationService { } WalletType type; - final FlutterSecureStorage secureStorage; + final SecureStorage secureStorage; final SharedPreferences sharedPreferences; final SettingsStore settingsStore; final KeyService keyService; diff --git a/lib/di.dart b/lib/di.dart index 20157b0e0..3ee57cb53 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -236,6 +236,32 @@ import 'package:get_it/get_it.dart'; import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:cake_wallet/core/secure_storage.dart'; +import 'package:cake_wallet/core/wallet_creation_service.dart'; +import 'package:cake_wallet/store/app_store.dart'; +import 'package:cw_core/wallet_type.dart'; +import 'package:cake_wallet/view_model/wallet_new_vm.dart'; +import 'package:cake_wallet/store/authentication_store.dart'; +import 'package:cake_wallet/store/dashboard/trades_store.dart'; +import 'package:cake_wallet/store/dashboard/trade_filter_store.dart'; +import 'package:cake_wallet/store/dashboard/transaction_filter_store.dart'; +import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart'; +import 'package:cake_wallet/store/templates/send_template_store.dart'; +import 'package:cake_wallet/store/templates/exchange_template_store.dart'; +import 'package:cake_wallet/entities/template.dart'; +import 'package:cake_wallet/exchange/exchange_template.dart'; +import 'package:cake_wallet/.secrets.g.dart' as secrets; +import 'package:cake_wallet/src/screens/dashboard/pages/address_page.dart'; +import 'package:cake_wallet/anypay/anypay_api.dart'; +import 'package:cake_wallet/view_model/ionia/ionia_gift_card_details_view_model.dart'; +import 'package:cake_wallet/src/screens/ionia/cards/ionia_payment_status_page.dart'; +import 'package:cake_wallet/view_model/ionia/ionia_payment_status_view_model.dart'; +import 'package:cake_wallet/anypay/any_pay_payment_committed_info.dart'; +import 'package:cake_wallet/ionia/ionia_any_pay_payment_info.dart'; +import 'package:cake_wallet/src/screens/receive/fullscreen_qr_page.dart'; +import 'package:cake_wallet/core/wallet_loading_service.dart'; +import 'package:cw_core/crypto_currency.dart'; +import 'package:cake_wallet/entities/qr_view_data.dart'; import 'buy/dfx/dfx_buy_provider.dart'; import 'core/totp_request_details.dart'; @@ -268,7 +294,7 @@ Future<void> setup({ required Box<Order> ordersSource, required Box<UnspentCoinsInfo> unspentCoinsInfoSource, required Box<AnonpayInvoiceInfo> anonpayInvoiceInfoSource, - required FlutterSecureStorage secureStorage, + required SecureStorage secureStorage, required GlobalKey<NavigatorState> navigatorKey, }) async { _walletInfoSource = walletInfoSource; @@ -285,7 +311,7 @@ Future<void> setup({ if (!_isSetupFinished) { getIt.registerSingletonAsync<SharedPreferences>(() => SharedPreferences.getInstance()); - getIt.registerSingleton<FlutterSecureStorage>(secureStorage); + getIt.registerSingleton<SecureStorage>(secureStorage); } if (!_isSetupFinished) { getIt.registerFactory(() => BackgroundTasks()); @@ -333,22 +359,22 @@ Future<void> setup({ getIt.registerSingleton<ExchangeTemplateStore>( ExchangeTemplateStore(templateSource: _exchangeTemplates)); getIt.registerSingleton<YatStore>( - YatStore(appStore: getIt.get<AppStore>(), secureStorage: getIt.get<FlutterSecureStorage>()) + YatStore(appStore: getIt.get<AppStore>(), secureStorage: getIt.get<SecureStorage>()) ..init()); getIt.registerSingleton<AnonpayTransactionsStore>( AnonpayTransactionsStore(anonpayInvoiceInfoSource: _anonpayInvoiceInfoSource)); - final secretStore = await SecretStoreBase.load(getIt.get<FlutterSecureStorage>()); + final secretStore = await SecretStoreBase.load(getIt.get<SecureStorage>()); getIt.registerSingleton<SecretStore>(secretStore); - getIt.registerFactory<KeyService>(() => KeyService(getIt.get<FlutterSecureStorage>())); + getIt.registerFactory<KeyService>(() => KeyService(getIt.get<SecureStorage>())); getIt.registerFactoryParam<WalletCreationService, WalletType, void>((type, _) => WalletCreationService( initialType: type, keyService: getIt.get<KeyService>(), - secureStorage: getIt.get<FlutterSecureStorage>(), + secureStorage: getIt.get<SecureStorage>(), sharedPreferences: getIt.get<SharedPreferences>(), settingsStore: getIt.get<SettingsStore>(), walletInfoSource: _walletInfoSource)); @@ -403,7 +429,7 @@ Future<void> setup({ getIt.registerFactory<AuthService>( () => AuthService( - secureStorage: getIt.get<FlutterSecureStorage>(), + secureStorage: getIt.get<SecureStorage>(), sharedPreferences: getIt.get<SharedPreferences>(), settingsStore: getIt.get<SettingsStore>(), ), @@ -980,16 +1006,16 @@ Future<void> setup({ trades: _tradesSource, settingsStore: getIt.get<SettingsStore>())); - getIt.registerFactory(() => BackupService(getIt.get<FlutterSecureStorage>(), _walletInfoSource, + getIt.registerFactory(() => BackupService(getIt.get<SecureStorage>(), _walletInfoSource, getIt.get<KeyService>(), getIt.get<SharedPreferences>())); getIt.registerFactory(() => BackupViewModel( - getIt.get<FlutterSecureStorage>(), getIt.get<SecretStore>(), getIt.get<BackupService>())); + getIt.get<SecureStorage>(), getIt.get<SecretStore>(), getIt.get<BackupService>())); getIt.registerFactory(() => BackupPage(getIt.get<BackupViewModel>())); getIt.registerFactory(() => - EditBackupPasswordViewModel(getIt.get<FlutterSecureStorage>(), getIt.get<SecretStore>())); + EditBackupPasswordViewModel(getIt.get<SecureStorage>(), getIt.get<SecretStore>())); getIt.registerFactory(() => EditBackupPasswordPage(getIt.get<EditBackupPasswordViewModel>())); @@ -1038,7 +1064,7 @@ Future<void> setup({ getIt.registerFactory(() => SupportPage(getIt.get<SupportViewModel>())); getIt.registerFactory(() => SupportChatPage(getIt.get<SupportViewModel>(), - secureStorage: getIt.get<FlutterSecureStorage>())); + secureStorage: getIt.get<SecureStorage>())); getIt.registerFactory(() => SupportOtherLinksPage(getIt.get<SupportViewModel>())); @@ -1080,7 +1106,7 @@ Future<void> setup({ getIt.registerFactory(() => AnyPayApi()); getIt.registerFactory<IoniaService>( - () => IoniaService(getIt.get<FlutterSecureStorage>(), getIt.get<IoniaApi>())); + () => IoniaService(getIt.get<SecureStorage>(), getIt.get<IoniaApi>())); getIt.registerFactory<IoniaAnyPay>(() => IoniaAnyPay( getIt.get<IoniaService>(), getIt.get<AnyPayApi>(), getIt.get<AppStore>().wallet!)); diff --git a/lib/entities/default_settings_migration.dart b/lib/entities/default_settings_migration.dart index 77db474a9..e67bd2fc6 100644 --- a/lib/entities/default_settings_migration.dart +++ b/lib/entities/default_settings_migration.dart @@ -1,10 +1,10 @@ import 'dart:io' show Directory, File, Platform; import 'package:cake_wallet/bitcoin/bitcoin.dart'; +import 'package:cake_wallet/core/secure_storage.dart'; import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:cw_core/pathForWallet.dart'; import 'package:cake_wallet/entities/secret_store_key.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:hive/hive.dart'; import 'package:path_provider/path_provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -42,7 +42,7 @@ const newCakeWalletBitcoinUri = 'btc-electrum.cakewallet.com:50002'; Future<void> defaultSettingsMigration( {required int version, required SharedPreferences sharedPreferences, - required FlutterSecureStorage secureStorage, + required SecureStorage secureStorage, required Box<Node> nodes, required Box<Node> powNodes, required Box<WalletInfo> walletInfoSource, @@ -485,7 +485,7 @@ Node? getTronDefaultNode({required Box<Node> nodes}) { Future<void> insecureStorageMigration({ required SharedPreferences sharedPreferences, - required FlutterSecureStorage secureStorage, + required SecureStorage secureStorage, }) async { bool? allowBiometricalAuthentication = sharedPreferences.getBool(SecureKey.allowBiometricalAuthenticationKey); @@ -559,7 +559,7 @@ Future<void> insecureStorageMigration({ } } -Future<void> rewriteSecureStoragePin({required FlutterSecureStorage secureStorage}) async { +Future<void> rewriteSecureStoragePin({required SecureStorage secureStorage}) async { // the bug only affects ios/mac: if (!Platform.isIOS && !Platform.isMacOS) { return; @@ -585,8 +585,9 @@ Future<void> rewriteSecureStoragePin({required FlutterSecureStorage secureStorag await secureStorage.write( key: keyForPinCode, value: encodedPin, - iOptions: IOSOptions(accessibility: KeychainAccessibility.first_unlock), - mOptions: MacOsOptions(accessibility: KeychainAccessibility.first_unlock), + // TODO: find a way to add those with the generated secure storage + // iOptions: IOSOptions(accessibility: KeychainAccessibility.first_unlock), + // mOptions: MacOsOptions(accessibility: KeychainAccessibility.first_unlock), ); } @@ -720,7 +721,7 @@ Future<void> updateDisplayModes(SharedPreferences sharedPreferences) async { await sharedPreferences.setInt(PreferencesKey.currentBalanceDisplayModeKey, balanceDisplayMode); } -Future<void> generateBackupPassword(FlutterSecureStorage secureStorage) async { +Future<void> generateBackupPassword(SecureStorage secureStorage) async { final key = generateStoreKeyFor(key: SecretStoreKey.backupPassword); if ((await secureStorage.read(key: key))?.isNotEmpty ?? false) { diff --git a/lib/entities/fs_migration.dart b/lib/entities/fs_migration.dart index 869ed66ff..14237f080 100644 --- a/lib/entities/fs_migration.dart +++ b/lib/entities/fs_migration.dart @@ -2,7 +2,6 @@ import 'dart:io'; import 'dart:convert'; import 'package:cake_wallet/core/secure_storage.dart'; import 'package:collection/collection.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:hive/hive.dart'; import 'package:path_provider/path_provider.dart'; @@ -11,8 +10,7 @@ import 'package:cake_wallet/entities/contact.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cake_wallet/entities/encrypt.dart'; import 'package:cake_wallet/entities/fiat_currency.dart'; -import 'package:cake_wallet/entities/ios_legacy_helper.dart' - as ios_legacy_helper; +import 'package:cake_wallet/entities/ios_legacy_helper.dart' as ios_legacy_helper; import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/entities/secret_store_key.dart'; import 'package:cw_core/wallet_info.dart'; @@ -30,8 +28,8 @@ Future<void> migrate_android_v1() async { await android_migrate_wallets(appDocDir: appDocDir); } -Future<void> ios_migrate_v1(Box<WalletInfo> walletInfoSource, - Box<Trade> tradeSource, Box<Contact> contactSource) async { +Future<void> ios_migrate_v1( + Box<WalletInfo> walletInfoSource, Box<Trade> tradeSource, Box<Contact> contactSource) async { final prefs = await SharedPreferences.getInstance(); if (prefs.getBool('ios_migration_v1_completed') ?? false) { @@ -67,10 +65,7 @@ Future<void> ios_migrate_user_defaults() async { if (activeCurrency != null) { final convertedCurrency = convertFiatLegacy(activeCurrency); - if (convertedCurrency != null) { - await prefs.setString( - 'current_fiat_currency', convertedCurrency.serialize()); - } + await prefs.setString('current_fiat_currency', convertedCurrency.serialize()); } //translate fee priority @@ -81,24 +76,21 @@ Future<void> ios_migrate_user_defaults() async { } //translate current balance mode - final currentBalanceMode = - await ios_legacy_helper.getInt('display_balance_mode'); + final currentBalanceMode = await ios_legacy_helper.getInt('display_balance_mode'); if (currentBalanceMode != null) { await prefs.setInt('current_balance_display_mode', currentBalanceMode); } //translate should save recipient address - final shouldSave = - await ios_legacy_helper.getBool('should_save_recipient_address'); - + final shouldSave = await ios_legacy_helper.getBool('should_save_recipient_address'); + if (shouldSave != null) { await prefs.setBool('save_recipient_address', shouldSave); } //translate biometric - final biometricOn = - await ios_legacy_helper.getBool('biometric_authentication_on'); - + final biometricOn = await ios_legacy_helper.getBool('biometric_authentication_on'); + if (biometricOn != null) { await prefs.setBool('allow_biometrical_authentication', biometricOn); } @@ -137,9 +129,8 @@ Future<void> ios_migrate_pin() async { return; } - final flutterSecureStorage = FlutterSecureStorage(); - final pinPassword = await flutterSecureStorage.read( - key: 'pin_password', iOptions: IOSOptions()); + final flutterSecureStorage = secureStorageShared; + final pinPassword = await flutterSecureStorage.readNoIOptions(key: 'pin_password'); // No pin if (pinPassword == null) { await prefs.setBool('ios_migration_pin_completed', true); @@ -148,7 +139,7 @@ Future<void> ios_migrate_pin() async { final key = generateStoreKeyFor(key: SecretStoreKey.pinCodePassword); final encodedPassword = encodedPinCode(pin: pinPassword); - await writeSecureStorage(flutterSecureStorage, key: key, value: encodedPassword); + await flutterSecureStorage.write(key: key, value: encodedPassword); await prefs.setBool('ios_migration_pin_completed', true); } @@ -161,7 +152,7 @@ Future<void> ios_migrate_wallet_passwords() async { } final appDocDir = await getApplicationDocumentsDirectory(); - final flutterSecureStorage = FlutterSecureStorage(); + final flutterSecureStorage = secureStorageShared; final keyService = KeyService(flutterSecureStorage); final walletsDir = Directory('${appDocDir.path}/wallets'); final moneroWalletsDir = Directory('${walletsDir.path}/monero'); @@ -176,10 +167,8 @@ Future<void> ios_migrate_wallet_passwords() async { if (item is Directory) { final name = item.path.split('/').last; final oldKey = 'wallet_monero_' + name + '_password'; - final password = await flutterSecureStorage.read( - key: oldKey, iOptions: IOSOptions()); - await keyService.saveWalletPassword( - walletName: name, password: password!); + final password = await flutterSecureStorage.readNoIOptions(key: oldKey); + await keyService.saveWalletPassword(walletName: name, password: password!); } } catch (e) { print(e.toString()); @@ -311,18 +300,14 @@ Future<void> ios_migrate_wallet_info(Box<WalletInfo> walletsInfoSource) async { return null; } - final config = json.decode(configFile.readAsStringSync()) - as Map<String, dynamic>; - final isRecovery = config['isRecovery'] as bool ?? false; + final config = json.decode(configFile.readAsStringSync()) as Map<String, dynamic>; + final isRecovery = config['isRecovery'] as bool? ?? false; final dateAsDouble = config['date'] as double; final timestamp = dateAsDouble.toInt() * 1000; final date = DateTime.fromMillisecondsSinceEpoch(timestamp); - final id = walletTypeToString(WalletType.monero).toLowerCase() + - '_' + - name; - final exist = walletsInfoSource.values - .firstWhereOrNull((el) => el.id == id) != null; - + final id = walletTypeToString(WalletType.monero).toLowerCase() + '_' + name; + final exist = walletsInfoSource.values.firstWhereOrNull((el) => el.id == id) != null; + if (exist) { return null; } @@ -373,12 +358,10 @@ Future<void> ios_migrate_trades_list(Box<Trade> tradeSource) async { } final content = file.readAsBytesSync(); - final flutterSecureStorage = FlutterSecureStorage(); - final masterPassword = await flutterSecureStorage.read( - key: 'master_password', iOptions: IOSOptions()); + final flutterSecureStorage = secureStorageShared; + final masterPassword = await flutterSecureStorage.readNoIOptions(key: 'master_password'); final key = masterPassword!.replaceAll('-', ''); - final decoded = - await ios_legacy_helper.decrypt(content, key: key, salt: secrets.salt); + final decoded = await ios_legacy_helper.decrypt(content, key: key, salt: secrets.salt); final decodedJson = json.decode(decoded) as List<dynamic>; final trades = decodedJson.map((dynamic el) { final elAsMap = el as Map<String, dynamic>; @@ -441,8 +424,7 @@ Future<void> ios_migrate_address_book(Box<Contact> contactSource) async { final address = _item["address"] as String; final name = _item["name"] as String; - return Contact( - address: address, name: name, type: CryptoCurrency.fromString(type)); + return Contact(address: address, name: name, type: CryptoCurrency.fromString(type)); }); await contactSource.addAll(contacts); diff --git a/lib/entities/get_encryption_key.dart b/lib/entities/get_encryption_key.dart index 04c3a65f7..618066cb8 100644 --- a/lib/entities/get_encryption_key.dart +++ b/lib/entities/get_encryption_key.dart @@ -1,9 +1,8 @@ import 'package:cake_wallet/core/secure_storage.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:cw_core/cake_hive.dart'; Future<List<int>> getEncryptionKey( - {required String forKey, required FlutterSecureStorage secureStorage}) async { + {required String forKey, required SecureStorage secureStorage}) async { final stringifiedKey = await secureStorage.read(key: 'transactionDescriptionsBoxKey'); List<int> key; @@ -11,7 +10,7 @@ Future<List<int>> getEncryptionKey( key = CakeHive.generateSecureKey(); final keyStringified = key.join(','); String storageKey = 'transactionDescriptionsBoxKey'; - await writeSecureStorage(secureStorage, key: storageKey, value: keyStringified); + await secureStorage.write(key: storageKey, value: keyStringified); } else { key = stringifiedKey.split(',').map((i) => int.parse(i)).toList(); } diff --git a/lib/entities/preferences_key.dart b/lib/entities/preferences_key.dart index cf9ae3019..79177178c 100644 --- a/lib/entities/preferences_key.dart +++ b/lib/entities/preferences_key.dart @@ -61,9 +61,9 @@ class PreferencesKey { static const defaultBananoRep = 'default_banano_representative'; static const lookupsTwitter = 'looks_up_twitter'; static const lookupsMastodon = 'looks_up_mastodon'; - static const lookupsYatService = 'looks_up_mastodon'; - static const lookupsUnstoppableDomains = 'looks_up_mastodon'; - static const lookupsOpenAlias = 'looks_up_mastodon'; + static const lookupsYatService = 'looks_up_yat'; + static const lookupsUnstoppableDomains = 'looks_up_unstoppable_domain'; + static const lookupsOpenAlias = 'looks_up_open_alias'; static const lookupsENS = 'looks_up_ens'; static String moneroWalletUpdateV1Key(String name) => diff --git a/lib/entities/secret_store_key.dart b/lib/entities/secret_store_key.dart index 2ee490c74..df6347cca 100644 --- a/lib/entities/secret_store_key.dart +++ b/lib/entities/secret_store_key.dart @@ -1,4 +1,4 @@ -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:cake_wallet/core/secure_storage.dart'; import 'package:shared_preferences/shared_preferences.dart'; enum SecretStoreKey { moneroWalletPassword, pinCodePassword, backupPassword } @@ -66,7 +66,7 @@ class SecureKey { static const lastAuthTimeMilliseconds = 'last_auth_time_milliseconds'; static Future<int?> getInt({ - required FlutterSecureStorage secureStorage, + required SecureStorage secureStorage, required SharedPreferences sharedPreferences, required String key, }) async { @@ -76,7 +76,7 @@ class SecureKey { } static Future<bool?> getBool({ - required FlutterSecureStorage secureStorage, + required SecureStorage secureStorage, required SharedPreferences sharedPreferences, required String key, }) async { @@ -91,7 +91,7 @@ class SecureKey { } static Future<String?> getString({ - required FlutterSecureStorage secureStorage, + required SecureStorage secureStorage, required SharedPreferences sharedPreferences, required String key, }) async { diff --git a/lib/ionia/ionia_service.dart b/lib/ionia/ionia_service.dart index 0396ed7c1..821824a87 100644 --- a/lib/ionia/ionia_service.dart +++ b/lib/ionia/ionia_service.dart @@ -1,7 +1,7 @@ +import 'package:cake_wallet/core/secure_storage.dart'; import 'package:cake_wallet/ionia/ionia_merchant.dart'; import 'package:cake_wallet/ionia/ionia_order.dart'; import 'package:cake_wallet/ionia/ionia_virtual_card.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:cake_wallet/.secrets.g.dart' as secrets; import 'package:cake_wallet/ionia/ionia_api.dart'; import 'package:cake_wallet/ionia/ionia_gift_card.dart'; @@ -16,7 +16,7 @@ class IoniaService { static String get clientId => secrets.ioniaClientId; - final FlutterSecureStorage secureStorage; + final SecureStorage secureStorage; final IoniaApi ioniaApi; // Create user diff --git a/lib/main.dart b/lib/main.dart index 2a4e12236..fa71da31d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart'; import 'package:cake_wallet/core/auth_service.dart'; +import 'package:cake_wallet/core/secure_storage.dart'; import 'package:cake_wallet/entities/language_service.dart'; import 'package:cake_wallet/buy/order.dart'; import 'package:cake_wallet/locales/locale.dart'; @@ -18,7 +19,6 @@ import 'package:hive/hive.dart'; import 'package:cake_wallet/di.dart'; import 'package:path_provider/path_provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:cake_wallet/themes/theme_base.dart'; import 'package:cake_wallet/router.dart' as Router; @@ -138,9 +138,8 @@ Future<void> initializeAppConfigs() async { CakeHive.registerAdapter(AnonpayInvoiceInfoAdapter()); } - final secureStorage = FlutterSecureStorage( - iOptions: IOSOptions(accessibility: KeychainAccessibility.first_unlock), - ); + final secureStorage = secureStorageShared; + final transactionDescriptionsBoxKey = await getEncryptionKey(secureStorage: secureStorage, forKey: TransactionDescription.boxKey); final tradesBoxKey = await getEncryptionKey(secureStorage: secureStorage, forKey: Trade.boxKey); @@ -191,7 +190,7 @@ Future<void> initialSetup( required Box<Template> templates, required Box<ExchangeTemplate> exchangeTemplates, required Box<TransactionDescription> transactionDescriptions, - required FlutterSecureStorage secureStorage, + required SecureStorage secureStorage, required Box<AnonpayInvoiceInfo> anonpayInvoiceInfo, required Box<UnspentCoinsInfo> unspentCoinsInfoSource, int initialMigrationVersion = 15}) async { diff --git a/lib/src/screens/support_chat/support_chat_page.dart b/lib/src/screens/support_chat/support_chat_page.dart index e84965b23..97d59a2d9 100644 --- a/lib/src/screens/support_chat/support_chat_page.dart +++ b/lib/src/screens/support_chat/support_chat_page.dart @@ -1,16 +1,16 @@ +import 'package:cake_wallet/core/secure_storage.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/screens/support_chat/widgets/chatwoot_widget.dart'; import 'package:cake_wallet/view_model/support_view_model.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; class SupportChatPage extends BasePage { SupportChatPage(this.supportViewModel, {required this.secureStorage}); final SupportViewModel supportViewModel; - final FlutterSecureStorage secureStorage; + final SecureStorage secureStorage; @override String get title => S.current.settings_support; diff --git a/lib/src/screens/support_chat/widgets/chatwoot_widget.dart b/lib/src/screens/support_chat/widgets/chatwoot_widget.dart index 60c9ea97f..b49993f7f 100644 --- a/lib/src/screens/support_chat/widgets/chatwoot_widget.dart +++ b/lib/src/screens/support_chat/widgets/chatwoot_widget.dart @@ -3,14 +3,13 @@ import 'dart:convert'; import 'package:cake_wallet/core/secure_storage.dart'; import 'package:flutter/material.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; const COOKIE_KEY = 'chatwootCookie'; class ChatwootWidget extends StatefulWidget { ChatwootWidget(this.secureStorage, {required this.supportUrl}); - final FlutterSecureStorage secureStorage; + final SecureStorage secureStorage; final String supportUrl; @override @@ -59,6 +58,6 @@ class ChatwootWidgetState extends State<ChatwootWidget> { } Future<void> storeCookie(String value) async { - await writeSecureStorage(widget.secureStorage, key: COOKIE_KEY, value: value); + await widget.secureStorage.write(key: COOKIE_KEY, value: value); } } diff --git a/lib/store/secret_store.dart b/lib/store/secret_store.dart index 6d958acb5..aa185ae3d 100644 --- a/lib/store/secret_store.dart +++ b/lib/store/secret_store.dart @@ -1,6 +1,5 @@ +import 'package:cake_wallet/core/secure_storage.dart'; import 'package:cake_wallet/entities/secret_store_key.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:mobx/mobx.dart'; part 'secret_store.g.dart'; @@ -8,7 +7,7 @@ part 'secret_store.g.dart'; class SecretStore = SecretStoreBase with _$SecretStore; abstract class SecretStoreBase with Store { - static Future<SecretStore> load(FlutterSecureStorage storage) async { + static Future<SecretStore> load(SecureStorage storage) async { final secretStore = SecretStore(); final backupPasswordKey = generateStoreKeyFor(key: SecretStoreKey.backupPassword); final backupPassword = await storage.read(key: backupPasswordKey); diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart index ada08c1b6..ec9820944 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -26,7 +26,6 @@ import 'package:cake_wallet/themes/theme_base.dart'; import 'package:cake_wallet/themes/theme_list.dart'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; import 'package:package_info/package_info.dart'; @@ -48,7 +47,7 @@ class SettingsStore = SettingsStoreBase with _$SettingsStore; abstract class SettingsStoreBase with Store { SettingsStoreBase( - {required FlutterSecureStorage secureStorage, + {required SecureStorage secureStorage, required BackgroundTasks backgroundTasks, required SharedPreferences sharedPreferences, required bool initialShouldShowMarketPlaceInDashboard, @@ -398,10 +397,8 @@ abstract class SettingsStoreBase with Store { (bool usePolygonScan) => _sharedPreferences.setBool(PreferencesKey.usePolygonScan, usePolygonScan)); - reaction( - (_) => useTronGrid, - (bool useTronGrid) => - _sharedPreferences.setBool(PreferencesKey.useTronGrid, useTronGrid)); + reaction((_) => useTronGrid, + (bool useTronGrid) => _sharedPreferences.setBool(PreferencesKey.useTronGrid, useTronGrid)); reaction((_) => defaultNanoRep, (String nanoRep) => _sharedPreferences.setString(PreferencesKey.defaultNanoRep, nanoRep)); @@ -441,83 +438,79 @@ abstract class SettingsStoreBase with Store { // secure storage keys: reaction( (_) => allowBiometricalAuthentication, - (bool biometricalAuthentication) => writeSecureStorage(secureStorage, + (bool biometricalAuthentication) => secureStorage.write( key: SecureKey.allowBiometricalAuthenticationKey, value: biometricalAuthentication.toString())); reaction( (_) => selectedCake2FAPreset, - (Cake2FAPresetsOptions selectedCake2FAPreset) => writeSecureStorage(secureStorage, + (Cake2FAPresetsOptions selectedCake2FAPreset) => secureStorage.write( key: SecureKey.selectedCake2FAPreset, value: selectedCake2FAPreset.serialize().toString())); reaction( (_) => shouldRequireTOTP2FAForAccessingWallet, - (bool requireTOTP2FAForAccessingWallet) => writeSecureStorage(secureStorage, + (bool requireTOTP2FAForAccessingWallet) => secureStorage.write( key: SecureKey.shouldRequireTOTP2FAForAccessingWallet, value: requireTOTP2FAForAccessingWallet.toString())); reaction( (_) => shouldRequireTOTP2FAForSendsToContact, - (bool requireTOTP2FAForSendsToContact) => writeSecureStorage(secureStorage, + (bool requireTOTP2FAForSendsToContact) => secureStorage.write( key: SecureKey.shouldRequireTOTP2FAForSendsToContact, value: requireTOTP2FAForSendsToContact.toString())); reaction( (_) => shouldRequireTOTP2FAForSendsToNonContact, - (bool requireTOTP2FAForSendsToNonContact) => writeSecureStorage(secureStorage, + (bool requireTOTP2FAForSendsToNonContact) => secureStorage.write( key: SecureKey.shouldRequireTOTP2FAForSendsToNonContact, value: requireTOTP2FAForSendsToNonContact.toString())); reaction( (_) => shouldRequireTOTP2FAForSendsToInternalWallets, - (bool requireTOTP2FAForSendsToInternalWallets) => writeSecureStorage(secureStorage, + (bool requireTOTP2FAForSendsToInternalWallets) => secureStorage.write( key: SecureKey.shouldRequireTOTP2FAForSendsToInternalWallets, value: requireTOTP2FAForSendsToInternalWallets.toString())); reaction( (_) => shouldRequireTOTP2FAForExchangesToInternalWallets, - (bool requireTOTP2FAForExchangesToInternalWallets) => writeSecureStorage(secureStorage, + (bool requireTOTP2FAForExchangesToInternalWallets) => secureStorage.write( key: SecureKey.shouldRequireTOTP2FAForExchangesToInternalWallets, value: requireTOTP2FAForExchangesToInternalWallets.toString())); reaction( (_) => shouldRequireTOTP2FAForExchangesToExternalWallets, - (bool requireTOTP2FAForExchangesToExternalWallets) => writeSecureStorage(secureStorage, + (bool requireTOTP2FAForExchangesToExternalWallets) => secureStorage.write( key: SecureKey.shouldRequireTOTP2FAForExchangesToExternalWallets, value: requireTOTP2FAForExchangesToExternalWallets.toString())); reaction( (_) => shouldRequireTOTP2FAForAddingContacts, - (bool requireTOTP2FAForAddingContacts) => writeSecureStorage(secureStorage, + (bool requireTOTP2FAForAddingContacts) => secureStorage.write( key: SecureKey.shouldRequireTOTP2FAForAddingContacts, value: requireTOTP2FAForAddingContacts.toString())); reaction( (_) => shouldRequireTOTP2FAForCreatingNewWallets, - (bool requireTOTP2FAForCreatingNewWallets) => writeSecureStorage(secureStorage, + (bool requireTOTP2FAForCreatingNewWallets) => secureStorage.write( key: SecureKey.shouldRequireTOTP2FAForCreatingNewWallets, value: requireTOTP2FAForCreatingNewWallets.toString())); reaction( (_) => shouldRequireTOTP2FAForAllSecurityAndBackupSettings, - (bool requireTOTP2FAForAllSecurityAndBackupSettings) => writeSecureStorage(secureStorage, + (bool requireTOTP2FAForAllSecurityAndBackupSettings) => secureStorage.write( key: SecureKey.shouldRequireTOTP2FAForAllSecurityAndBackupSettings, value: requireTOTP2FAForAllSecurityAndBackupSettings.toString())); - reaction( - (_) => useTOTP2FA, - (bool use) => - writeSecureStorage(secureStorage, key: SecureKey.useTOTP2FA, value: use.toString())); + reaction((_) => useTOTP2FA, + (bool use) => secureStorage.write(key: SecureKey.useTOTP2FA, value: use.toString())); - reaction( - (_) => totpSecretKey, - (String totpKey) => - writeSecureStorage(secureStorage, key: SecureKey.totpSecretKey, value: totpKey)); + reaction((_) => totpSecretKey, + (String totpKey) => secureStorage.write(key: SecureKey.totpSecretKey, value: totpKey)); reaction( (_) => pinTimeOutDuration, - (PinCodeRequiredDuration pinCodeInterval) => writeSecureStorage(secureStorage, + (PinCodeRequiredDuration pinCodeInterval) => secureStorage.write( key: SecureKey.pinTimeOutDuration, value: pinCodeInterval.value.toString())); reaction( @@ -720,7 +713,7 @@ abstract class SettingsStoreBase with Store { @observable int customBitcoinFeeRate; - final FlutterSecureStorage _secureStorage; + final SecureStorage _secureStorage; final SharedPreferences _sharedPreferences; final BackgroundTasks _backgroundTasks; @@ -763,7 +756,7 @@ abstract class SettingsStoreBase with Store { BalanceDisplayMode initialBalanceDisplayMode = BalanceDisplayMode.availableBalance, ThemeBase? initialTheme}) async { final sharedPreferences = await getIt.getAsync<SharedPreferences>(); - final secureStorage = await getIt.get<FlutterSecureStorage>(); + final secureStorage = await getIt.get<SecureStorage>(); final backgroundTasks = getIt.get<BackgroundTasks>(); final currentFiatCurrency = FiatCurrency.deserialize( raw: sharedPreferences.getString(PreferencesKey.currentFiatCurrencyKey)!); diff --git a/lib/store/yat/yat_store.dart b/lib/store/yat/yat_store.dart index 290b991c9..81c5de3b5 100644 --- a/lib/store/yat/yat_store.dart +++ b/lib/store/yat/yat_store.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/core/secure_storage.dart'; import 'package:cw_core/transaction_history.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/balance.dart'; @@ -10,7 +11,6 @@ import 'dart:convert'; import 'package:cake_wallet/store/yat/yat_exception.dart'; import 'package:http/http.dart'; import 'dart:async'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; part 'yat_store.g.dart'; @@ -193,7 +193,7 @@ abstract class YatStoreBase with Store { AppStore appStore; - FlutterSecureStorage secureStorage; + SecureStorage secureStorage; @observable String emoji; diff --git a/lib/view_model/backup_view_model.dart b/lib/view_model/backup_view_model.dart index 8cc651b17..1b9b5e4ff 100644 --- a/lib/view_model/backup_view_model.dart +++ b/lib/view_model/backup_view_model.dart @@ -1,10 +1,9 @@ import 'dart:io'; import 'package:cake_wallet/core/backup_service.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:flutter/foundation.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:mobx/mobx.dart'; import 'package:intl/intl.dart'; import 'package:cake_wallet/wallet_type_utils.dart'; @@ -34,7 +33,7 @@ abstract class BackupViewModelBase with Store { }, fireImmediately: true); } - final FlutterSecureStorage secureStorage; + final SecureStorage secureStorage; final SecretStore secretStore; final BackupService backupService; diff --git a/lib/view_model/edit_backup_password_view_model.dart b/lib/view_model/edit_backup_password_view_model.dart index 729551e74..64c6c166a 100644 --- a/lib/view_model/edit_backup_password_view_model.dart +++ b/lib/view_model/edit_backup_password_view_model.dart @@ -1,6 +1,5 @@ import 'package:cake_wallet/core/secure_storage.dart'; import 'package:mobx/mobx.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:cake_wallet/entities/secret_store_key.dart'; import 'package:cake_wallet/store/secret_store.dart'; @@ -14,7 +13,7 @@ abstract class EditBackupPasswordViewModelBase with Store { : backupPassword = secretStore.read(generateStoreKeyFor(key: SecretStoreKey.backupPassword)), _originalPassword = ''; - final FlutterSecureStorage secureStorage; + final SecureStorage secureStorage; final SecretStore secretStore; @observable @@ -38,7 +37,7 @@ abstract class EditBackupPasswordViewModelBase with Store { @action Future<void> save() async { final key = generateStoreKeyFor(key: SecretStoreKey.backupPassword); - await writeSecureStorage(secureStorage, key: key, value: backupPassword); + await secureStorage.write(key: key, value: backupPassword); secretStore.write(key: key, value: backupPassword); } } diff --git a/pubspec_base.yaml b/pubspec_base.yaml index 02e61e0b4..a501b9af9 100644 --- a/pubspec_base.yaml +++ b/pubspec_base.yaml @@ -11,12 +11,6 @@ dependencies: ref: cake-4.0.2 version: 4.0.2 shared_preferences: ^2.0.15 - flutter_secure_storage: - git: - url: https://github.com/cake-tech/flutter_secure_storage.git - path: flutter_secure_storage - ref: cake-9.0.0 - version: 9.0.0 # provider: ^6.0.3 rxdart: ^0.27.4 yaml: ^3.1.1 diff --git a/tool/configure.dart b/tool/configure.dart index 64f43d349..959809b07 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -10,6 +10,7 @@ const polygonOutputPath = 'lib/polygon/polygon.dart'; const solanaOutputPath = 'lib/solana/solana.dart'; const tronOutputPath = 'lib/tron/tron.dart'; const walletTypesPath = 'lib/wallet_types.g.dart'; +const secureStoragePath = 'lib/core/secure_storage.dart'; const pubspecDefaultPath = 'pubspec_default.yaml'; const pubspecOutputPath = 'pubspec.yaml'; @@ -25,6 +26,7 @@ Future<void> main(List<String> args) async { final hasPolygon = args.contains('${prefix}polygon'); final hasSolana = args.contains('${prefix}solana'); final hasTron = args.contains('${prefix}tron'); + final excludeFlutterSecureStorage = args.contains('${prefix}excludeFlutterSecureStorage'); await generateBitcoin(hasBitcoin); await generateMonero(hasMonero); @@ -45,6 +47,7 @@ Future<void> main(List<String> args) async { hasNano: hasNano, hasBanano: hasBanano, hasBitcoinCash: hasBitcoinCash, + hasFlutterSecureStorage: !excludeFlutterSecureStorage, hasPolygon: hasPolygon, hasSolana: hasSolana, hasTron: hasTron, @@ -61,6 +64,7 @@ Future<void> main(List<String> args) async { hasSolana: hasSolana, hasTron: hasTron, ); + await injectSecureStorage(!excludeFlutterSecureStorage); } Future<void> generateBitcoin(bool hasImplementation) async { @@ -1142,6 +1146,7 @@ Future<void> generatePubspec( required bool hasNano, required bool hasBanano, required bool hasBitcoinCash, + required bool hasFlutterSecureStorage, required bool hasPolygon, required bool hasSolana, required bool hasTron}) async { @@ -1165,6 +1170,14 @@ Future<void> generatePubspec( cw_shared_external: path: ./cw_shared_external """; + const flutterSecureStorage = """ + flutter_secure_storage: + git: + url: https://github.com/cake-tech/flutter_secure_storage.git + path: flutter_secure_storage + ref: cake-9.0.0 + version: 9.0.0 + """; const cwEthereum = """ cw_ethereum: path: ./cw_ethereum @@ -1246,6 +1259,10 @@ Future<void> generatePubspec( output += '\n$cwHaven'; } + if (hasFlutterSecureStorage) { + output += '\n$flutterSecureStorage\n'; + } + if (hasEthereum || hasPolygon) { output += '\n$cwEVM'; } @@ -1330,3 +1347,103 @@ Future<void> generateWalletTypes( outputContent += '];\n'; await walletTypesFile.writeAsString(outputContent); } + +Future<void> injectSecureStorage(bool hasFlutterSecureStorage) async { + const flutterSecureStorageHeader = """ +import 'dart:async'; +import 'dart:io'; +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +"""; + const abstractSecureStorage = """ +abstract class SecureStorage { + Future<String?> read({required String key}); + Future<void> write({required String key, required String? value}); + Future<void> delete({required String key}); + // Legacy + Future<String?> readNoIOptions({required String key}); + }"""; + const defaultSecureStorage = """ +class DefaultSecureStorage extends SecureStorage { + DefaultSecureStorage._(this._secureStorage); + + factory DefaultSecureStorage() => _instance; + + static final _instance = DefaultSecureStorage._(FlutterSecureStorage( + iOptions: IOSOptions(accessibility: KeychainAccessibility.first_unlock), + )); + + final FlutterSecureStorage _secureStorage; + + @override + Future<String?> read({required String key}) async => await _readInternal(key, false); + + @override + Future<void> write({required String key, required String? value}) async { + // delete the value before writing on macOS because of a weird bug + // https://github.com/mogol/flutter_secure_storage/issues/581 + if (Platform.isMacOS) { + await _secureStorage.delete(key: key); + } + await _secureStorage.write(key: key, value: value); + } + + @override + Future<void> delete({required String key}) async => _secureStorage.delete(key: key); + + @override + Future<String?> readNoIOptions({required String key}) async => await _readInternal(key, true); + + Future<String?> _readInternal(String key, bool useNoIOptions) async { + String? result; + + const maxWait = Duration(seconds: 3); + const checkInterval = Duration(milliseconds: 200); + + DateTime start = DateTime.now(); + + while (result == null && DateTime.now().difference(start) < maxWait) { + result = await _secureStorage.read( + key: key, + iOptions: useNoIOptions ? IOSOptions() : null, + ); + + if (result != null) { + break; + } + + await Future.delayed(checkInterval); + } + + return result; + } + }"""; + const fakeSecureStorage = """ +class FakeSecureStorage extends SecureStorage { + @override + Future<String?> read({required String key}) async => null; + @override + Future<void> write({required String key, required String? value}) async {} + @override + Future<void> delete({required String key}) async {} + @override + Future<String?> readNoIOptions({required String key}) async => null; + }"""; + final outputFile = File(secureStoragePath); + final header = hasFlutterSecureStorage + ? '${flutterSecureStorageHeader}\n\nfinal SecureStorage secureStorageShared = DefaultSecureStorage();\n' + : 'final SecureStorage secureStorageShared = FakeSecureStorage();\n'; + var output = ''; + if (outputFile.existsSync()) { + await outputFile.delete(); + } + + output += '${header}\n${abstractSecureStorage}\n\n'; + + if (hasFlutterSecureStorage) { + output += '${defaultSecureStorage}\n'; + } else { + output += '${fakeSecureStorage}\n'; + } + + await outputFile.writeAsString(output); +}