CW-1092-restoring-from-backup-doesnt-maintain-hardware-wallets (#2319)

* feat: add hardware wallet verification during backup restoration

* style: improve readability of verifyHardwareWallets in backup_service_v3.dart
This commit is contained in:
Konstantin Ullrich 2025-06-17 00:31:49 +02:00 committed by GitHub
parent 4fb2fc47ad
commit 85d3e727e2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 42 additions and 5 deletions

View file

@ -110,7 +110,7 @@ class $BackupService {
}
Future<void> verifyWallets() async {
final walletInfoSource = await _reloadHiveWalletInfoBox();
final walletInfoSource = await reloadHiveWalletInfoBox();
correctWallets =
walletInfoSource.values.where((info) => availableWalletTypes.contains(info.type)).toList();
@ -119,7 +119,7 @@ class $BackupService {
}
}
Future<Box<WalletInfo>> _reloadHiveWalletInfoBox() async {
Future<Box<WalletInfo>> reloadHiveWalletInfoBox() async {
final appDir = await getAppDir();
await CakeHive.close();
CakeHive.init(appDir.path);
@ -288,13 +288,15 @@ class $BackupService {
return {
'name': walletInfo.name,
'type': walletInfo.type.toString(),
'password': await keyService.getWalletPassword(walletName: walletInfo.name)
'password': await keyService.getWalletPassword(walletName: walletInfo.name),
'hardwareWalletType': walletInfo.hardwareWalletType?.index,
};
} catch (e) {
return {
'name': walletInfo.name,
'type': walletInfo.type.toString(),
'password': ''
'password': '',
'hardwareWalletType': walletInfo.hardwareWalletType?.index,
};
}
}));

View file

@ -10,6 +10,7 @@ import 'package:cake_wallet/utils/package_info.dart';
import 'package:crypto/crypto.dart';
import 'package:cw_core/root_dir.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:flutter/foundation.dart';
enum BackupVersion {
@ -305,6 +306,7 @@ class BackupServiceV3 extends $BackupService {
// Continue importing the backup the old way
await super.verifyWallets();
await verifyHardwareWallets(password);
await super.importKeychainDumpV2(password);
await super.importPreferencesDump();
await super.importTransactionDescriptionDump();
@ -313,6 +315,39 @@ class BackupServiceV3 extends $BackupService {
decryptedData.deleteSync();
}
Future<void> verifyHardwareWallets(String password,
{String keychainSalt = secrets.backupKeychainSalt}) async {
final walletInfoSource = await reloadHiveWalletInfoBox();
final appDir = await getAppDir();
final keychainDumpFile = File('${appDir.path}/~_keychain_dump');
final decryptedKeychainDumpFileData = await decryptV2(
keychainDumpFile.readAsBytesSync(), '$keychainSalt$password');
final keychainJSON = json.decode(utf8.decode(decryptedKeychainDumpFileData))
as Map<String, dynamic>;
final keychainWalletsInfo = keychainJSON['wallets'] as List;
final expectedHardwareWallets = keychainWalletsInfo
.where((e) =>
(e as Map<String, dynamic>).containsKey("hardwareWalletType") &&
e["hardwareWalletType"] != null)
.toList();
for (final expectedHardwareWallet in expectedHardwareWallets) {
final info = expectedHardwareWallet as Map<String, dynamic>;
final actualWalletInfo = walletInfoSource.values
.where((e) =>
e.name == info['name'] && e.type.toString() == info['type'])
.firstOrNull;
if (actualWalletInfo != null &&
info["hardwareWalletType"] !=
actualWalletInfo.hardwareWalletType?.index) {
actualWalletInfo.hardwareWalletType =
HardwareWalletType.values[info["hardwareWalletType"] as int];
await actualWalletInfo.save();
}
}
}
Future<File> exportBackupFileV3(String password, {String nonce = secrets.backupSalt}) async {
final metadata = BackupMetadata(
version: BackupVersion.v3,