From 1ff8e54748d2c3037d1b3559358f832ebbf7f979 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Tue, 22 Jun 2021 10:20:04 -0300 Subject: [PATCH] Re-initialize backup location when changing recovery code * delete all storage backups for current user * clears the storage backup cache * start a new app data restore set and initializes it The reason is that old backups won't be readable anymore with the new key. We also can't delete other backups safely as we did before, because we can't be sure that they don't belong to a different device or user. --- .../java/com/stevesoltys/seedvault/App.kt | 2 +- .../saf/DocumentsProviderBackupPlugin.kt | 5 --- .../transport/backup/BackupPlugin.kt | 6 --- .../recoverycode/RecoveryCodeInputFragment.kt | 2 +- .../ui/recoverycode/RecoveryCodeViewModel.kt | 40 ++++++++++++++++--- 5 files changed, 36 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/com/stevesoltys/seedvault/App.kt b/app/src/main/java/com/stevesoltys/seedvault/App.kt index 6aaeeb26..a443b3bc 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/App.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/App.kt @@ -46,7 +46,7 @@ open class App : Application() { factory { AppListRetriever(this@App, get(), get(), get()) } viewModel { SettingsViewModel(this@App, get(), get(), get(), get(), get(), get()) } - viewModel { RecoveryCodeViewModel(this@App, get(), get(), get()) } + viewModel { RecoveryCodeViewModel(this@App, get(), get(), get(), get(), get()) } viewModel { BackupStorageViewModel(this@App, get(), get(), get(), get()) } viewModel { RestoreStorageViewModel(this@App, get(), get()) } viewModel { RestoreViewModel(this@App, get(), get(), get(), get(), get(), get()) } diff --git a/app/src/main/java/com/stevesoltys/seedvault/plugins/saf/DocumentsProviderBackupPlugin.kt b/app/src/main/java/com/stevesoltys/seedvault/plugins/saf/DocumentsProviderBackupPlugin.kt index 9789b434..c71d4509 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/plugins/saf/DocumentsProviderBackupPlugin.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/plugins/saf/DocumentsProviderBackupPlugin.kt @@ -44,11 +44,6 @@ internal class DocumentsProviderBackupPlugin( storage.currentFullBackupDir ?: throw IOException() } - @Throws(IOException::class) - override suspend fun deleteAllBackups() { - storage.rootBackupDir?.deleteContents(context) - } - @Throws(IOException::class) override suspend fun getMetadataOutputStream(): OutputStream { val setDir = storage.getSetDir() ?: throw IOException() diff --git a/app/src/main/java/com/stevesoltys/seedvault/transport/backup/BackupPlugin.kt b/app/src/main/java/com/stevesoltys/seedvault/transport/backup/BackupPlugin.kt index 5a08a63a..f8672503 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/transport/backup/BackupPlugin.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/transport/backup/BackupPlugin.kt @@ -25,12 +25,6 @@ interface BackupPlugin { @Throws(IOException::class) suspend fun initializeDevice() - /** - * Delete all existing [RestoreSet]s from the storage medium. - */ - @Throws(IOException::class) - suspend fun deleteAllBackups() - /** * Returns an [OutputStream] for writing backup metadata. */ diff --git a/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeInputFragment.kt b/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeInputFragment.kt index 1bde12c7..4b707b84 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeInputFragment.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeInputFragment.kt @@ -192,7 +192,7 @@ class RecoveryCodeInputFragment : Fragment() { private val regenRequest = registerForActivityResult(StartActivityForResult()) { if (it.resultCode == RESULT_OK) { - viewModel.deleteAllBackup() + viewModel.reinitializeBackupLocation() parentFragmentManager.popBackStack() Snackbar.make(requireView(), R.string.recovery_code_recreated, Snackbar.LENGTH_LONG) .show() diff --git a/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeViewModel.kt b/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeViewModel.kt index c3069475..3b8c6173 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeViewModel.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeViewModel.kt @@ -1,11 +1,14 @@ package com.stevesoltys.seedvault.ui.recoverycode +import android.app.backup.IBackupManager +import android.os.UserHandle import android.util.Log import androidx.lifecycle.AndroidViewModel import com.stevesoltys.seedvault.App import com.stevesoltys.seedvault.crypto.Crypto import com.stevesoltys.seedvault.crypto.KeyManager -import com.stevesoltys.seedvault.transport.backup.BackupPlugin +import com.stevesoltys.seedvault.transport.TRANSPORT_ID +import com.stevesoltys.seedvault.transport.backup.BackupCoordinator import com.stevesoltys.seedvault.ui.LiveEvent import com.stevesoltys.seedvault.ui.MutableLiveEvent import io.github.novacrypto.bip39.JavaxPBKDF2WithHmacSHA512 @@ -21,6 +24,7 @@ import io.github.novacrypto.bip39.wordlists.English import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch +import org.calyxos.backup.storage.api.StorageBackup import java.io.IOException import java.security.SecureRandom import java.util.ArrayList @@ -28,11 +32,15 @@ import java.util.ArrayList internal const val WORD_NUM = 12 internal const val WORD_LIST_SIZE = 2048 -class RecoveryCodeViewModel( +private val TAG = RecoveryCodeViewModel::class.java.simpleName + +internal class RecoveryCodeViewModel( app: App, private val crypto: Crypto, private val keyManager: KeyManager, - private val backupPlugin: BackupPlugin + private val backupManager: IBackupManager, + private val backupCoordinator: BackupCoordinator, + private val storageBackup: StorageBackup ) : AndroidViewModel(app) { internal val wordList: List by lazy { @@ -79,12 +87,32 @@ class RecoveryCodeViewModel( } } - fun deleteAllBackup() { + /** + * Deletes all storage backups for current user and clears the storage backup cache. + * Also starts a new app data restore set and initializes it. + * + * The reason is that old backups won't be readable anymore with the new key. + * We can't delete other backups safely, because we can't be sure + * that they don't belong to a different device or user. + */ + fun reinitializeBackupLocation() { + Log.d(TAG, "Re-initializing backup location...") GlobalScope.launch(Dispatchers.IO) { + // remove old storage snapshots and clear cache + storageBackup.deleteAllSnapshots() + storageBackup.clearCache() try { - backupPlugin.deleteAllBackups() + // will also generate a new backup token for the new restore set + backupCoordinator.startNewRestoreSet() + + // initialize the new location + backupManager.initializeTransportsForUser( + UserHandle.myUserId(), + arrayOf(TRANSPORT_ID), + null + ) } catch (e: IOException) { - Log.e("RecoveryCodeViewModel", "Error deleting backups", e) + Log.e(TAG, "Error starting new RestoreSet", e) } } }