Store additional data that will allow us to reduce the number of verification SMSs.

This commit is contained in:
Nicholas
2023-01-17 16:41:02 -05:00
committed by Cody Henthorne
parent dcf8a82c37
commit 70c6e9e60f
10 changed files with 191 additions and 2 deletions

View File

@@ -0,0 +1,11 @@
package org.thoughtcrime.securesms.absbackup
/**
* Abstracts away the implementation of pieces of data we want to hand off to various backup services.
* Here we can control precisely which data gets backed up and more importantly, what does not.
*/
interface AndroidBackupItem {
fun getKey(): String
fun getDataForBackup(): ByteArray
fun restoreData(data: ByteArray)
}

View File

@@ -0,0 +1,65 @@
package org.thoughtcrime.securesms.absbackup
import android.app.backup.BackupAgent
import android.app.backup.BackupDataInput
import android.app.backup.BackupDataOutput
import android.os.ParcelFileDescriptor
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.absbackup.backupables.KbsAuthTokens
import java.io.DataInputStream
import java.io.DataOutputStream
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.IOException
/**
* Uses the [Android Backup Service](https://developer.android.com/guide/topics/data/keyvaluebackup) and backs up everything in [items]
*/
class SignalBackupAgent : BackupAgent() {
private val items: List<AndroidBackupItem> = listOf(
KbsAuthTokens,
)
override fun onBackup(oldState: ParcelFileDescriptor?, data: BackupDataOutput, newState: ParcelFileDescriptor) {
val contentsHash = cumulativeHashCode()
if (oldState == null) {
performBackup(data)
} else {
val hash = try {
DataInputStream(FileInputStream(oldState.fileDescriptor)).use { it.readInt() }
} catch (e: IOException) {
Log.w(TAG, "No old state, may be first backup request or bug with not writing to newState at end.", e)
}
if (hash != contentsHash) {
performBackup(data)
}
}
DataOutputStream(FileOutputStream(newState.fileDescriptor)).use { it.writeInt(contentsHash) }
}
private fun performBackup(data: BackupDataOutput) {
items.forEach {
val backupData = it.getDataForBackup()
data.writeEntityHeader(it.getKey(), backupData.size)
data.writeEntityData(backupData, backupData.size)
}
}
override fun onRestore(dataInput: BackupDataInput, appVersionCode: Int, newState: ParcelFileDescriptor) {
while (dataInput.readNextHeader()) {
val buffer = ByteArray(dataInput.dataSize)
dataInput.readEntityData(buffer, 0, dataInput.dataSize)
items.find { dataInput.key == it.getKey() }?.restoreData(buffer)
}
DataOutputStream(FileOutputStream(newState.fileDescriptor)).use { it.writeInt(cumulativeHashCode()) }
}
private fun cumulativeHashCode(): Int {
return items.fold("") { acc: String, androidBackupItem: AndroidBackupItem -> acc + androidBackupItem.getDataForBackup().decodeToString() }.hashCode()
}
companion object {
private const val TAG = "SignalBackupAgent"
}
}

View File

@@ -0,0 +1,40 @@
package org.thoughtcrime.securesms.absbackup.backupables
import com.google.protobuf.InvalidProtocolBufferException
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.absbackup.AndroidBackupItem
import org.thoughtcrime.securesms.absbackup.ExternalBackupProtos
import org.thoughtcrime.securesms.keyvalue.SignalStore
/**
* This backs up the not-secret KBS Auth tokens, which can be combined with a PIN to prove ownership of a phone number in order to complete the registration process.
*/
object KbsAuthTokens : AndroidBackupItem {
private const val TAG = "KbsAuthTokens"
override fun getKey(): String {
return TAG
}
override fun getDataForBackup(): ByteArray {
val registrationRecoveryTokenList = SignalStore.kbsValues().kbsAuthTokenList
val proto = ExternalBackupProtos.KbsAuthToken.newBuilder()
.addAllToken(registrationRecoveryTokenList)
.build()
return proto.toByteArray()
}
override fun restoreData(data: ByteArray) {
if (SignalStore.kbsValues().kbsAuthTokenList.isNotEmpty()) {
return
}
try {
val proto = ExternalBackupProtos.KbsAuthToken.parseFrom(data)
SignalStore.kbsValues().putAuthTokenList(proto.tokenList)
} catch (e: InvalidProtocolBufferException) {
Log.w(TAG, "Cannot restore KbsAuthToken from backup service.")
}
}
}