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

@@ -9,6 +9,7 @@ import org.junit.Assert.assertEquals
import org.junit.Test
import org.signal.core.util.Base64
import org.signal.core.util.Hex
import org.signal.libsignal.messagebackup.BackupForwardSecrecyToken
import org.thoughtcrime.securesms.backup.v2.proto.AccountData
import org.thoughtcrime.securesms.backup.v2.proto.BackupInfo
import org.thoughtcrime.securesms.backup.v2.proto.Frame
@@ -28,7 +29,7 @@ class EncryptedBackupReaderWriterTest {
val outputStream = ByteArrayOutputStream()
val frameCount = 10_000
EncryptedBackupWriter(key, aci, outputStream, append = { outputStream.write(it) }).use { writer ->
EncryptedBackupWriter.createForLocalOrLinking(key, aci, outputStream, append = { outputStream.write(it) }).use { writer ->
writer.write(BackupInfo(version = 1, backupTimeMs = 1000L))
for (i in 0 until frameCount) {
@@ -39,7 +40,7 @@ class EncryptedBackupReaderWriterTest {
val ciphertext: ByteArray = outputStream.toByteArray()
println(ciphertext.size)
val frames: List<Frame> = EncryptedBackupReader(key, aci, ciphertext.size.toLong()) { ciphertext.inputStream() }.use { reader ->
val frames: List<Frame> = EncryptedBackupReader.createForLocalOrLinking(key, aci, ciphertext.size.toLong()) { ciphertext.inputStream() }.use { reader ->
assertEquals(reader.backupInfo?.version, 1L)
assertEquals(reader.backupInfo?.backupTimeMs, 1000L)
reader.asSequence().toList()
@@ -61,7 +62,7 @@ class EncryptedBackupReaderWriterTest {
.map { frameCount ->
val outputStream = ByteArrayOutputStream()
EncryptedBackupWriter(key, aci, outputStream, append = { outputStream.write(it) }).use { writer ->
EncryptedBackupWriter.createForLocalOrLinking(key, aci, outputStream, append = { outputStream.write(it) }).use { writer ->
writer.write(BackupInfo(version = 1, backupTimeMs = 1000L))
for (i in 0 until frameCount) {
@@ -86,7 +87,7 @@ class EncryptedBackupReaderWriterTest {
.map {
val outputStream = ByteArrayOutputStream()
EncryptedBackupWriter(key, aci, outputStream, append = { outputStream.write(it) }).use { writer ->
EncryptedBackupWriter.createForLocalOrLinking(key, aci, outputStream, append = { outputStream.write(it) }).use { writer ->
writer.write(BackupInfo(version = 1, backupTimeMs = 1000L))
writer.write(Frame(account = AccountData(username = "static-data")))
}
@@ -98,4 +99,45 @@ class EncryptedBackupReaderWriterTest {
assertEquals(count, uniqueOutputs.size)
}
@Test
fun `can read back all of the frames we write - forward secrecy`() {
val key = MessageBackupKey(Util.getSecretBytes(32))
val aci = ACI.from(UUID.randomUUID())
val outputStream = ByteArrayOutputStream()
val forwardSecrecyToken = BackupForwardSecrecyToken(Util.getSecretBytes(32))
val frameCount = 10_000
EncryptedBackupWriter.createForSignalBackup(
key = key,
aci = aci,
forwardSecrecyToken = forwardSecrecyToken,
forwardSecrecyMetadata = Util.getSecretBytes(64),
outputStream = outputStream,
append = { outputStream.write(it) }
).use { writer ->
writer.write(BackupInfo(version = 1, backupTimeMs = 1000L))
for (i in 0 until frameCount) {
writer.write(Frame(account = AccountData(username = "username-$i")))
}
}
val ciphertext: ByteArray = outputStream.toByteArray()
println(ciphertext.size)
val frames: List<Frame> = EncryptedBackupReader.createForSignalBackup(key, aci, forwardSecrecyToken, ciphertext.size.toLong()) { ciphertext.inputStream() }.use { reader ->
assertEquals(reader.backupInfo?.version, 1L)
assertEquals(reader.backupInfo?.backupTimeMs, 1000L)
reader.asSequence().toList()
}
assertEquals(frameCount, frames.size)
for (i in 0 until frameCount) {
assertEquals("username-$i", frames[i].account?.username)
}
}
}

View File

@@ -56,6 +56,7 @@ import org.whispersystems.signalservice.api.remoteconfig.RemoteConfigApi
import org.whispersystems.signalservice.api.services.DonationsService
import org.whispersystems.signalservice.api.services.ProfileService
import org.whispersystems.signalservice.api.storage.StorageServiceApi
import org.whispersystems.signalservice.api.svr.SvrBApi
import org.whispersystems.signalservice.api.username.UsernameApi
import org.whispersystems.signalservice.api.websocket.SignalWebSocket
import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration
@@ -300,4 +301,8 @@ class MockApplicationDependencyProvider : AppDependencies.Provider {
override fun provideDonationsApi(authWebSocket: SignalWebSocket.AuthenticatedWebSocket, unauthWebSocket: SignalWebSocket.UnauthenticatedWebSocket): DonationsApi {
return mockk(relaxed = true)
}
override fun provideSvrBApi(libSignalNetwork: Network): SvrBApi {
return mockk(relaxed = true)
}
}