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 a0a8e41d3..cce2687c2 100644 --- a/cw_ethereum/lib/ethereum_wallet.dart +++ b/cw_ethereum/lib/ethereum_wallet.dart @@ -14,6 +14,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'; @@ -45,18 +46,24 @@ abstract class EthereumWalletBase required WalletInfo walletInfo, required String mnemonic, required String password, + required EncryptionFileUtils encryptionFileUtils, ERC20Balance? initialBalance, }) : syncStatus = NotConnectedSyncStatus(), _password = password, _mnemonic = mnemonic, _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 (!Hive.isAdapterRegistered(Erc20Token.typeId)) { Hive.registerAdapter(Erc20TokenAdapter()); @@ -68,6 +75,8 @@ abstract class EthereumWalletBase final String _mnemonic; final String _password; + final EncryptionFileUtils _encryptionFileUtils; + late final Box erc20TokensBox; late final EthPrivateKey _privateKey; @@ -282,7 +291,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(); } @@ -321,9 +330,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 balance = ERC20Balance.fromJSON(data['balance'] as String) ?? ERC20Balance(BigInt.zero); @@ -333,6 +343,7 @@ abstract class EthereumWalletBase password: password, mnemonic: mnemonic, initialBalance: balance, + encryptionFileUtils: encryptionFileUtils, ); } diff --git a/cw_ethereum/lib/ethereum_wallet_service.dart b/cw_ethereum/lib/ethereum_wallet_service.dart index 318f287fc..9ff44276b 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); } @@ -81,6 +85,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 cb1046d5a..a6fd321bb 100644 --- a/cw_ethereum/pubspec.yaml +++ b/cw_ethereum/pubspec.yaml @@ -22,6 +22,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 22a3139b9..1c81f33fc 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -768,7 +768,9 @@ Future setup({ _walletInfoSource, _unspentCoinsInfoSource!, SettingsStoreBase.walletPasswordDirectInput); case WalletType.ethereum: - return ethereum!.createEthereumWalletService(_walletInfoSource); + return ethereum!.createEthereumWalletService( + _walletInfoSource, + SettingsStoreBase.walletPasswordDirectInput); default: throw Exception('Unexpected token: ${param1.toString()} for generating of WalletService'); } diff --git a/lib/ethereum/cw_ethereum.dart b/lib/ethereum/cw_ethereum.dart index 0010cb21e..3c819ebc9 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 d38b8b95b..e84975fd2 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -507,7 +507,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}); String getAddress(WalletBase wallet);