mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-23 04:28:35 +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 */
|
/** Only to be used when restoring an identity public key from an old backup */
|
||||||
fun restoreLegacyIdentityPublicKeyFromBackup(base64: String) {
|
fun restoreLegacyIdentityPublicKeyFromBackup(base64: String) {
|
||||||
Log.w(TAG, "Restoring legacy identity public key from backup.")
|
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.delay
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import okio.ByteString.Companion.toByteString
|
||||||
import org.signal.core.util.Base64.decode
|
import org.signal.core.util.Base64.decode
|
||||||
import org.signal.core.util.isNotNullOrBlank
|
import org.signal.core.util.isNotNullOrBlank
|
||||||
import org.signal.core.util.logging.Log
|
import org.signal.core.util.logging.Log
|
||||||
@@ -85,7 +86,11 @@ object QuickRegistrationRepository {
|
|||||||
null -> null
|
null -> null
|
||||||
},
|
},
|
||||||
backupSizeBytes = SignalStore.backup.totalBackupSize.takeIf { it > 0 },
|
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()
|
.successOrThrow()
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import kotlinx.coroutines.withContext
|
|||||||
import org.signal.core.util.Base64
|
import org.signal.core.util.Base64
|
||||||
import org.signal.core.util.isNotNullOrBlank
|
import org.signal.core.util.isNotNullOrBlank
|
||||||
import org.signal.core.util.logging.Log
|
import org.signal.core.util.logging.Log
|
||||||
|
import org.signal.libsignal.protocol.IdentityKeyPair
|
||||||
import org.thoughtcrime.securesms.backup.v2.BackupRepository
|
import org.thoughtcrime.securesms.backup.v2.BackupRepository
|
||||||
import org.thoughtcrime.securesms.database.model.databaseprotos.RestoreDecisionState
|
import org.thoughtcrime.securesms.database.model.databaseprotos.RestoreDecisionState
|
||||||
import org.thoughtcrime.securesms.dependencies.AppDependencies
|
import org.thoughtcrime.securesms.dependencies.AppDependencies
|
||||||
@@ -1029,7 +1030,7 @@ class RegistrationViewModel : ViewModel() {
|
|||||||
setInProgress(false)
|
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)
|
setInProgress(true)
|
||||||
|
|
||||||
viewModelScope.launch(context = coroutineExceptionHandler) {
|
viewModelScope.launch(context = coroutineExceptionHandler) {
|
||||||
@@ -1040,6 +1041,13 @@ class RegistrationViewModel : ViewModel() {
|
|||||||
val accountEntropyPool = AccountEntropyPool(backupKey)
|
val accountEntropyPool = AccountEntropyPool(backupKey)
|
||||||
SignalStore.account.restoreAccountEntropyPool(accountEntropyPool)
|
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()
|
val masterKey = accountEntropyPool.deriveMasterKey()
|
||||||
setRecoveryPassword(masterKey.deriveRegistrationRecoveryPassword())
|
setRecoveryPassword(masterKey.deriveRegistrationRecoveryPassword())
|
||||||
verifyReRegisterInternal(context = context, pin = pin, masterKey = masterKey)
|
verifyReRegisterInternal(context = context, pin = pin, masterKey = masterKey)
|
||||||
|
|||||||
@@ -102,7 +102,9 @@ class EnterBackupKeyFragment : ComposeFragment() {
|
|||||||
context = requireContext(),
|
context = requireContext(),
|
||||||
backupKey = viewModel.backupKey,
|
backupKey = viewModel.backupKey,
|
||||||
e164 = null,
|
e164 = null,
|
||||||
pin = null
|
pin = null,
|
||||||
|
aciIdentityKeyPair = null,
|
||||||
|
pniIdentityKeyPair = null
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
onLearnMore = { CommunicationActions.openBrowserLink(requireContext(), LEARN_MORE_URL) },
|
onLearnMore = { CommunicationActions.openBrowserLink(requireContext(), LEARN_MORE_URL) },
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ class EnterBackupKeyViewModel : ViewModel() {
|
|||||||
|
|
||||||
if (incorrectKeyError && SignalStore.account.restoredAccountEntropyPool) {
|
if (incorrectKeyError && SignalStore.account.restoredAccountEntropyPool) {
|
||||||
SignalStore.account.resetAccountEntropyPool()
|
SignalStore.account.resetAccountEntropyPool()
|
||||||
|
SignalStore.account.resetAciAndPniIdentityKeysAfterFailedRestore()
|
||||||
}
|
}
|
||||||
|
|
||||||
it.copy(
|
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()
|
.distinctUntilChanged()
|
||||||
.collect { message ->
|
.collect { message ->
|
||||||
if (message.platform == RegistrationProvisionMessage.Platform.ANDROID || message.tier != null) {
|
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 {
|
} else {
|
||||||
findNavController().safeNavigate(RestoreViaQrFragmentDirections.goToNoBackupToRestore())
|
findNavController().safeNavigate(RestoreViaQrFragmentDirections.goToNoBackupToRestore())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,4 +28,8 @@ message RegistrationProvisionMessage {
|
|||||||
optional Tier tier = 7;
|
optional Tier tier = 7;
|
||||||
optional uint64 backupSizeBytes = 8;
|
optional uint64 backupSizeBytes = 8;
|
||||||
string restoreMethodToken = 9;
|
string restoreMethodToken = 9;
|
||||||
|
bytes aciIdentityKeyPublic = 10;
|
||||||
|
bytes aciIdentityKeyPrivate = 11;
|
||||||
|
bytes pniIdentityKeyPublic = 12;
|
||||||
|
bytes pniIdentityKeyPrivate = 13;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user