diff --git a/lib/shared_prefs_helper.dart b/lib/shared_prefs_helper.dart index 7971906336911a580bb35971e91f6c504411ccd1..29dc69be53b81282e5dc0a06b7b5797fcc085965 100644 --- a/lib/shared_prefs_helper.dart +++ b/lib/shared_prefs_helper.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'package:durt/durt.dart'; import 'package:flutter/foundation.dart'; +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:polkadart_keyring/polkadart_keyring.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -18,34 +19,65 @@ class SharedPreferencesHelper with ChangeNotifier { SharedPreferencesHelper._internal() { SharedPreferences.getInstance().then((SharedPreferences value) { _prefs = value; + _migrateToSecureStorage(); }); } - List<CesiumCard> cesiumCards = <CesiumCard>[]; + static const FlutterSecureStorage _secureStorage = FlutterSecureStorage(); + List<AccountCard> cesiumCards = <AccountCard>[]; Map<String, CesiumWallet> cesiumVolatileCards = <String, CesiumWallet>{}; + late SharedPreferences _prefs; static final SharedPreferencesHelper _instance = SharedPreferencesHelper._internal(); - late SharedPreferences _prefs; - + // Legacy keys static const String _seedKey = 'seed'; static const String _pubKey = 'pub'; + static const String _legacyAccountsKey = 'cesiumCards'; + static const String _legacyCurrentAccountIndex = 'current_wallet_index'; + + // new keys + static const String _accountsKey = 'duniter_accounts'; + static const String _currentAccountIndex = 'current_account_index'; Future<void> init() async { _prefs = await SharedPreferences.getInstance(); + await _migrateToSecureStorage(); - final String? json = _prefs.getString('cesiumCards'); + // Load data from secure storage + final String? json = await _secureStorage.read(key: _accountsKey); if (json != null) { final List<dynamic> list = jsonDecode(json) as List<dynamic>; cesiumCards = list - .map((dynamic e) => CesiumCard.fromJson(e as Map<String, dynamic>)) + .map((dynamic e) => AccountCard.fromJson(e as Map<String, dynamic>)) .toList(); } + } + + Future<void> _migrateToSecureStorage() async { + // Check if data is already in secure storage + final bool isMigrated = await _secureStorage.containsKey(key: _accountsKey); - // Migrate the current pair if exists - await migrateCurrentPair(); + if (!isMigrated) { + // Migrate cesiumCards + final String? json = _prefs.getString(_legacyAccountsKey); + if (json != null) { + await _secureStorage.write(key: _accountsKey, value: json); + } + + // Migrate current wallet index + final int? currentWalletIndex = _prefs.getInt(_legacyCurrentAccountIndex); + if (currentWalletIndex != null) { + await _secureStorage.write( + key: _currentAccountIndex, value: currentWalletIndex.toString()); + } + + // Remove sensitive data from SharedPreferences + await _prefs.remove(_legacyAccountsKey); + await _prefs.remove(_legacyCurrentAccountIndex); + } } Future<void> migrateCurrentPair() async { @@ -54,7 +86,7 @@ class SharedPreferencesHelper with ChangeNotifier { cesiumCards.isEmpty) { final String seed = _prefs.getString(_seedKey)!; final String pubKey = _prefs.getString(_pubKey)!; - final CesiumCard card = buildCesiumCard(seed: seed, pubKey: pubKey); + final AccountCard card = buildCesiumCard(seed: seed, pubKey: pubKey); addCesiumCard(card); // Let's do this later await _prefs.remove(_seedKey); @@ -63,12 +95,12 @@ class SharedPreferencesHelper with ChangeNotifier { } } - CesiumCard buildCesiumCard({required String seed, required String pubKey}) { - return CesiumCard( + AccountCard buildCesiumCard({required String seed, required String pubKey}) { + return AccountCard( seed: seed, pubKey: pubKey, theme: CreditCardThemes.theme1, name: ''); } - void addCesiumCard(CesiumCard cesiumCard) { + void addCesiumCard(AccountCard cesiumCard) { cesiumCards.add(cesiumCard); saveCesiumCards(); } @@ -85,8 +117,8 @@ class SharedPreferencesHelper with ChangeNotifier { Future<void> saveCesiumCards([bool notify = true]) async { final String json = - jsonEncode(cesiumCards.map((CesiumCard e) => e.toJson()).toList()); - await _prefs.setString('cesiumCards', json); + jsonEncode(cesiumCards.map((AccountCard e) => e.toJson()).toList()); + await _secureStorage.write(key: _accountsKey, value: json); if (notify) { notifyListeners(); } @@ -95,7 +127,7 @@ class SharedPreferencesHelper with ChangeNotifier { // Get the wallet from the specified index (default to first wallet) Future<CesiumWallet> getWallet() async { if (cesiumCards.isNotEmpty) { - final CesiumCard card = cesiumCards[getCurrentWalletIndex()]; + final AccountCard card = cesiumCards[getCurrentWalletIndex()]; if (isG1nkgoCard()) { return CesiumWallet.fromSeed(seedFromString(card.seed)); } else { @@ -120,54 +152,53 @@ class SharedPreferencesHelper with ChangeNotifier { // Get the public key from the specified index (default to first wallet) String getPubKey() { - final CesiumCard card = cesiumCards[getCurrentWalletIndex()]; + final AccountCard card = cesiumCards[getCurrentWalletIndex()]; final String pubKey = card.pubKey; final String checksum = pkChecksum(extractPublicKey(pubKey)); return '$pubKey:$checksum'; } String getName() { - final CesiumCard card = cesiumCards[getCurrentWalletIndex()]; + final AccountCard card = cesiumCards[getCurrentWalletIndex()]; return card.name; } - CreditCardTheme getTheme() { - final CesiumCard card = cesiumCards[getCurrentWalletIndex()]; + AccountCardTheme getTheme() { + final AccountCard card = cesiumCards[getCurrentWalletIndex()]; return card.theme; } void setName({required String name, bool notify = true}) { - final CesiumCard card = cesiumCards[getCurrentWalletIndex()]; + final AccountCard card = cesiumCards[getCurrentWalletIndex()]; cesiumCards[getCurrentWalletIndex()] = card.copyWith(name: name); saveCesiumCards(notify); } - void setTheme({required CreditCardTheme theme}) { - final CesiumCard card = cesiumCards[getCurrentWalletIndex()]; + void setTheme({required AccountCardTheme theme}) { + final AccountCard card = cesiumCards[getCurrentWalletIndex()]; cesiumCards[getCurrentWalletIndex()] = card.copyWith(theme: theme); saveCesiumCards(); } - List<CesiumCard> get cards => cesiumCards; - - static const String _currentWalletIndexKey = 'current_wallet_index'; + List<AccountCard> get cards => cesiumCards; - // Get the current wallet index from shared preferences int getCurrentWalletIndex() { - return _prefs.getInt(_currentWalletIndexKey) ?? 0; + final String? indexStr = + _secureStorage.read(key: _currentAccountIndex) as String?; + return indexStr != null ? int.parse(indexStr) : 0; } - // Set the current wallet index in shared preferences Future<void> setCurrentWalletIndex(int index) async { - await _prefs.setInt(_currentWalletIndexKey, index); + await _secureStorage.write( + key: _currentAccountIndex, value: index.toString()); notifyListeners(); } - Future<void> selectCurrentWallet(CesiumCard card) async { + Future<void> selectCurrentWallet(AccountCard card) async { // TODO(vjrj): this should be a find with pubkey final int index = cards.indexOf(card); if (index >= 0) { - await _prefs.setInt(_currentWalletIndexKey, index); + await _prefs.setInt(_legacyCurrentAccountIndex, index); notifyListeners(); } else { throw Exception('Invalid wallet index: $index'); @@ -184,7 +215,7 @@ class SharedPreferencesHelper with ChangeNotifier { } bool has(String pubKey) { - for (final CesiumCard card in cesiumCards) { + for (final AccountCard card in cesiumCards) { if (card.pubKey == extractPublicKey(pubKey) || card.pubKey == pubKey) { return true; } @@ -204,8 +235,8 @@ class SharedPreferencesHelper with ChangeNotifier { cesiumVolatileCards[cesiumWallet.pubkey] = cesiumWallet; } - bool isG1nkgoCard([CesiumCard? otherCard]) { - final CesiumCard card = otherCard ?? cesiumCards[getCurrentWalletIndex()]; + bool isG1nkgoCard([AccountCard? otherCard]) { + final AccountCard card = otherCard ?? cesiumCards[getCurrentWalletIndex()]; return card.seed.isNotEmpty; } diff --git a/pubspec.lock b/pubspec.lock index 525b85df4b585ecf4878da39a1bfce7560f726cd..9543cb9ea3aaffc1f8a47aecdb047018a68065bb 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -870,6 +870,54 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.24" + flutter_secure_storage: + dependency: "direct main" + description: + name: flutter_secure_storage + sha256: "9cad52d75ebc511adfae3d447d5d13da15a55a92c9410e50f67335b6d21d16ea" + url: "https://pub.dev" + source: hosted + version: "9.2.4" + flutter_secure_storage_linux: + dependency: transitive + description: + name: flutter_secure_storage_linux + sha256: bf7404619d7ab5c0a1151d7c4e802edad8f33535abfbeff2f9e1fe1274e2d705 + url: "https://pub.dev" + source: hosted + version: "1.2.2" + flutter_secure_storage_macos: + dependency: transitive + description: + name: flutter_secure_storage_macos + sha256: "6c0a2795a2d1de26ae202a0d78527d163f4acbb11cde4c75c670f3a0fc064247" + url: "https://pub.dev" + source: hosted + version: "3.1.3" + flutter_secure_storage_platform_interface: + dependency: transitive + description: + name: flutter_secure_storage_platform_interface + sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8 + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_secure_storage_web: + dependency: transitive + description: + name: flutter_secure_storage_web + sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + flutter_secure_storage_windows: + dependency: transitive + description: + name: flutter_secure_storage_windows + sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709 + url: "https://pub.dev" + source: hosted + version: "3.1.2" flutter_slidable: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 78f3049e4bddc0d116199bbe7d60bd62c5f18e58..8f81e16ba17861667311d5313148bf26fed2c152 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -136,6 +136,7 @@ dependencies: substrate_bip39: ^0.4.1 intl: ^0.18.1 app_links: ^6.3.3 + flutter_secure_storage: ^9.2.4 dev_dependencies: flutter_test: