Add mostly-working SVR3 implementation behind flag.

This commit is contained in:
Greyson Parrelli
2024-06-07 15:40:53 -04:00
committed by Alex Hart
parent 143a61e312
commit 664c22d8f1
44 changed files with 1008 additions and 313 deletions

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.pin
import org.thoughtcrime.securesms.util.FeatureFlags
object Svr3Migration {
/**
* Whether we should read from SVR3. This is a compile-time flag because it affects what happens pre-registration.
* It only exists so that we can merge this in before SVR3 is ready server-side. This flag will be removed once we actually launch SVR3 support.
*/
const val shouldReadFromSvr3 = false
/**
* Whether or not you should write to SVR3. If [shouldWriteToSvr2] is also enabled, you should write to SVR3 first.
*/
val shouldWriteToSvr3: Boolean
get() = shouldReadFromSvr3 && FeatureFlags.svr3MigrationPhase().let { it == 1 || it == 2 }
/**
* Whether or not you should write to SVR2. If [shouldWriteToSvr3] is also enabled, you should write to SVR3 first.
*/
val shouldWriteToSvr2: Boolean
get() = !shouldReadFromSvr3 || FeatureFlags.svr3MigrationPhase() != 2
}

View File

@@ -20,6 +20,7 @@ import org.thoughtcrime.securesms.jobs.StorageAccountRestoreJob
import org.thoughtcrime.securesms.jobs.StorageForcePushJob
import org.thoughtcrime.securesms.jobs.StorageSyncJob
import org.thoughtcrime.securesms.jobs.Svr2MirrorJob
import org.thoughtcrime.securesms.jobs.Svr3MirrorJob
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.lock.v2.PinKeyboardType
import org.thoughtcrime.securesms.megaphone.Megaphones
@@ -29,6 +30,8 @@ import org.whispersystems.signalservice.api.kbs.MasterKey
import org.whispersystems.signalservice.api.svr.SecureValueRecovery
import org.whispersystems.signalservice.api.svr.SecureValueRecovery.BackupResponse
import org.whispersystems.signalservice.api.svr.SecureValueRecovery.RestoreResponse
import org.whispersystems.signalservice.api.svr.SecureValueRecovery.SvrVersion
import org.whispersystems.signalservice.api.svr.Svr3Credentials
import org.whispersystems.signalservice.internal.push.AuthCredentials
import java.io.IOException
import java.util.concurrent.TimeUnit
@@ -40,12 +43,23 @@ object SvrRepository {
val TAG = Log.tag(SvrRepository::class.java)
private val svr2: SecureValueRecovery = AppDependencies.signalServiceAccountManager.getSecureValueRecoveryV2(BuildConfig.SVR2_MRENCLAVE)
private val svr3: SecureValueRecovery = AppDependencies.signalServiceAccountManager.getSecureValueRecoveryV3(AppDependencies.libsignalNetwork)
/** An ordered list of SVR implementations to read from. They should be in priority order, with the most important one listed first. */
private val readImplementations: List<SecureValueRecovery> = listOf(svr2)
private val readImplementations: List<SecureValueRecovery> = if (Svr3Migration.shouldReadFromSvr3) listOf(svr3, svr2) else listOf(svr2)
/** An ordered list of SVR implementations to write to. They should be in priority order, with the most important one listed first. */
private val writeImplementations: List<SecureValueRecovery> = listOf(svr2)
private val writeImplementations: List<SecureValueRecovery>
get() {
val implementations = mutableListOf<SecureValueRecovery>()
if (Svr3Migration.shouldWriteToSvr3) {
implementations += svr3
}
if (Svr3Migration.shouldWriteToSvr2) {
implementations += svr2
}
return implementations
}
/**
* A lock that ensures that only one thread at a time is altering the various pieces of SVR state.
@@ -72,16 +86,23 @@ object SvrRepository {
operationLock.withLock {
Log.i(TAG, "restoreMasterKeyPreRegistration()", true)
val operations: List<Pair<SecureValueRecovery, () -> RestoreResponse>> = listOf(
svr2 to { restoreMasterKeyPreRegistration(svr2, credentials.svr2, userPin) }
)
val operations: List<Pair<SecureValueRecovery, () -> RestoreResponse>> = if (Svr3Migration.shouldReadFromSvr3) {
listOf(
svr3 to { restoreMasterKeyPreRegistrationFromV3(credentials.svr3, userPin) },
svr2 to { restoreMasterKeyPreRegistrationFromV2(credentials.svr2, userPin) }
)
} else {
listOf(svr2 to { restoreMasterKeyPreRegistrationFromV2(credentials.svr2, userPin) })
}
for ((implementation, operation) in operations) {
when (val response: RestoreResponse = operation()) {
is RestoreResponse.Success -> {
Log.i(TAG, "[restoreMasterKeyPreRegistration] Successfully restored master key. $implementation", true)
if (implementation == svr2) {
SignalStore.svr().appendAuthTokenToList(response.authorization.asBasic())
when (implementation.svrVersion) {
SvrVersion.SVR2 -> SignalStore.svr().appendSvr2AuthTokenToList(response.authorization.asBasic())
SvrVersion.SVR3 -> SignalStore.svr().appendSvr3AuthTokenToList(response.authorization.asBasic())
}
return response.masterKey
@@ -138,9 +159,12 @@ object SvrRepository {
SignalStore.storageService().setNeedsAccountRestore(false)
SignalStore.pinValues().keyboardType = pinKeyboardType
SignalStore.storageService().setNeedsAccountRestore(false)
if (implementation == svr2) {
SignalStore.svr().appendAuthTokenToList(response.authorization.asBasic())
when (implementation.svrVersion) {
SvrVersion.SVR2 -> SignalStore.svr().appendSvr2AuthTokenToList(response.authorization.asBasic())
SvrVersion.SVR3 -> SignalStore.svr().appendSvr3AuthTokenToList(response.authorization.asBasic())
}
AppDependencies.jobManager.add(ResetSvrGuessCountJob())
stopwatch.split("metadata")
@@ -154,11 +178,14 @@ object SvrRepository {
.enqueueAndBlockUntilCompletion(TimeUnit.SECONDS.toMillis(10))
stopwatch.split("contact-restore")
// TODO [svr3]
if (implementation != svr2) {
if (implementation.svrVersion != SvrVersion.SVR2 && Svr3Migration.shouldWriteToSvr2) {
AppDependencies.jobManager.add(Svr2MirrorJob())
}
if (implementation.svrVersion != SvrVersion.SVR3 && Svr3Migration.shouldWriteToSvr3) {
AppDependencies.jobManager.add(Svr3MirrorJob())
}
stopwatch.stop(TAG)
return response
@@ -200,7 +227,9 @@ object SvrRepository {
return operationLock.withLock {
val masterKey: MasterKey = SignalStore.svr().getOrCreateMasterKey()
val responses: List<BackupResponse> = writeImplementations
val writeTargets = writeImplementations
val responses: List<BackupResponse> = writeTargets
.map { it.setPin(userPin, masterKey) }
.map { it.execute() }
@@ -226,7 +255,14 @@ object SvrRepository {
SignalStore.svr().setMasterKey(masterKey, userPin)
SignalStore.svr().isPinForgottenOrSkipped = false
SignalStore.svr().appendAuthTokenToList(overallResponse.authorization.asBasic())
responses
.filterIsInstance<BackupResponse.Success>()
.forEach {
when (it.svrVersion) {
SvrVersion.SVR2 -> SignalStore.svr().appendSvr2AuthTokenToList(it.authorization.asBasic())
SvrVersion.SVR3 -> SignalStore.svr().appendSvr3AuthTokenToList(it.authorization.asBasic())
}
}
SignalStore.pinValues().keyboardType = keyboardType
SignalStore.pinValues().resetPinReminders()
@@ -348,8 +384,23 @@ object SvrRepository {
@Throws(IOException::class)
fun refreshAndStoreAuthorization() {
try {
val credentials: AuthCredentials = svr2.authorization()
backupSvrCredentials(credentials)
var newToken = if (Svr3Migration.shouldWriteToSvr3) {
val credentials: AuthCredentials = svr3.authorization()
SignalStore.svr().appendSvr3AuthTokenToList(credentials.asBasic())
} else {
false
}
newToken = newToken || if (Svr3Migration.shouldWriteToSvr2) {
val credentials: AuthCredentials = svr2.authorization()
SignalStore.svr().appendSvr2AuthTokenToList(credentials.asBasic())
} else {
false
}
if (newToken && SignalStore.svr().hasPin()) {
BackupManager(AppDependencies.application).dataChanged()
}
} catch (e: Throwable) {
if (e is IOException) {
throw e
@@ -361,11 +412,21 @@ object SvrRepository {
@WorkerThread
@VisibleForTesting
fun restoreMasterKeyPreRegistration(svr: SecureValueRecovery, credentials: AuthCredentials?, userPin: String): RestoreResponse {
fun restoreMasterKeyPreRegistrationFromV2(credentials: AuthCredentials?, userPin: String): RestoreResponse {
return if (credentials == null) {
RestoreResponse.Missing
} else {
svr.restoreDataPreRegistration(credentials, userPin)
svr2.restoreDataPreRegistration(credentials, shareSet = null, userPin)
}
}
@WorkerThread
@VisibleForTesting
fun restoreMasterKeyPreRegistrationFromV3(credentials: Svr3Credentials?, userPin: String): RestoreResponse {
return if (credentials?.shareSet == null) {
RestoreResponse.Missing
} else {
svr3.restoreDataPreRegistration(credentials.authCredentials, credentials.shareSet, userPin)
}
}
@@ -395,14 +456,6 @@ object SvrRepository {
}
}
private fun backupSvrCredentials(credentials: AuthCredentials) {
val tokenIsNew = SignalStore.svr().appendAuthTokenToList(credentials.asBasic())
if (tokenIsNew && SignalStore.svr().hasPin()) {
BackupManager(AppDependencies.application).dataChanged()
}
}
private val hasNoRegistrationLock: Boolean
get() {
return !SignalStore.svr().isRegistrationLockEnabled &&