cleaner backup changes

This commit is contained in:
fosse 2023-12-15 12:12:49 -05:00
parent 41bf68c886
commit e699cb0335

View file

@ -28,11 +28,10 @@ class BackupService {
: _cipher = Cryptography.instance.chacha20Poly1305Aead(),
_correctWallets = <WalletInfo>[];
static const currentVersion = _v3;
static const currentVersion = _v2;
static const _v1 = 1;
static const _v2 = 2;
static const _v3 = 3;
final Cipher _cipher;
final FlutterSecureStorage _flutterSecureStorage;
@ -54,9 +53,6 @@ class BackupService {
case _v2:
await _importBackupV2(data, password);
break;
case _v3:
await _importBackupV3(data, password);
break;
default:
break;
}
@ -69,8 +65,6 @@ class BackupService {
return await _exportBackupV1(password, nonce: nonce);
case _v2:
return await _exportBackupV2(password);
case _v3:
return await _exportBackupV3(password);
default:
throw Exception('Incorrect version: $version for exportBackup');
}
@ -80,16 +74,14 @@ class BackupService {
Future<Uint8List> _exportBackupV1(String password, {String nonce = secrets.backupSalt}) async =>
throw Exception('Deprecated. Export for backups v1 is deprecated. Please use export v2.');
Future<Uint8List> _exportBackupV2(String password, {bool keychainV3 = false}) async {
Future<Uint8List> _exportBackupV2(String password) async {
final zipEncoder = ZipFileEncoder();
final appDir = await getApplicationDocumentsDirectory();
final now = DateTime.now();
final tmpDir = Directory('${appDir.path}/~_BACKUP_TMP');
final archivePath = '${tmpDir.path}/backup_${now.toString()}.zip';
final fileEntities = appDir.listSync(recursive: false);
final keychainDump = keychainV3
? (await _exportKeychainDumpV2(password))
: (await _exportKeychainDumpV3(password));
final keychainDump = await _exportKeychainDumpV2(password);
final preferencesDump = await _exportPreferencesJSON();
final preferencesDumpFile = File('${tmpDir.path}/~_preferences_dump_TMP');
final keychainDumpFile = File('${tmpDir.path}/~_keychain_dump_TMP');
@ -123,10 +115,6 @@ class BackupService {
return await _encryptV2(content, password);
}
Future<Uint8List> _exportBackupV3(String password) async {
return await _exportBackupV2(password, keychainV3: true);
}
Future<void> _importBackupV1(Uint8List data, String password, {required String nonce}) async {
final appDir = await getApplicationDocumentsDirectory();
final decryptedData = await _decryptV1(data, password, nonce);
@ -150,7 +138,7 @@ class BackupService {
await _importPreferencesDump();
}
Future<void> _importBackupV2(Uint8List data, String password, {bool keychainV3 = false}) async {
Future<void> _importBackupV2(Uint8List data, String password) async {
final appDir = await getApplicationDocumentsDirectory();
final decryptedData = await _decryptV2(data, password);
final zip = ZipDecoder().decodeBytes(decryptedData);
@ -169,18 +157,10 @@ class BackupService {
});
await _verifyWallets();
if (keychainV3) {
await _importKeychainDumpV3(password);
} else {
await _importKeychainDumpV2(password);
}
await _importKeychainDumpV2(password);
await _importPreferencesDump();
}
Future<void> _importBackupV3(Uint8List data, String password) async {
await _importBackupV2(data, password, keychainV3: true);
}
Future<void> _verifyWallets() async {
final walletInfoSource = await _reloadHiveWalletInfoBox();
_correctWallets =
@ -465,6 +445,7 @@ class BackupService {
});
await _flutterSecureStorage.delete(key: pinCodeKey);
// we know it's the old pin format since it's a v1 import:
await _flutterSecureStorage.write(
key: pinCodeKey, value: (await argon2Hash(password: decodedPin)));
@ -480,7 +461,7 @@ class BackupService {
final keychainJSON =
json.decode(utf8.decode(decryptedKeychainDumpFileData)) as Map<String, dynamic>;
final keychainWalletsInfo = keychainJSON['wallets'] as List;
final decodedPin = keychainJSON['pin'] as String;
final potentiallyDecodedPin = keychainJSON['pin'] as String;
final pinCodeKey = generateStoreKeyFor(key: SecretStoreKey.pinCodePassword);
final backupPasswordKey = generateStoreKeyFor(key: SecretStoreKey.backupPassword);
final backupPassword = keychainJSON[backupPasswordKey] as String;
@ -493,34 +474,13 @@ class BackupService {
await importWalletKeychainInfo(info);
});
await _flutterSecureStorage.delete(key: pinCodeKey);
await _flutterSecureStorage.write(
key: pinCodeKey, value: (await argon2Hash(password: decodedPin)));
keychainDumpFile.deleteSync();
}
Future<void> _importKeychainDumpV3(String password,
{String keychainSalt = secrets.backupKeychainSalt}) async {
final appDir = await getApplicationDocumentsDirectory();
final keychainDumpFile = File('${appDir.path}/~_keychain_dump');
final decryptedKeychainDumpFileData =
await _decryptV3(keychainDumpFile.readAsBytesSync(), '$keychainSalt$password');
final keychainJSON =
json.decode(utf8.decode(decryptedKeychainDumpFileData)) as Map<String, dynamic>;
final keychainWalletsInfo = keychainJSON['wallets'] as List;
final encodedPin = keychainJSON['pin'] as String;
final pinCodeKey = generateStoreKeyFor(key: SecretStoreKey.pinCodePassword);
final backupPasswordKey = generateStoreKeyFor(key: SecretStoreKey.backupPassword);
final backupPassword = keychainJSON[backupPasswordKey] as String;
await _flutterSecureStorage.delete(key: backupPasswordKey);
await _flutterSecureStorage.write(key: backupPasswordKey, value: backupPassword);
keychainWalletsInfo.forEach((dynamic rawInfo) async {
final info = rawInfo as Map<String, dynamic>;
await importWalletKeychainInfo(info);
});
late String encodedPin;
// not a formal check but an argon2 hash is always going to be > 10 characters long (and decoded pins either 4 or 6)
if (potentiallyDecodedPin.length < 10) {
encodedPin = await argon2Hash(password: potentiallyDecodedPin);
} else {
encodedPin = potentiallyDecodedPin;
}
await _flutterSecureStorage.delete(key: pinCodeKey);
await _flutterSecureStorage.write(key: pinCodeKey, value: encodedPin);
@ -561,26 +521,6 @@ class BackupService {
return encrypted;
}
Future<Uint8List> _exportKeychainDumpV3(String password,
{String keychainSalt = secrets.backupKeychainSalt}) async {
final key = generateStoreKeyFor(key: SecretStoreKey.pinCodePassword);
final encodedPin = await _flutterSecureStorage.read(key: key);
final wallets = await Future.wait(_walletInfoSource.values.map((walletInfo) async {
return {
'name': walletInfo.name,
'type': walletInfo.type.toString(),
'password': await _keyService.getWalletPassword(walletName: walletInfo.name)
};
}));
final backupPasswordKey = generateStoreKeyFor(key: SecretStoreKey.backupPassword);
final backupPassword = await _flutterSecureStorage.read(key: backupPasswordKey);
final data = utf8.encode(
json.encode({'pin': encodedPin, 'wallets': wallets, backupPasswordKey: backupPassword}));
final encrypted = await _encryptV3(Uint8List.fromList(data), '$keychainSalt$password');
return encrypted;
}
Future<String> _exportPreferencesJSON() async {
final preferences = <String, dynamic>{
PreferencesKey.currentWalletName:
@ -689,10 +629,4 @@ class BackupService {
Future<Uint8List> _decryptV2(Uint8List data, String passphrase) async =>
cake_backup.decrypt(passphrase, data);
Future<Uint8List> _encryptV3(Uint8List data, String passphrase) async =>
cake_backup.encrypt(passphrase, data, version: _v3);
Future<Uint8List> _decryptV3(Uint8List data, String passphrase) async =>
cake_backup.decrypt(passphrase, data);
}