mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-20 00:29:11 +01:00
Add mostly-working SVR3 implementation behind flag.
This commit is contained in:
committed by
Alex Hart
parent
143a61e312
commit
664c22d8f1
@@ -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
|
||||
}
|
||||
@@ -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 &&
|
||||
|
||||
Reference in New Issue
Block a user