Add Encryption file utils to Ethereum (#1025)

This commit is contained in:
Omar Hatem 2023-08-08 16:58:07 +03:00 committed by OmarHatem
parent 74b5ee315a
commit bdb3ec2048
8 changed files with 89 additions and 19 deletions

View file

@ -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<void> write({required String path, required String password, required String data});
Future<String> read({required String path, required String password});
}
class Salsa20EncryhptionFileUtils extends EncryptionFileUtils {
// Requires legacy complex key + iv as password
@override
Future<void> 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<String> read({required String path, required String password}) async
=> await ef.read(path: path, password: password);
}
class XChaCha20EncryptionFileUtils extends EncryptionFileUtils {
@override
Future<void> 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<String> 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);
}
}

View file

@ -2,7 +2,7 @@ import 'dart:convert';
import 'dart:core'; import 'dart:core';
import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/wallet_info.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:mobx/mobx.dart';
import 'package:cw_core/transaction_history.dart'; import 'package:cw_core/transaction_history.dart';
import 'package:cw_ethereum/ethereum_transaction_info.dart'; import 'package:cw_ethereum/ethereum_transaction_info.dart';
@ -15,12 +15,16 @@ class EthereumTransactionHistory = EthereumTransactionHistoryBase with _$Ethereu
abstract class EthereumTransactionHistoryBase abstract class EthereumTransactionHistoryBase
extends TransactionHistoryBase<EthereumTransactionInfo> with Store { extends TransactionHistoryBase<EthereumTransactionInfo> with Store {
EthereumTransactionHistoryBase({required this.walletInfo, required String password}) EthereumTransactionHistoryBase({
: _password = password { required this.walletInfo,
required String password,
required this.encryptionFileUtils,
}) : _password = password {
transactions = ObservableMap<String, EthereumTransactionInfo>(); transactions = ObservableMap<String, EthereumTransactionInfo>();
} }
final WalletInfo walletInfo; final WalletInfo walletInfo;
final EncryptionFileUtils encryptionFileUtils;
String _password; String _password;
Future<void> init() async => await _load(); Future<void> init() async => await _load();
@ -31,7 +35,7 @@ abstract class EthereumTransactionHistoryBase
final dirPath = await pathForWalletDir(name: walletInfo.name, type: walletInfo.type); final dirPath = await pathForWalletDir(name: walletInfo.name, type: walletInfo.type);
final path = '$dirPath/$transactionsHistoryFileName'; final path = '$dirPath/$transactionsHistoryFileName';
final data = json.encode({'transactions': transactions}); 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) { } catch (e, s) {
print('Error while save ethereum transaction history: ${e.toString()}'); print('Error while save ethereum transaction history: ${e.toString()}');
print(s); print(s);
@ -48,7 +52,7 @@ abstract class EthereumTransactionHistoryBase
Future<Map<String, dynamic>> _read() async { Future<Map<String, dynamic>> _read() async {
final dirPath = await pathForWalletDir(name: walletInfo.name, type: walletInfo.type); final dirPath = await pathForWalletDir(name: walletInfo.name, type: walletInfo.type);
final path = '$dirPath/$transactionsHistoryFileName'; final path = '$dirPath/$transactionsHistoryFileName';
final content = await read(path: path, password: _password); final content = await encryptionFileUtils.read(path: path, password: _password);
if (content.isEmpty) { if (content.isEmpty) {
return {}; return {};
} }

View file

@ -15,6 +15,7 @@ import 'package:cw_core/wallet_addresses.dart';
import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_info.dart';
import 'package:cw_ethereum/default_erc20_tokens.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/erc20_balance.dart';
import 'package:cw_ethereum/ethereum_client.dart'; import 'package:cw_ethereum/ethereum_client.dart';
import 'package:cw_ethereum/ethereum_exceptions.dart'; import 'package:cw_ethereum/ethereum_exceptions.dart';
@ -47,19 +48,25 @@ abstract class EthereumWalletBase
String? mnemonic, String? mnemonic,
String? privateKey, String? privateKey,
required String password, required String password,
required EncryptionFileUtils encryptionFileUtils,
ERC20Balance? initialBalance, ERC20Balance? initialBalance,
}) : syncStatus = NotConnectedSyncStatus(), }) : syncStatus = NotConnectedSyncStatus(),
_password = password, _password = password,
_mnemonic = mnemonic, _mnemonic = mnemonic,
_hexPrivateKey = privateKey, _hexPrivateKey = privateKey,
_isTransactionUpdating = false, _isTransactionUpdating = false,
_encryptionFileUtils = encryptionFileUtils,
_client = EthereumClient(), _client = EthereumClient(),
walletAddresses = EthereumWalletAddresses(walletInfo), walletAddresses = EthereumWalletAddresses(walletInfo),
balance = ObservableMap<CryptoCurrency, ERC20Balance>.of( balance = ObservableMap<CryptoCurrency, ERC20Balance>.of(
{CryptoCurrency.eth: initialBalance ?? ERC20Balance(BigInt.zero)}), {CryptoCurrency.eth: initialBalance ?? ERC20Balance(BigInt.zero)}),
super(walletInfo) { super(walletInfo) {
this.walletInfo = walletInfo; this.walletInfo = walletInfo;
transactionHistory = EthereumTransactionHistory(walletInfo: walletInfo, password: password); transactionHistory = EthereumTransactionHistory(
walletInfo: walletInfo,
password: password,
encryptionFileUtils: encryptionFileUtils,
);
if (!CakeHive.isAdapterRegistered(Erc20Token.typeId)) { if (!CakeHive.isAdapterRegistered(Erc20Token.typeId)) {
CakeHive.registerAdapter(Erc20TokenAdapter()); CakeHive.registerAdapter(Erc20TokenAdapter());
@ -72,6 +79,8 @@ abstract class EthereumWalletBase
final String? _hexPrivateKey; final String? _hexPrivateKey;
final String _password; final String _password;
final EncryptionFileUtils _encryptionFileUtils;
late final Box<Erc20Token> erc20TokensBox; late final Box<Erc20Token> erc20TokensBox;
late final EthPrivateKey _ethPrivateKey; late final EthPrivateKey _ethPrivateKey;
@ -301,7 +310,7 @@ abstract class EthereumWalletBase
Future<void> save() async { Future<void> save() async {
await walletAddresses.updateAddressesInBox(); await walletAddresses.updateAddressesInBox();
final path = await makePath(); final path = await makePath();
await write(path: path, password: _password, data: toJSON()); await _encryptionFileUtils.write(path: path, password: _password, data: toJSON());
await transactionHistory.save(); await transactionHistory.save();
} }
@ -344,9 +353,10 @@ abstract class EthereumWalletBase
required String name, required String name,
required String password, required String password,
required WalletInfo walletInfo, required WalletInfo walletInfo,
required EncryptionFileUtils encryptionFileUtils,
}) async { }) async {
final path = await pathForWallet(name: name, type: walletInfo.type); 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 data = json.decode(jsonSource) as Map;
final mnemonic = data['mnemonic'] as String?; final mnemonic = data['mnemonic'] as String?;
final privateKey = data['private_key'] as String?; final privateKey = data['private_key'] as String?;
@ -358,6 +368,7 @@ abstract class EthereumWalletBase
mnemonic: mnemonic, mnemonic: mnemonic,
privateKey: privateKey, privateKey: privateKey,
initialBalance: balance, initialBalance: balance,
encryptionFileUtils: encryptionFileUtils,
); );
} }

View file

@ -5,6 +5,7 @@ import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_service.dart'; import 'package:cw_core/wallet_service.dart';
import 'package:cw_core/wallet_type.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_mnemonics.dart';
import 'package:cw_ethereum/ethereum_wallet.dart'; import 'package:cw_ethereum/ethereum_wallet.dart';
import 'package:cw_ethereum/ethereum_wallet_creation_credentials.dart'; import 'package:cw_ethereum/ethereum_wallet_creation_credentials.dart';
@ -14,9 +15,10 @@ import 'package:collection/collection.dart';
class EthereumWalletService extends WalletService<EthereumNewWalletCredentials, class EthereumWalletService extends WalletService<EthereumNewWalletCredentials,
EthereumRestoreWalletFromSeedCredentials, EthereumRestoreWalletFromPrivateKey> { EthereumRestoreWalletFromSeedCredentials, EthereumRestoreWalletFromPrivateKey> {
EthereumWalletService(this.walletInfoSource); EthereumWalletService(this.walletInfoSource, this.isDirect);
final Box<WalletInfo> walletInfoSource; final Box<WalletInfo> walletInfoSource;
final bool isDirect;
@override @override
Future<EthereumWallet> create(EthereumNewWalletCredentials credentials) async { Future<EthereumWallet> create(EthereumNewWalletCredentials credentials) async {
@ -25,6 +27,7 @@ class EthereumWalletService extends WalletService<EthereumNewWalletCredentials,
walletInfo: credentials.walletInfo!, walletInfo: credentials.walletInfo!,
mnemonic: mnemonic, mnemonic: mnemonic,
password: credentials.password!, password: credentials.password!,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
); );
await wallet.init(); await wallet.init();
@ -49,6 +52,7 @@ class EthereumWalletService extends WalletService<EthereumNewWalletCredentials,
name: name, name: name,
password: password, password: password,
walletInfo: walletInfo, walletInfo: walletInfo,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
); );
await wallet.init(); await wallet.init();
@ -60,8 +64,8 @@ class EthereumWalletService extends WalletService<EthereumNewWalletCredentials,
@override @override
Future<void> remove(String wallet) async { Future<void> remove(String wallet) async {
File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true); File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true);
final walletInfo = walletInfoSource.values.firstWhereOrNull( final walletInfo = walletInfoSource.values
(info) => info.id == WalletBase.idFor(wallet, getType()))!; .firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
await walletInfoSource.delete(walletInfo.key); await walletInfoSource.delete(walletInfo.key);
} }
@ -71,6 +75,7 @@ class EthereumWalletService extends WalletService<EthereumNewWalletCredentials,
password: credentials.password!, password: credentials.password!,
privateKey: credentials.privateKey, privateKey: credentials.privateKey,
walletInfo: credentials.walletInfo!, walletInfo: credentials.walletInfo!,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
); );
await wallet.init(); await wallet.init();
@ -91,6 +96,7 @@ class EthereumWalletService extends WalletService<EthereumNewWalletCredentials,
password: credentials.password!, password: credentials.password!,
mnemonic: credentials.mnemonic, mnemonic: credentials.mnemonic,
walletInfo: credentials.walletInfo!, walletInfo: credentials.walletInfo!,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
); );
await wallet.init(); await wallet.init();
@ -105,7 +111,11 @@ class EthereumWalletService extends WalletService<EthereumNewWalletCredentials,
final currentWalletInfo = walletInfoSource.values final currentWalletInfo = walletInfoSource.values
.firstWhere((info) => info.id == WalletBase.idFor(currentName, getType())); .firstWhere((info) => info.id == WalletBase.idFor(currentName, getType()));
final currentWallet = await EthereumWalletBase.open( final currentWallet = await EthereumWalletBase.open(
password: password, name: currentName, walletInfo: currentWalletInfo); password: password,
name: currentName,
walletInfo: currentWalletInfo,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await currentWallet.renameWalletFiles(newName); await currentWallet.renameWalletFiles(newName);

View file

@ -23,6 +23,11 @@ dependencies:
shared_preferences: ^2.0.15 shared_preferences: ^2.0.15
cw_core: cw_core:
path: ../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: dev_dependencies:
flutter_test: flutter_test:

View file

@ -748,11 +748,9 @@ Future<void> setup({
getIt.registerFactoryParam<WalletService, WalletType, void>((WalletType param1, __) { getIt.registerFactoryParam<WalletService, WalletType, void>((WalletType param1, __) {
switch (param1) { switch (param1) {
case WalletType.haven: case WalletType.haven:
return haven!.createHavenWalletService( return haven!.createHavenWalletService(_walletInfoSource);
_walletInfoSource, SettingsStoreBase.walletPasswordDirectInput);
case WalletType.monero: case WalletType.monero:
return monero!.createMoneroWalletService(_walletInfoSource, _unspentCoinsInfoSource, return monero!.createMoneroWalletService(_walletInfoSource, _unspentCoinsInfoSource);
SettingsStoreBase.walletPasswordDirectInput);
case WalletType.bitcoin: case WalletType.bitcoin:
return bitcoin!.createBitcoinWalletService(_walletInfoSource, _unspentCoinsInfoSource!, return bitcoin!.createBitcoinWalletService(_walletInfoSource, _unspentCoinsInfoSource!,
SettingsStoreBase.walletPasswordDirectInput); SettingsStoreBase.walletPasswordDirectInput);

View file

@ -4,8 +4,8 @@ class CWEthereum extends Ethereum {
@override @override
List<String> getEthereumWordList(String language) => EthereumMnemonics.englishWordlist; List<String> getEthereumWordList(String language) => EthereumMnemonics.englishWordlist;
WalletService createEthereumWalletService(Box<WalletInfo> walletInfoSource) => WalletService createEthereumWalletService(Box<WalletInfo> walletInfoSource, bool isDirect) =>
EthereumWalletService(walletInfoSource); EthereumWalletService(walletInfoSource, isDirect);
@override @override
WalletCredentials createEthereumNewWalletCredentials({ WalletCredentials createEthereumNewWalletCredentials({

View file

@ -496,7 +496,7 @@ import 'package:cw_ethereum/ethereum_transaction_priority.dart';
const ethereumContent = """ const ethereumContent = """
abstract class Ethereum { abstract class Ethereum {
List<String> getEthereumWordList(String language); List<String> getEthereumWordList(String language);
WalletService createEthereumWalletService(Box<WalletInfo> walletInfoSource); WalletService createEthereumWalletService(Box<WalletInfo> walletInfoSource, bool isDirect);
WalletCredentials createEthereumNewWalletCredentials({required String name, WalletInfo? walletInfo, String? password}); WalletCredentials createEthereumNewWalletCredentials({required String name, WalletInfo? walletInfo, String? password});
WalletCredentials createEthereumRestoreWalletFromSeedCredentials({required String name, required String mnemonic, required String password}); WalletCredentials createEthereumRestoreWalletFromSeedCredentials({required String name, required String mnemonic, required String password});
WalletCredentials createEthereumRestoreWalletFromPrivateKey({required String name, required String privateKey, required String password}); WalletCredentials createEthereumRestoreWalletFromPrivateKey({required String name, required String privateKey, required String password});