From bdb3ec204881d87415985ce4a7cdf734953f8b91 Mon Sep 17 00:00:00 2001 From: Omar Hatem Date: Tue, 8 Aug 2023 16:58:07 +0300 Subject: [PATCH] Add Encryption file utils to Ethereum (#1025) --- cw_ethereum/lib/encryption_file_utils.dart | 42 +++++++++++++++++++ .../lib/ethereum_transaction_history.dart | 14 ++++--- cw_ethereum/lib/ethereum_wallet.dart | 17 ++++++-- cw_ethereum/lib/ethereum_wallet_service.dart | 18 ++++++-- cw_ethereum/pubspec.yaml | 5 +++ lib/di.dart | 6 +-- lib/ethereum/cw_ethereum.dart | 4 +- tool/configure.dart | 2 +- 8 files changed, 89 insertions(+), 19 deletions(-) create mode 100644 cw_ethereum/lib/encryption_file_utils.dart diff --git a/cw_ethereum/lib/encryption_file_utils.dart b/cw_ethereum/lib/encryption_file_utils.dart new file mode 100644 index 000000000..4644b8cfb --- /dev/null +++ b/cw_ethereum/lib/encryption_file_utils.dart @@ -0,0 +1,42 @@ +import 'dart:io'; +import 'dart:typed_data'; +import 'package:cw_ethereum/file.dart' as ef; +import 'package:cake_backup/backup.dart' as cwb; + +EncryptionFileUtils encryptionFileUtilsFor(bool direct) + => direct + ? XChaCha20EncryptionFileUtils() + : Salsa20EncryhptionFileUtils(); + +abstract class EncryptionFileUtils { + Future write({required String path, required String password, required String data}); + Future read({required String path, required String password}); +} + +class Salsa20EncryhptionFileUtils extends EncryptionFileUtils { + // Requires legacy complex key + iv as password + @override + Future write({required String path, required String password, required String data}) async + => await ef.write(path: path, password: password, data: data); + + // Requires legacy complex key + iv as password + @override + Future read({required String path, required String password}) async + => await ef.read(path: path, password: password); +} + +class XChaCha20EncryptionFileUtils extends EncryptionFileUtils { + @override + Future write({required String path, required String password, required String data}) async { + final encrypted = await cwb.encrypt(password, Uint8List.fromList(data.codeUnits)); + await File(path).writeAsBytes(encrypted); + } + + @override + Future read({required String path, required String password}) async { + final file = File(path); + final encrypted = await file.readAsBytes(); + final bytes = await cwb.decrypt(password, encrypted); + return String.fromCharCodes(bytes); + } +} \ No newline at end of file diff --git a/cw_ethereum/lib/ethereum_transaction_history.dart b/cw_ethereum/lib/ethereum_transaction_history.dart index 4511f4436..397625f05 100644 --- a/cw_ethereum/lib/ethereum_transaction_history.dart +++ b/cw_ethereum/lib/ethereum_transaction_history.dart @@ -2,7 +2,7 @@ import 'dart:convert'; import 'dart:core'; import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/wallet_info.dart'; -import 'package:cw_ethereum/file.dart'; +import 'package:cw_ethereum/encryption_file_utils.dart'; import 'package:mobx/mobx.dart'; import 'package:cw_core/transaction_history.dart'; import 'package:cw_ethereum/ethereum_transaction_info.dart'; @@ -15,12 +15,16 @@ class EthereumTransactionHistory = EthereumTransactionHistoryBase with _$Ethereu abstract class EthereumTransactionHistoryBase extends TransactionHistoryBase with Store { - EthereumTransactionHistoryBase({required this.walletInfo, required String password}) - : _password = password { + EthereumTransactionHistoryBase({ + required this.walletInfo, + required String password, + required this.encryptionFileUtils, + }) : _password = password { transactions = ObservableMap(); } final WalletInfo walletInfo; + final EncryptionFileUtils encryptionFileUtils; String _password; Future init() async => await _load(); @@ -31,7 +35,7 @@ abstract class EthereumTransactionHistoryBase final dirPath = await pathForWalletDir(name: walletInfo.name, type: walletInfo.type); final path = '$dirPath/$transactionsHistoryFileName'; final data = json.encode({'transactions': transactions}); - await writeData(path: path, password: _password, data: data); + await encryptionFileUtils.write(path: path, password: _password, data: data); } catch (e, s) { print('Error while save ethereum transaction history: ${e.toString()}'); print(s); @@ -48,7 +52,7 @@ abstract class EthereumTransactionHistoryBase Future> _read() async { final dirPath = await pathForWalletDir(name: walletInfo.name, type: walletInfo.type); final path = '$dirPath/$transactionsHistoryFileName'; - final content = await read(path: path, password: _password); + final content = await encryptionFileUtils.read(path: path, password: _password); if (content.isEmpty) { return {}; } diff --git a/cw_ethereum/lib/ethereum_wallet.dart b/cw_ethereum/lib/ethereum_wallet.dart index dd6ec8ee1..5e082e484 100644 --- a/cw_ethereum/lib/ethereum_wallet.dart +++ b/cw_ethereum/lib/ethereum_wallet.dart @@ -15,6 +15,7 @@ import 'package:cw_core/wallet_addresses.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_ethereum/default_erc20_tokens.dart'; +import 'package:cw_ethereum/encryption_file_utils.dart'; import 'package:cw_ethereum/erc20_balance.dart'; import 'package:cw_ethereum/ethereum_client.dart'; import 'package:cw_ethereum/ethereum_exceptions.dart'; @@ -47,19 +48,25 @@ abstract class EthereumWalletBase String? mnemonic, String? privateKey, required String password, + required EncryptionFileUtils encryptionFileUtils, ERC20Balance? initialBalance, }) : syncStatus = NotConnectedSyncStatus(), _password = password, _mnemonic = mnemonic, _hexPrivateKey = privateKey, _isTransactionUpdating = false, + _encryptionFileUtils = encryptionFileUtils, _client = EthereumClient(), walletAddresses = EthereumWalletAddresses(walletInfo), balance = ObservableMap.of( {CryptoCurrency.eth: initialBalance ?? ERC20Balance(BigInt.zero)}), super(walletInfo) { this.walletInfo = walletInfo; - transactionHistory = EthereumTransactionHistory(walletInfo: walletInfo, password: password); + transactionHistory = EthereumTransactionHistory( + walletInfo: walletInfo, + password: password, + encryptionFileUtils: encryptionFileUtils, + ); if (!CakeHive.isAdapterRegistered(Erc20Token.typeId)) { CakeHive.registerAdapter(Erc20TokenAdapter()); @@ -72,6 +79,8 @@ abstract class EthereumWalletBase final String? _hexPrivateKey; final String _password; + final EncryptionFileUtils _encryptionFileUtils; + late final Box erc20TokensBox; late final EthPrivateKey _ethPrivateKey; @@ -301,7 +310,7 @@ abstract class EthereumWalletBase Future save() async { await walletAddresses.updateAddressesInBox(); final path = await makePath(); - await write(path: path, password: _password, data: toJSON()); + await _encryptionFileUtils.write(path: path, password: _password, data: toJSON()); await transactionHistory.save(); } @@ -344,9 +353,10 @@ abstract class EthereumWalletBase required String name, required String password, required WalletInfo walletInfo, + required EncryptionFileUtils encryptionFileUtils, }) async { final path = await pathForWallet(name: name, type: walletInfo.type); - final jsonSource = await read(path: path, password: password); + final jsonSource = await encryptionFileUtils.read(path: path, password: password); final data = json.decode(jsonSource) as Map; final mnemonic = data['mnemonic'] as String?; final privateKey = data['private_key'] as String?; @@ -358,6 +368,7 @@ abstract class EthereumWalletBase mnemonic: mnemonic, privateKey: privateKey, initialBalance: balance, + encryptionFileUtils: encryptionFileUtils, ); } diff --git a/cw_ethereum/lib/ethereum_wallet_service.dart b/cw_ethereum/lib/ethereum_wallet_service.dart index 16dbc0b04..289e7d25b 100644 --- a/cw_ethereum/lib/ethereum_wallet_service.dart +++ b/cw_ethereum/lib/ethereum_wallet_service.dart @@ -5,6 +5,7 @@ import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_service.dart'; import 'package:cw_core/wallet_type.dart'; +import 'package:cw_ethereum/encryption_file_utils.dart'; import 'package:cw_ethereum/ethereum_mnemonics.dart'; import 'package:cw_ethereum/ethereum_wallet.dart'; import 'package:cw_ethereum/ethereum_wallet_creation_credentials.dart'; @@ -14,9 +15,10 @@ import 'package:collection/collection.dart'; class EthereumWalletService extends WalletService { - EthereumWalletService(this.walletInfoSource); + EthereumWalletService(this.walletInfoSource, this.isDirect); final Box walletInfoSource; + final bool isDirect; @override Future create(EthereumNewWalletCredentials credentials) async { @@ -25,6 +27,7 @@ class EthereumWalletService extends WalletService remove(String wallet) async { File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true); - final walletInfo = walletInfoSource.values.firstWhereOrNull( - (info) => info.id == WalletBase.idFor(wallet, getType()))!; + final walletInfo = walletInfoSource.values + .firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!; await walletInfoSource.delete(walletInfo.key); } @@ -71,6 +75,7 @@ class EthereumWalletService extends WalletService info.id == WalletBase.idFor(currentName, getType())); final currentWallet = await EthereumWalletBase.open( - password: password, name: currentName, walletInfo: currentWalletInfo); + password: password, + name: currentName, + walletInfo: currentWalletInfo, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), + ); await currentWallet.renameWalletFiles(newName); diff --git a/cw_ethereum/pubspec.yaml b/cw_ethereum/pubspec.yaml index 5d19589f3..6b324aa7b 100644 --- a/cw_ethereum/pubspec.yaml +++ b/cw_ethereum/pubspec.yaml @@ -23,6 +23,11 @@ dependencies: shared_preferences: ^2.0.15 cw_core: path: ../cw_core + cake_backup: + git: + url: https://github.com/cake-tech/cake_backup.git + ref: main + version: 1.0.0 dev_dependencies: flutter_test: diff --git a/lib/di.dart b/lib/di.dart index 8f6bbbc44..ef3d60ebe 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -748,11 +748,9 @@ Future setup({ getIt.registerFactoryParam((WalletType param1, __) { switch (param1) { case WalletType.haven: - return haven!.createHavenWalletService( - _walletInfoSource, SettingsStoreBase.walletPasswordDirectInput); + return haven!.createHavenWalletService(_walletInfoSource); case WalletType.monero: - return monero!.createMoneroWalletService(_walletInfoSource, _unspentCoinsInfoSource, - SettingsStoreBase.walletPasswordDirectInput); + return monero!.createMoneroWalletService(_walletInfoSource, _unspentCoinsInfoSource); case WalletType.bitcoin: return bitcoin!.createBitcoinWalletService(_walletInfoSource, _unspentCoinsInfoSource!, SettingsStoreBase.walletPasswordDirectInput); diff --git a/lib/ethereum/cw_ethereum.dart b/lib/ethereum/cw_ethereum.dart index 7ad90191f..cb13b30d2 100644 --- a/lib/ethereum/cw_ethereum.dart +++ b/lib/ethereum/cw_ethereum.dart @@ -4,8 +4,8 @@ class CWEthereum extends Ethereum { @override List getEthereumWordList(String language) => EthereumMnemonics.englishWordlist; - WalletService createEthereumWalletService(Box walletInfoSource) => - EthereumWalletService(walletInfoSource); + WalletService createEthereumWalletService(Box walletInfoSource, bool isDirect) => + EthereumWalletService(walletInfoSource, isDirect); @override WalletCredentials createEthereumNewWalletCredentials({ diff --git a/tool/configure.dart b/tool/configure.dart index f95eb6277..01d805e71 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -496,7 +496,7 @@ import 'package:cw_ethereum/ethereum_transaction_priority.dart'; const ethereumContent = """ abstract class Ethereum { List getEthereumWordList(String language); - WalletService createEthereumWalletService(Box walletInfoSource); + WalletService createEthereumWalletService(Box walletInfoSource, bool isDirect); WalletCredentials createEthereumNewWalletCredentials({required String name, WalletInfo? walletInfo, String? password}); WalletCredentials createEthereumRestoreWalletFromSeedCredentials({required String name, required String mnemonic, required String password}); WalletCredentials createEthereumRestoreWalletFromPrivateKey({required String name, required String privateKey, required String password});