mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-23 12:38:33 +00:00
Prevent safety number changes during quick restore flow.
This commit is contained in:
@@ -337,6 +337,22 @@ class AccountValues internal constructor(store: KeyValueStore, context: Context)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Only to be used as part of Quick Restore, DO NOT USE OTHERWISE.
|
||||
*/
|
||||
fun resetAciAndPniIdentityKeysAfterFailedRestore() {
|
||||
synchronized(this) {
|
||||
Log.i(TAG, "Resetting ACI and PNI identity keys after failed quick registration and restore")
|
||||
|
||||
store.beginWrite()
|
||||
.remove(KEY_ACI_IDENTITY_PUBLIC_KEY)
|
||||
.remove(KEY_ACI_IDENTITY_PRIVATE_KEY)
|
||||
.remove(KEY_PNI_IDENTITY_PUBLIC_KEY)
|
||||
.remove(KEY_PNI_IDENTITY_PRIVATE_KEY)
|
||||
.commit()
|
||||
}
|
||||
}
|
||||
|
||||
/** Only to be used when restoring an identity public key from an old backup */
|
||||
fun restoreLegacyIdentityPublicKeyFromBackup(base64: String) {
|
||||
Log.w(TAG, "Restoring legacy identity public key from backup.")
|
||||
|
||||
@@ -10,6 +10,7 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.withContext
|
||||
import okio.ByteString.Companion.toByteString
|
||||
import org.signal.core.util.Base64.decode
|
||||
import org.signal.core.util.isNotNullOrBlank
|
||||
import org.signal.core.util.logging.Log
|
||||
@@ -85,7 +86,11 @@ object QuickRegistrationRepository {
|
||||
null -> null
|
||||
},
|
||||
backupSizeBytes = SignalStore.backup.totalBackupSize.takeIf { it > 0 },
|
||||
restoreMethodToken = restoreMethodToken
|
||||
restoreMethodToken = restoreMethodToken,
|
||||
aciIdentityKeyPublic = SignalStore.account.aciIdentityKey.publicKey.serialize().toByteString(),
|
||||
aciIdentityKeyPrivate = SignalStore.account.aciIdentityKey.privateKey.serialize().toByteString(),
|
||||
pniIdentityKeyPublic = SignalStore.account.pniIdentityKey.publicKey.serialize().toByteString(),
|
||||
pniIdentityKeyPrivate = SignalStore.account.pniIdentityKey.privateKey.serialize().toByteString()
|
||||
)
|
||||
)
|
||||
.successOrThrow()
|
||||
|
||||
@@ -25,6 +25,7 @@ import kotlinx.coroutines.withContext
|
||||
import org.signal.core.util.Base64
|
||||
import org.signal.core.util.isNotNullOrBlank
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.signal.libsignal.protocol.IdentityKeyPair
|
||||
import org.thoughtcrime.securesms.backup.v2.BackupRepository
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.RestoreDecisionState
|
||||
import org.thoughtcrime.securesms.dependencies.AppDependencies
|
||||
@@ -1029,7 +1030,7 @@ class RegistrationViewModel : ViewModel() {
|
||||
setInProgress(false)
|
||||
}
|
||||
|
||||
fun registerWithBackupKey(context: Context, backupKey: String, e164: String?, pin: String?) {
|
||||
fun registerWithBackupKey(context: Context, backupKey: String, e164: String?, pin: String?, aciIdentityKeyPair: IdentityKeyPair?, pniIdentityKeyPair: IdentityKeyPair?) {
|
||||
setInProgress(true)
|
||||
|
||||
viewModelScope.launch(context = coroutineExceptionHandler) {
|
||||
@@ -1040,6 +1041,13 @@ class RegistrationViewModel : ViewModel() {
|
||||
val accountEntropyPool = AccountEntropyPool(backupKey)
|
||||
SignalStore.account.restoreAccountEntropyPool(accountEntropyPool)
|
||||
|
||||
if (aciIdentityKeyPair != null) {
|
||||
SignalStore.account.restoreAciIdentityKeyFromBackup(aciIdentityKeyPair.publicKey.serialize(), aciIdentityKeyPair.privateKey.serialize())
|
||||
if (pniIdentityKeyPair != null) {
|
||||
SignalStore.account.restorePniIdentityKeyFromBackup(pniIdentityKeyPair.publicKey.serialize(), pniIdentityKeyPair.privateKey.serialize())
|
||||
}
|
||||
}
|
||||
|
||||
val masterKey = accountEntropyPool.deriveMasterKey()
|
||||
setRecoveryPassword(masterKey.deriveRegistrationRecoveryPassword())
|
||||
verifyReRegisterInternal(context = context, pin = pin, masterKey = masterKey)
|
||||
|
||||
@@ -102,7 +102,9 @@ class EnterBackupKeyFragment : ComposeFragment() {
|
||||
context = requireContext(),
|
||||
backupKey = viewModel.backupKey,
|
||||
e164 = null,
|
||||
pin = null
|
||||
pin = null,
|
||||
aciIdentityKeyPair = null,
|
||||
pniIdentityKeyPair = null
|
||||
)
|
||||
},
|
||||
onLearnMore = { CommunicationActions.openBrowserLink(requireContext(), LEARN_MORE_URL) },
|
||||
|
||||
@@ -62,6 +62,7 @@ class EnterBackupKeyViewModel : ViewModel() {
|
||||
|
||||
if (incorrectKeyError && SignalStore.account.restoredAccountEntropyPool) {
|
||||
SignalStore.account.resetAccountEntropyPool()
|
||||
SignalStore.account.resetAciAndPniIdentityKeysAfterFailedRestore()
|
||||
}
|
||||
|
||||
it.copy(
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.registrationv3.ui.restore
|
||||
|
||||
import org.signal.libsignal.protocol.IdentityKey
|
||||
import org.signal.libsignal.protocol.IdentityKeyPair
|
||||
import org.signal.libsignal.protocol.ecc.ECPrivateKey
|
||||
import org.signal.registration.proto.RegistrationProvisionMessage
|
||||
import java.security.InvalidKeyException
|
||||
|
||||
/**
|
||||
* Attempt to parse the ACI identity key pair from the proto message parts.
|
||||
*/
|
||||
val RegistrationProvisionMessage.aciIdentityKeyPair: IdentityKeyPair?
|
||||
get() {
|
||||
return try {
|
||||
IdentityKeyPair(
|
||||
IdentityKey(aciIdentityKeyPublic.toByteArray()),
|
||||
ECPrivateKey(aciIdentityKeyPrivate.toByteArray())
|
||||
)
|
||||
} catch (_: InvalidKeyException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to parse the PNI identity key pair from the proto message parts.
|
||||
*/
|
||||
val RegistrationProvisionMessage.pniIdentityKeyPair: IdentityKeyPair?
|
||||
get() {
|
||||
return try {
|
||||
IdentityKeyPair(
|
||||
IdentityKey(pniIdentityKeyPublic.toByteArray()),
|
||||
ECPrivateKey(pniIdentityKeyPrivate.toByteArray())
|
||||
)
|
||||
} catch (_: InvalidKeyException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
@@ -89,7 +89,7 @@ class RestoreViaQrFragment : ComposeFragment() {
|
||||
.distinctUntilChanged()
|
||||
.collect { message ->
|
||||
if (message.platform == RegistrationProvisionMessage.Platform.ANDROID || message.tier != null) {
|
||||
sharedViewModel.registerWithBackupKey(requireContext(), message.accountEntropyPool, message.e164, message.pin)
|
||||
sharedViewModel.registerWithBackupKey(requireContext(), message.accountEntropyPool, message.e164, message.pin, message.aciIdentityKeyPair, message.pniIdentityKeyPair)
|
||||
} else {
|
||||
findNavController().safeNavigate(RestoreViaQrFragmentDirections.goToNoBackupToRestore())
|
||||
}
|
||||
|
||||
@@ -28,4 +28,8 @@ message RegistrationProvisionMessage {
|
||||
optional Tier tier = 7;
|
||||
optional uint64 backupSizeBytes = 8;
|
||||
string restoreMethodToken = 9;
|
||||
bytes aciIdentityKeyPublic = 10;
|
||||
bytes aciIdentityKeyPrivate = 11;
|
||||
bytes pniIdentityKeyPublic = 12;
|
||||
bytes pniIdentityKeyPrivate = 13;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user