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 '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<EthereumTransactionInfo> 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<String, EthereumTransactionInfo>();
}
final WalletInfo walletInfo;
final EncryptionFileUtils encryptionFileUtils;
String _password;
Future<void> 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<Map<String, dynamic>> _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 {};
}

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_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<CryptoCurrency, ERC20Balance>.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<Erc20Token> erc20TokensBox;
late final EthPrivateKey _ethPrivateKey;
@ -301,7 +310,7 @@ abstract class EthereumWalletBase
Future<void> 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,
);
}

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_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<EthereumNewWalletCredentials,
EthereumRestoreWalletFromSeedCredentials, EthereumRestoreWalletFromPrivateKey> {
EthereumWalletService(this.walletInfoSource);
EthereumWalletService(this.walletInfoSource, this.isDirect);
final Box<WalletInfo> walletInfoSource;
final bool isDirect;
@override
Future<EthereumWallet> create(EthereumNewWalletCredentials credentials) async {
@ -25,6 +27,7 @@ class EthereumWalletService extends WalletService<EthereumNewWalletCredentials,
walletInfo: credentials.walletInfo!,
mnemonic: mnemonic,
password: credentials.password!,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.init();
@ -49,6 +52,7 @@ class EthereumWalletService extends WalletService<EthereumNewWalletCredentials,
name: name,
password: password,
walletInfo: walletInfo,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.init();
@ -60,8 +64,8 @@ class EthereumWalletService extends WalletService<EthereumNewWalletCredentials,
@override
Future<void> 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<EthereumNewWalletCredentials,
password: credentials.password!,
privateKey: credentials.privateKey,
walletInfo: credentials.walletInfo!,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.init();
@ -91,6 +96,7 @@ class EthereumWalletService extends WalletService<EthereumNewWalletCredentials,
password: credentials.password!,
mnemonic: credentials.mnemonic,
walletInfo: credentials.walletInfo!,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.init();
@ -105,7 +111,11 @@ class EthereumWalletService extends WalletService<EthereumNewWalletCredentials,
final currentWalletInfo = walletInfoSource.values
.firstWhere((info) => 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);

View file

@ -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:

View file

@ -748,11 +748,9 @@ Future<void> setup({
getIt.registerFactoryParam<WalletService, WalletType, void>((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);

View file

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

View file

@ -496,7 +496,7 @@ import 'package:cw_ethereum/ethereum_transaction_priority.dart';
const ethereumContent = """
abstract class Ethereum {
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 createEthereumRestoreWalletFromSeedCredentials({required String name, required String mnemonic, required String password});
WalletCredentials createEthereumRestoreWalletFromPrivateKey({required String name, required String privateKey, required String password});