From 636762ea5bdae8f79bed442532040810f29c7499 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Wed, 23 Sep 2026 10:17:06 -0400 Subject: [PATCH] Update to nullable fields on registration provisioning proto. --- .../data/QuickRegistrationRepository.kt | 7 +++---- .../ui/restore/NoBackupToRestoreFragment.kt | 2 +- .../ui/restore/RegistrationProvisionMessageExt.kt | 15 ++++++++++++--- .../ui/restore/RestoreViaQrFragment.kt | 2 +- .../v2/AppRegistrationNetworkController.kt | 10 ++++------ .../sample/dependencies/DemoNetworkController.kt | 9 ++++++--- .../sample/dependencies/DemoStorageController.kt | 8 ++------ .../olddevicetransfer/TransferAccountViewModel.kt | 8 ++++---- .../quickrestore/QuickRestoreQrViewModel.kt | 1 - .../main/protowire/RegistrationProvisioning.proto | 8 ++++---- 10 files changed, 37 insertions(+), 33 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/data/QuickRegistrationRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/data/QuickRegistrationRepository.kt index c0c90a480c..9251218022 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/data/QuickRegistrationRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/data/QuickRegistrationRepository.kt @@ -10,7 +10,6 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.isActive import kotlinx.coroutines.withContext -import okio.ByteString import okio.ByteString.Companion.toByteString import org.signal.core.util.Base64.decode import org.signal.core.util.isNotNullOrBlank @@ -105,7 +104,7 @@ object QuickRegistrationRepository { ephemeralId, publicKey, RegistrationProvisionMessage( - e164 = e164.orEmpty(), + e164 = e164, aci = SignalStore.account.requireAci().toByteString(), accountEntropyPool = SignalStore.account.accountEntropyPool.value, pin = SignalStore.svr.pin, @@ -120,8 +119,8 @@ object QuickRegistrationRepository { restoreMethodToken = restoreMethodToken, aciIdentityKeyPublic = SignalStore.account.aciIdentityKey.publicKey.serialize().toByteString(), aciIdentityKeyPrivate = SignalStore.account.aciIdentityKey.privateKey.serialize().toByteString(), - pniIdentityKeyPublic = pniIdentityKey?.publicKey?.serialize()?.toByteString() ?: ByteString.EMPTY, - pniIdentityKeyPrivate = pniIdentityKey?.privateKey?.serialize()?.toByteString() ?: ByteString.EMPTY, + pniIdentityKeyPublic = pniIdentityKey?.publicKey?.serialize()?.toByteString(), + pniIdentityKeyPrivate = pniIdentityKey?.privateKey?.serialize()?.toByteString(), backupVersion = SignalStore.backup.lastBackupProtoVersion ) ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/ui/restore/NoBackupToRestoreFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/ui/restore/NoBackupToRestoreFragment.kt index 16b8eaaef1..7f522ffc9b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/ui/restore/NoBackupToRestoreFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/ui/restore/NoBackupToRestoreFragment.kt @@ -91,7 +91,7 @@ class NoBackupToRestoreFragment : ComposeFragment() { sharedViewModel.registerWithBackupKey( context = requireContext(), backupKey = message.accountEntropyPool, - e164 = message.e164, + e164 = message.e164OrNull, pin = message.pin, aciIdentityKeyPair = message.aciIdentityKeyPair, pniIdentityKeyPair = message.pniIdentityKeyPair diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/ui/restore/RegistrationProvisionMessageExt.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/ui/restore/RegistrationProvisionMessageExt.kt index 3db16f8692..1fd67d9c32 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/ui/restore/RegistrationProvisionMessageExt.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/ui/restore/RegistrationProvisionMessageExt.kt @@ -11,6 +11,12 @@ import org.signal.libsignal.protocol.ecc.ECPrivateKey import org.signal.registration.proto.RegistrationProvisionMessage import java.security.InvalidKeyException +/** + * The phone number, or null if absent or empty. + */ +val RegistrationProvisionMessage.e164OrNull: String? + get() = e164?.takeIf { it.isNotEmpty() } + /** * Attempt to parse the ACI identity key pair from the proto message parts. */ @@ -27,14 +33,17 @@ val RegistrationProvisionMessage.aciIdentityKeyPair: IdentityKeyPair? } /** - * Attempt to parse the PNI identity key pair from the proto message parts. + * Attempt to parse the PNI identity key pair from the proto message parts. Null if absent or empty. */ val RegistrationProvisionMessage.pniIdentityKeyPair: IdentityKeyPair? get() { + val publicKey = pniIdentityKeyPublic?.takeIf { it.size > 0 } ?: return null + val privateKey = pniIdentityKeyPrivate?.takeIf { it.size > 0 } ?: return null + return try { IdentityKeyPair( - IdentityKey(pniIdentityKeyPublic.toByteArray()), - ECPrivateKey(pniIdentityKeyPrivate.toByteArray()) + IdentityKey(publicKey.toByteArray()), + ECPrivateKey(privateKey.toByteArray()) ) } catch (_: InvalidKeyException) { null diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/ui/restore/RestoreViaQrFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/ui/restore/RestoreViaQrFragment.kt index 051a002c82..886128671b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/ui/restore/RestoreViaQrFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/ui/restore/RestoreViaQrFragment.kt @@ -106,7 +106,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, message.aciIdentityKeyPair, message.pniIdentityKeyPair) + sharedViewModel.registerWithBackupKey(requireContext(), message.accountEntropyPool, message.e164OrNull, message.pin, message.aciIdentityKeyPair, message.pniIdentityKeyPair) } else { sharedViewModel.registrationProvisioningMessage = message findNavController().safeNavigate(RestoreViaQrFragmentDirections.goToNoBackupToRestore()) diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/AppRegistrationNetworkController.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/AppRegistrationNetworkController.kt index 67a9a9a64e..b96cdc02a4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/AppRegistrationNetworkController.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/AppRegistrationNetworkController.kt @@ -102,6 +102,8 @@ import org.thoughtcrime.securesms.profiles.ProfileName import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.registration.fcm.PushChallengeRequest import org.thoughtcrime.securesms.registration.ui.restore.StorageServiceRestore +import org.thoughtcrime.securesms.registration.ui.restore.e164OrNull +import org.thoughtcrime.securesms.registration.ui.restore.pniIdentityKeyPair import org.thoughtcrime.securesms.registration.util.RegistrationUtil import org.thoughtcrime.securesms.registration.viewmodel.SvrAuthCredentialSet import org.thoughtcrime.securesms.util.Environment @@ -623,14 +625,10 @@ class AppRegistrationNetworkController( ProvisioningMessage( accountEntropyPool = msg.accountEntropyPool, aci = aci, - e164 = msg.e164.takeIf { it.isNotBlank() }, + e164 = msg.e164OrNull, pin = msg.pin, aciIdentityKeyPair = IdentityKeyPair(IdentityKey(msg.aciIdentityKeyPublic.toByteArray()), ECPrivateKey(msg.aciIdentityKeyPrivate.toByteArray())), - pniIdentityKeyPair = if (msg.pniIdentityKeyPublic.size > 0 && msg.pniIdentityKeyPrivate.size > 0) { - IdentityKeyPair(IdentityKey(msg.pniIdentityKeyPublic.toByteArray()), ECPrivateKey(msg.pniIdentityKeyPrivate.toByteArray())) - } else { - null - }, + pniIdentityKeyPair = msg.pniIdentityKeyPair, platform = when (msg.platform) { RegistrationProvisionMessage.Platform.ANDROID -> ProvisioningMessage.Platform.ANDROID RegistrationProvisionMessage.Platform.IOS -> ProvisioningMessage.Platform.IOS diff --git a/demo/registration/src/main/java/org/signal/registration/sample/dependencies/DemoNetworkController.kt b/demo/registration/src/main/java/org/signal/registration/sample/dependencies/DemoNetworkController.kt index a3e7faafe4..ee496c1075 100644 --- a/demo/registration/src/main/java/org/signal/registration/sample/dependencies/DemoNetworkController.kt +++ b/demo/registration/src/main/java/org/signal/registration/sample/dependencies/DemoNetworkController.kt @@ -552,16 +552,19 @@ class DemoNetworkController( return@start } + val pniPublicKey = msg.pniIdentityKeyPublic + val pniPrivateKey = msg.pniIdentityKeyPrivate + trySend( ProvisioningEvent.MessageReceived( ProvisioningMessage( accountEntropyPool = msg.accountEntropyPool, aci = aci, - e164 = msg.e164.takeIf { it.isNotBlank() }, + e164 = msg.e164?.takeIf { it.isNotEmpty() }, pin = msg.pin, aciIdentityKeyPair = IdentityKeyPair(IdentityKey(msg.aciIdentityKeyPublic.toByteArray()), ECPrivateKey(msg.aciIdentityKeyPrivate.toByteArray())), - pniIdentityKeyPair = if (msg.pniIdentityKeyPublic.size > 0 && msg.pniIdentityKeyPrivate.size > 0) { - IdentityKeyPair(IdentityKey(msg.pniIdentityKeyPublic.toByteArray()), ECPrivateKey(msg.pniIdentityKeyPrivate.toByteArray())) + pniIdentityKeyPair = if (pniPublicKey != null && pniPublicKey.size > 0 && pniPrivateKey != null && pniPrivateKey.size > 0) { + IdentityKeyPair(IdentityKey(pniPublicKey.toByteArray()), ECPrivateKey(pniPrivateKey.toByteArray())) } else { null }, diff --git a/demo/registration/src/main/java/org/signal/registration/sample/dependencies/DemoStorageController.kt b/demo/registration/src/main/java/org/signal/registration/sample/dependencies/DemoStorageController.kt index 33fff98547..25adbd2eec 100644 --- a/demo/registration/src/main/java/org/signal/registration/sample/dependencies/DemoStorageController.kt +++ b/demo/registration/src/main/java/org/signal/registration/sample/dependencies/DemoStorageController.kt @@ -217,14 +217,10 @@ class DemoStorageController(private val context: Context) : StorageController { NetworkController.ProvisioningMessage( accountEntropyPool = data.accountEntropyPool, aci = ACI.parseOrThrow(accountData.aci), - e164 = accountData.e164, + e164 = accountData.e164?.takeIf { it.isNotEmpty() }, pin = data.pin.ifEmpty { null }, aciIdentityKeyPair = IdentityKeyPair(accountData.aciIdentityKeyPair.toByteArray()), - pniIdentityKeyPair = if (accountData.pniIdentityKeyPair.size > 0) { - IdentityKeyPair(accountData.pniIdentityKeyPair.toByteArray()) - } else { - null - }, + pniIdentityKeyPair = accountData.pniIdentityKeyPair.takeIf { it.size > 0 }?.let { IdentityKeyPair(it.toByteArray()) }, platform = when (prov.platform) { ProvisioningData.Platform.ANDROID -> NetworkController.ProvisioningMessage.Platform.ANDROID ProvisioningData.Platform.IOS -> NetworkController.ProvisioningMessage.Platform.IOS diff --git a/demo/registration/src/main/java/org/signal/registration/sample/screens/olddevicetransfer/TransferAccountViewModel.kt b/demo/registration/src/main/java/org/signal/registration/sample/screens/olddevicetransfer/TransferAccountViewModel.kt index 26b15f16da..5bc5245e9d 100644 --- a/demo/registration/src/main/java/org/signal/registration/sample/screens/olddevicetransfer/TransferAccountViewModel.kt +++ b/demo/registration/src/main/java/org/signal/registration/sample/screens/olddevicetransfer/TransferAccountViewModel.kt @@ -84,11 +84,11 @@ class TransferAccountViewModel( withContext(Dispatchers.IO) { val publicKey = ECPublicKey(Base64.decode(publicKeyEncoded)) - val e164 = checkNotNull(RegistrationPreferences.e164) { "No e164 stored" } + val e164 = RegistrationPreferences.e164 val aci = checkNotNull(RegistrationPreferences.aci) { "No ACI stored" } val aep = checkNotNull(RegistrationPreferences.aep) { "No AEP stored" } val aciKeyPair = checkNotNull(RegistrationPreferences.aciIdentityKeyPair) { "No ACI identity key pair stored" } - val pniKeyPair = checkNotNull(RegistrationPreferences.pniIdentityKeyPair) { "No PNI identity key pair stored" } + val pniKeyPair = RegistrationPreferences.pniIdentityKeyPair val restoreMethodToken = UUID.randomUUID().toString() val message = RegistrationProvisionMessage( @@ -101,8 +101,8 @@ class TransferAccountViewModel( restoreMethodToken = restoreMethodToken, aciIdentityKeyPublic = okio.ByteString.of(*aciKeyPair.publicKey.serialize()), aciIdentityKeyPrivate = okio.ByteString.of(*aciKeyPair.privateKey.serialize()), - pniIdentityKeyPublic = okio.ByteString.of(*pniKeyPair.publicKey.serialize()), - pniIdentityKeyPrivate = okio.ByteString.of(*pniKeyPair.privateKey.serialize()), + pniIdentityKeyPublic = pniKeyPair?.let { okio.ByteString.of(*it.publicKey.serialize()) }, + pniIdentityKeyPrivate = pniKeyPair?.let { okio.ByteString.of(*it.privateKey.serialize()) }, backupVersion = 0 ) diff --git a/feature/registration/src/main/java/org/signal/registration/screens/quickrestore/QuickRestoreQrViewModel.kt b/feature/registration/src/main/java/org/signal/registration/screens/quickrestore/QuickRestoreQrViewModel.kt index b3b947d0ba..1ee5ddc4b7 100644 --- a/feature/registration/src/main/java/org/signal/registration/screens/quickrestore/QuickRestoreQrViewModel.kt +++ b/feature/registration/src/main/java/org/signal/registration/screens/quickrestore/QuickRestoreQrViewModel.kt @@ -100,7 +100,6 @@ class QuickRestoreQrViewModel( private suspend fun handleProvisioningMessage(message: NetworkController.ProvisioningMessage) { parentEventEmitter(RegistrationFlowEvent.RestoreMethodTokenReceived(message.restoreMethodToken)) - if (message.e164 != null) { parentEventEmitter(RegistrationFlowEvent.E164Chosen(message.e164)) } diff --git a/lib/libsignal-service/src/main/protowire/RegistrationProvisioning.proto b/lib/libsignal-service/src/main/protowire/RegistrationProvisioning.proto index 6f6ab7cd76..d73ff2ee92 100644 --- a/lib/libsignal-service/src/main/protowire/RegistrationProvisioning.proto +++ b/lib/libsignal-service/src/main/protowire/RegistrationProvisioning.proto @@ -19,7 +19,7 @@ message RegistrationProvisionMessage { PAID = 1; } - string e164 = 1; + optional string e164 = 1; bytes aci = 2; string accountEntropyPool = 3; optional string pin = 4; @@ -30,7 +30,7 @@ message RegistrationProvisionMessage { string restoreMethodToken = 9; bytes aciIdentityKeyPublic = 10; bytes aciIdentityKeyPrivate = 11; - bytes pniIdentityKeyPublic = 12; - bytes pniIdentityKeyPrivate = 13; + optional bytes pniIdentityKeyPublic = 12; + optional bytes pniIdentityKeyPrivate = 13; uint64 backupVersion = 14; -} +} \ No newline at end of file