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.
This commit is contained in:
Torsten Grote 2021-06-22 10:20:04 -03:00 committed by Chirayu Desai
parent 347d2a316f
commit 1ff8e54748
5 changed files with 36 additions and 19 deletions

View file

@ -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()) }

View file

@ -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()

View file

@ -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.
*/

View file

@ -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()

View file

@ -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<CharSequence> 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)
}
}
}