Add initial SVRB support.

This commit is contained in:
Greyson Parrelli
2025-08-01 16:33:57 -04:00
committed by Cody Henthorne
parent f6ab408fc8
commit 5aeca1deb1
29 changed files with 763 additions and 185 deletions

View File

@@ -45,6 +45,7 @@ import org.thoughtcrime.securesms.backup.v2.local.ArchiveResult
import org.thoughtcrime.securesms.backup.v2.local.LocalArchiver
import org.thoughtcrime.securesms.backup.v2.local.LocalArchiver.FailureCause
import org.thoughtcrime.securesms.backup.v2.local.SnapshotFileSystem
import org.thoughtcrime.securesms.backup.v2.stream.EncryptedBackupReader
import org.thoughtcrime.securesms.backup.v2.stream.EncryptedBackupReader.Companion.MAC_SIZE
import org.thoughtcrime.securesms.database.AttachmentTable
import org.thoughtcrime.securesms.database.AttachmentTable.DebugAttachmentStats
@@ -53,11 +54,13 @@ import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.thoughtcrime.securesms.jobs.BackupMessagesJob
import org.thoughtcrime.securesms.jobs.RestoreLocalAttachmentJob
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.net.SignalNetwork
import org.thoughtcrime.securesms.providers.BlobProvider
import org.thoughtcrime.securesms.recipients.Recipient
import org.whispersystems.signalservice.api.NetworkResult
import org.whispersystems.signalservice.api.backup.MessageBackupKey
import org.whispersystems.signalservice.api.push.ServiceId.ACI
import org.whispersystems.signalservice.api.svr.SvrBApi
import java.io.FileOutputStream
import java.io.IOException
import java.io.InputStream
@@ -99,7 +102,7 @@ class InternalBackupPlaygroundViewModel : ViewModel() {
_state.value = _state.value.copy(statusMessage = "Exporting encrypted backup to disk...")
disposables += Single
.fromCallable {
BackupRepository.export(
BackupRepository.exportForDebugging(
outputStream = openStream(),
append = { bytes -> appendStream().use { it.write(bytes) } }
)
@@ -115,7 +118,7 @@ class InternalBackupPlaygroundViewModel : ViewModel() {
_state.value = _state.value.copy(statusMessage = "Exporting plaintext backup to disk...")
disposables += Single
.fromCallable {
BackupRepository.export(
BackupRepository.exportForDebugging(
outputStream = openStream(),
append = { bytes -> appendStream().use { it.write(bytes) } },
plaintext = true
@@ -134,12 +137,12 @@ class InternalBackupPlaygroundViewModel : ViewModel() {
disposables += Single
.fromCallable {
BackupRepository.export(
BackupRepository.exportForDebugging(
outputStream = FileOutputStream(tempFile),
append = { bytes -> tempFile.appendBytes(bytes) }
)
_state.value = _state.value.copy(statusMessage = "Export complete! Validating...")
ArchiveValidator.validate(tempFile, SignalStore.backup.messageBackupKey, forTransfer = false)
ArchiveValidator.validateLocalOrLinking(tempFile, SignalStore.backup.messageBackupKey, forTransfer = false)
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
@@ -181,7 +184,7 @@ class InternalBackupPlaygroundViewModel : ViewModel() {
val selfData = BackupRepository.SelfData(aci, self.pni.get(), self.e164.get(), ProfileKey(self.profileKey))
val backupKey = customCredentials?.messageBackupKey ?: SignalStore.backup.messageBackupKey
disposables += Single.fromCallable { BackupRepository.import(length, inputStreamFactory, selfData, backupKey) }
disposables += Single.fromCallable { BackupRepository.importForDebugging(length, inputStreamFactory, selfData, backupKey) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeBy {
@@ -234,10 +237,27 @@ class InternalBackupPlaygroundViewModel : ViewModel() {
}
}
val encryptedStream = tempBackupFile.inputStream()
val forwardSecrecyMetadata = tempBackupFile.inputStream().use { EncryptedBackupReader.readForwardSecrecyMetadata(it) }
if (forwardSecrecyMetadata == null) {
throw IOException("Failed to read forward secrecy metadata!")
}
val svrBAuth = when (val result = BackupRepository.getSvrBAuth()) {
is NetworkResult.Success -> result.result
else -> throw IOException("Failed to read forward secrecy metadata!")
}
val forwardSecrecyToken = when (val result = SignalNetwork.svrB.restore(svrBAuth, SignalStore.backup.messageBackupKey, forwardSecrecyMetadata)) {
is SvrBApi.RestoreResult.Success -> result.data.forwardSecrecyToken
else -> throw IOException("Failed to read forward secrecy metadata!")
}
val encryptedStream = tempBackupFile.inputStream().apply {
EncryptedBackupReader.readForwardSecrecyMetadata(this)
}
val iv = encryptedStream.readNBytesOrThrow(16)
val backupKey = SignalStore.backup.messageBackupKey
val keyMaterial = backupKey.deriveBackupSecrets(Recipient.self().aci.get())
val keyMaterial = backupKey.deriveBackupSecrets(Recipient.self().aci.get(), forwardSecrecyToken)
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding").apply {
init(Cipher.DECRYPT_MODE, SecretKeySpec(keyMaterial.aesKey, "AES"), IvParameterSpec(iv))
}
@@ -246,7 +266,7 @@ class InternalBackupPlaygroundViewModel : ViewModel() {
CipherInputStream(
LimitedInputStream(
wrapped = encryptedStream,
maxBytes = tempBackupFile.length() - MAC_SIZE
maxBytes = tempBackupFile.length() - MAC_SIZE - tempBackupFile.inputStream().use { EncryptedBackupReader.getForwardSecrecyPrefixDataLength(it) }
),
cipher
)