From 85d3e727e20203b411e3225396f6ca00d280f18c Mon Sep 17 00:00:00 2001 From: Konstantin Ullrich Date: Tue, 17 Jun 2025 00:31:49 +0200 Subject: [PATCH] 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 --- lib/core/backup_service.dart | 10 +++++---- lib/core/backup_service_v3.dart | 37 ++++++++++++++++++++++++++++++++- 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/lib/core/backup_service.dart b/lib/core/backup_service.dart index 2e8523024..87bb71ce9 100644 --- a/lib/core/backup_service.dart +++ b/lib/core/backup_service.dart @@ -110,7 +110,7 @@ class $BackupService { } Future 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> _reloadHiveWalletInfoBox() async { + Future> 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, }; } })); diff --git a/lib/core/backup_service_v3.dart b/lib/core/backup_service_v3.dart index be678b679..a0640dfd3 100644 --- a/lib/core/backup_service_v3.dart +++ b/lib/core/backup_service_v3.dart @@ -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 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; + final keychainWalletsInfo = keychainJSON['wallets'] as List; + + final expectedHardwareWallets = keychainWalletsInfo + .where((e) => + (e as Map).containsKey("hardwareWalletType") && + e["hardwareWalletType"] != null) + .toList(); + + for (final expectedHardwareWallet in expectedHardwareWallets) { + final info = expectedHardwareWallet as Map; + 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 exportBackupFileV3(String password, {String nonce = secrets.backupSalt}) async { final metadata = BackupMetadata( version: BackupVersion.v3, @@ -467,4 +502,4 @@ This backup was created on ${DateTime.now().toIso8601String()} file.writeAsBytesSync(data); return file; } -} \ No newline at end of file +}