From c66819449d49dde2f53819995d7eed34fa7a172c Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Fri, 14 Mar 2025 18:26:36 -0400 Subject: [PATCH] Convert provisioning and certificate endpoints to WebSocket and finalize attachments. --- ...umentationApplicationDependencyProvider.kt | 6 +- .../changenumber/ChangeNumberRepository.kt | 4 +- .../securesms/dependencies/AppDependencies.kt | 12 +++- .../ApplicationDependencyProvider.java | 18 +++++- .../dependencies/NetworkDependenciesModule.kt | 12 +++- .../securesms/jobs/RotateCertificateJob.java | 13 ++-- .../securesms/net/SignalNetwork.kt | 10 +++ .../data/QuickRegistrationRepository.kt | 11 ++-- .../olddevice/TransferAccountActivity.kt | 2 +- .../olddevice/TransferAccountViewModel.kt | 2 +- .../ui/restore/RemoteRestoreViewModel.kt | 2 +- .../ui/restore/RestoreViaQrViewModel.kt | 2 +- .../securesms/restore/RestoreViewModel.kt | 2 +- .../selection/SelectRestoreMethodFragment.kt | 2 +- .../MockApplicationDependencyProvider.kt | 12 +++- .../api/SignalServiceAccountManager.java | 8 --- .../api/SignalServiceMessageSender.java | 55 ++++++---------- .../api/certificate/CertificateApi.kt | 38 +++++++++++ .../signalservice/api/link/LinkDeviceApi.kt | 2 +- .../api/provisioning/ProvisioningApi.kt | 64 +++++++++++++++++++ .../provisioning}/ProvisioningMessage.java | 2 +- .../ProvisioningSocket.kt | 4 +- .../RestoreMethod.kt | 2 +- .../api/registration/RegistrationApi.kt | 29 +-------- .../api/registration/RestoreMethodBody.kt | 1 + .../api/services/AttachmentService.kt | 32 ---------- .../internal/push/PushServiceSocket.java | 44 ------------- 27 files changed, 208 insertions(+), 183 deletions(-) create mode 100644 libsignal-service/src/main/java/org/whispersystems/signalservice/api/certificate/CertificateApi.kt create mode 100644 libsignal-service/src/main/java/org/whispersystems/signalservice/api/provisioning/ProvisioningApi.kt rename libsignal-service/src/main/java/org/whispersystems/signalservice/{internal/push => api/provisioning}/ProvisioningMessage.java (77%) rename libsignal-service/src/main/java/org/whispersystems/signalservice/api/{registration => provisioning}/ProvisioningSocket.kt (99%) rename libsignal-service/src/main/java/org/whispersystems/signalservice/api/{registration => provisioning}/RestoreMethod.kt (82%) delete mode 100644 libsignal-service/src/main/java/org/whispersystems/signalservice/api/services/AttachmentService.kt diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/dependencies/InstrumentationApplicationDependencyProvider.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/dependencies/InstrumentationApplicationDependencyProvider.kt index f2fcecd276..4c4d089bf0 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/dependencies/InstrumentationApplicationDependencyProvider.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/dependencies/InstrumentationApplicationDependencyProvider.kt @@ -26,10 +26,10 @@ import org.thoughtcrime.securesms.testing.runSync import org.thoughtcrime.securesms.testing.success import org.whispersystems.signalservice.api.SignalServiceDataStore import org.whispersystems.signalservice.api.SignalServiceMessageSender +import org.whispersystems.signalservice.api.attachment.AttachmentApi import org.whispersystems.signalservice.api.keys.KeysApi import org.whispersystems.signalservice.api.message.MessageApi import org.whispersystems.signalservice.api.push.TrustStore -import org.whispersystems.signalservice.api.websocket.SignalWebSocket import org.whispersystems.signalservice.internal.configuration.SignalCdnUrl import org.whispersystems.signalservice.internal.configuration.SignalCdsiUrl import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration @@ -123,14 +123,14 @@ class InstrumentationApplicationDependencyProvider(val application: Application, } override fun provideSignalServiceMessageSender( - authWebSocket: SignalWebSocket.AuthenticatedWebSocket, protocolStore: SignalServiceDataStore, pushServiceSocket: PushServiceSocket, + attachmentApi: AttachmentApi, messageApi: MessageApi, keysApi: KeysApi ): SignalServiceMessageSender { if (signalServiceMessageSender == null) { - signalServiceMessageSender = spyk(objToCopy = default.provideSignalServiceMessageSender(authWebSocket, protocolStore, pushServiceSocket, messageApi, keysApi)) + signalServiceMessageSender = spyk(objToCopy = default.provideSignalServiceMessageSender(protocolStore, pushServiceSocket, attachmentApi, messageApi, keysApi)) } return signalServiceMessageSender!! } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/changenumber/ChangeNumberRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/changenumber/ChangeNumberRepository.kt index 306f60c7e0..da8feb0380 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/changenumber/ChangeNumberRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/changenumber/ChangeNumberRepository.kt @@ -206,8 +206,8 @@ class ChangeNumberRepository( for (certificateType in certificateTypes) { val certificate: ByteArray? = when (certificateType) { - CertificateType.ACI_AND_E164 -> accountManager.senderCertificate - CertificateType.ACI_ONLY -> accountManager.senderCertificateForPhoneNumberPrivacy + CertificateType.ACI_AND_E164 -> SignalNetwork.certificate.getSenderCertificate().successOrThrow() + CertificateType.ACI_ONLY -> SignalNetwork.certificate.getSenderCertificateForPhoneNumberPrivacy().successOrThrow() else -> throw AssertionError() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/dependencies/AppDependencies.kt b/app/src/main/java/org/thoughtcrime/securesms/dependencies/AppDependencies.kt index 39e9394367..aa4e758e55 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/dependencies/AppDependencies.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/dependencies/AppDependencies.kt @@ -47,11 +47,13 @@ import org.whispersystems.signalservice.api.archive.ArchiveApi import org.whispersystems.signalservice.api.attachment.AttachmentApi import org.whispersystems.signalservice.api.calling.CallingApi import org.whispersystems.signalservice.api.cds.CdsApi +import org.whispersystems.signalservice.api.certificate.CertificateApi import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations import org.whispersystems.signalservice.api.keys.KeysApi import org.whispersystems.signalservice.api.link.LinkDeviceApi import org.whispersystems.signalservice.api.message.MessageApi import org.whispersystems.signalservice.api.payments.PaymentsApi +import org.whispersystems.signalservice.api.provisioning.ProvisioningApi import org.whispersystems.signalservice.api.ratelimit.RateLimitChallengeApi import org.whispersystems.signalservice.api.registration.RegistrationApi import org.whispersystems.signalservice.api.services.DonationsService @@ -334,6 +336,12 @@ object AppDependencies { val messageApi: MessageApi get() = networkModule.messageApi + val provisioningApi: ProvisioningApi + get() = networkModule.provisioningApi + + val certificateApi: CertificateApi + get() = networkModule.certificateApi + @JvmStatic val okHttpClient: OkHttpClient get() = networkModule.okHttpClient @@ -362,7 +370,7 @@ object AppDependencies { fun providePushServiceSocket(signalServiceConfiguration: SignalServiceConfiguration, groupsV2Operations: GroupsV2Operations): PushServiceSocket fun provideGroupsV2Operations(signalServiceConfiguration: SignalServiceConfiguration): GroupsV2Operations fun provideSignalServiceAccountManager(authWebSocket: SignalWebSocket.AuthenticatedWebSocket, accountApi: AccountApi, pushServiceSocket: PushServiceSocket, groupsV2Operations: GroupsV2Operations): SignalServiceAccountManager - fun provideSignalServiceMessageSender(authWebSocket: SignalWebSocket.AuthenticatedWebSocket, protocolStore: SignalServiceDataStore, pushServiceSocket: PushServiceSocket, messageApi: MessageApi, keysApi: KeysApi): SignalServiceMessageSender + fun provideSignalServiceMessageSender(protocolStore: SignalServiceDataStore, pushServiceSocket: PushServiceSocket, attachmentApi: AttachmentApi, messageApi: MessageApi, keysApi: KeysApi): SignalServiceMessageSender fun provideSignalServiceMessageReceiver(pushServiceSocket: PushServiceSocket): SignalServiceMessageReceiver fun provideSignalServiceNetworkAccess(): SignalServiceNetworkAccess fun provideRecipientCache(): LiveRecipientCache @@ -411,5 +419,7 @@ object AppDependencies { fun provideCdsApi(authWebSocket: SignalWebSocket.AuthenticatedWebSocket): CdsApi fun provideRateLimitChallengeApi(authWebSocket: SignalWebSocket.AuthenticatedWebSocket): RateLimitChallengeApi fun provideMessageApi(authWebSocket: SignalWebSocket.AuthenticatedWebSocket, unauthWebSocket: SignalWebSocket.UnauthenticatedWebSocket): MessageApi + fun provideProvisioningApi(authWebSocket: SignalWebSocket.AuthenticatedWebSocket): ProvisioningApi + fun provideCertificateApi(authWebSocket: SignalWebSocket.AuthenticatedWebSocket): CertificateApi } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java index 2386411198..937381299d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java +++ b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java @@ -86,12 +86,14 @@ import org.whispersystems.signalservice.api.archive.ArchiveApi; import org.whispersystems.signalservice.api.attachment.AttachmentApi; import org.whispersystems.signalservice.api.calling.CallingApi; import org.whispersystems.signalservice.api.cds.CdsApi; +import org.whispersystems.signalservice.api.certificate.CertificateApi; import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations; import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations; import org.whispersystems.signalservice.api.keys.KeysApi; import org.whispersystems.signalservice.api.link.LinkDeviceApi; import org.whispersystems.signalservice.api.message.MessageApi; import org.whispersystems.signalservice.api.payments.PaymentsApi; +import org.whispersystems.signalservice.api.provisioning.ProvisioningApi; import org.whispersystems.signalservice.api.push.ServiceId.ACI; import org.whispersystems.signalservice.api.push.ServiceId.PNI; import org.whispersystems.signalservice.api.ratelimit.RateLimitChallengeApi; @@ -151,15 +153,15 @@ public class ApplicationDependencyProvider implements AppDependencies.Provider { } @Override - public @NonNull SignalServiceMessageSender provideSignalServiceMessageSender(@NonNull SignalWebSocket.AuthenticatedWebSocket authWebSocket, - @NonNull SignalServiceDataStore protocolStore, + public @NonNull SignalServiceMessageSender provideSignalServiceMessageSender(@NonNull SignalServiceDataStore protocolStore, @NonNull PushServiceSocket pushServiceSocket, + @NonNull AttachmentApi attachmentApi, @NonNull MessageApi messageApi, @NonNull KeysApi keysApi) { return new SignalServiceMessageSender(pushServiceSocket, protocolStore, ReentrantSessionLock.INSTANCE, - authWebSocket, + attachmentApi, messageApi, keysApi, Optional.of(new SecurityEventListener(context)), @@ -535,6 +537,16 @@ public class ApplicationDependencyProvider implements AppDependencies.Provider { return new MessageApi(authWebSocket, unauthWebSocket); } + @Override + public @NonNull ProvisioningApi provideProvisioningApi(@NonNull SignalWebSocket.AuthenticatedWebSocket authWebSocket) { + return new ProvisioningApi(authWebSocket); + } + + @Override + public @NonNull CertificateApi provideCertificateApi(@NonNull SignalWebSocket.AuthenticatedWebSocket authWebSocket) { + return new CertificateApi(authWebSocket); + } + @VisibleForTesting static class DynamicCredentialsProvider implements CredentialsProvider { diff --git a/app/src/main/java/org/thoughtcrime/securesms/dependencies/NetworkDependenciesModule.kt b/app/src/main/java/org/thoughtcrime/securesms/dependencies/NetworkDependenciesModule.kt index 33aba286c5..cd9c2bc439 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/dependencies/NetworkDependenciesModule.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/dependencies/NetworkDependenciesModule.kt @@ -32,11 +32,13 @@ import org.whispersystems.signalservice.api.archive.ArchiveApi import org.whispersystems.signalservice.api.attachment.AttachmentApi import org.whispersystems.signalservice.api.calling.CallingApi import org.whispersystems.signalservice.api.cds.CdsApi +import org.whispersystems.signalservice.api.certificate.CertificateApi import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations import org.whispersystems.signalservice.api.keys.KeysApi import org.whispersystems.signalservice.api.link.LinkDeviceApi import org.whispersystems.signalservice.api.message.MessageApi import org.whispersystems.signalservice.api.payments.PaymentsApi +import org.whispersystems.signalservice.api.provisioning.ProvisioningApi import org.whispersystems.signalservice.api.push.TrustStore import org.whispersystems.signalservice.api.ratelimit.RateLimitChallengeApi import org.whispersystems.signalservice.api.registration.RegistrationApi @@ -82,7 +84,7 @@ class NetworkDependenciesModule( val protocolStore: SignalServiceDataStoreImpl by _protocolStore private val _signalServiceMessageSender = resettableLazy { - provider.provideSignalServiceMessageSender(authWebSocket, protocolStore, pushServiceSocket, messageApi, keysApi) + provider.provideSignalServiceMessageSender(protocolStore, pushServiceSocket, attachmentApi, messageApi, keysApi) } val signalServiceMessageSender: SignalServiceMessageSender by _signalServiceMessageSender @@ -193,6 +195,14 @@ class NetworkDependenciesModule( provider.provideMessageApi(authWebSocket, unauthWebSocket) } + val provisioningApi: ProvisioningApi by lazy { + provider.provideProvisioningApi(authWebSocket) + } + + val certificateApi: CertificateApi by lazy { + provider.provideCertificateApi(authWebSocket) + } + val okHttpClient: OkHttpClient by lazy { OkHttpClient.Builder() .addInterceptor(StandardUserAgentInterceptor()) diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RotateCertificateJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/RotateCertificateJob.java index 243067eb66..bea4b6590e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/RotateCertificateJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RotateCertificateJob.java @@ -4,13 +4,13 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import org.signal.core.util.logging.Log; -import org.thoughtcrime.securesms.dependencies.AppDependencies; import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; import org.thoughtcrime.securesms.keyvalue.CertificateType; import org.thoughtcrime.securesms.keyvalue.SignalStore; -import org.whispersystems.signalservice.api.SignalServiceAccountManager; -import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; +import org.thoughtcrime.securesms.net.SignalNetwork; +import org.thoughtcrime.securesms.util.ExceptionHelper; +import org.whispersystems.signalservice.api.NetworkResultUtil; import java.io.IOException; import java.util.Collection; @@ -56,7 +56,6 @@ public final class RotateCertificateJob extends BaseJob { } synchronized (RotateCertificateJob.class) { - SignalServiceAccountManager accountManager = AppDependencies.getSignalServiceAccountManager(); Collection certificateTypes = SignalStore.phoneNumberPrivacy() .getAllCertificateTypes(); @@ -66,8 +65,8 @@ public final class RotateCertificateJob extends BaseJob { byte[] certificate; switch (certificateType) { - case ACI_AND_E164: certificate = accountManager.getSenderCertificate(); break; - case ACI_ONLY : certificate = accountManager.getSenderCertificateForPhoneNumberPrivacy(); break; + case ACI_AND_E164: certificate = NetworkResultUtil.toBasicLegacy(SignalNetwork.certificate().getSenderCertificate()); break; + case ACI_ONLY : certificate = NetworkResultUtil.toBasicLegacy(SignalNetwork.certificate().getSenderCertificateForPhoneNumberPrivacy()); break; default : throw new AssertionError(); } @@ -80,7 +79,7 @@ public final class RotateCertificateJob extends BaseJob { @Override public boolean onShouldRetry(@NonNull Exception e) { - return e instanceof PushNetworkException; + return ExceptionHelper.isRetryableIOException(e); } @Override diff --git a/app/src/main/java/org/thoughtcrime/securesms/net/SignalNetwork.kt b/app/src/main/java/org/thoughtcrime/securesms/net/SignalNetwork.kt index fcf486b60a..c6955dbb96 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/net/SignalNetwork.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/net/SignalNetwork.kt @@ -11,10 +11,12 @@ import org.whispersystems.signalservice.api.archive.ArchiveApi import org.whispersystems.signalservice.api.attachment.AttachmentApi import org.whispersystems.signalservice.api.calling.CallingApi import org.whispersystems.signalservice.api.cds.CdsApi +import org.whispersystems.signalservice.api.certificate.CertificateApi import org.whispersystems.signalservice.api.keys.KeysApi import org.whispersystems.signalservice.api.link.LinkDeviceApi import org.whispersystems.signalservice.api.message.MessageApi import org.whispersystems.signalservice.api.payments.PaymentsApi +import org.whispersystems.signalservice.api.provisioning.ProvisioningApi import org.whispersystems.signalservice.api.ratelimit.RateLimitChallengeApi import org.whispersystems.signalservice.api.storage.StorageServiceApi import org.whispersystems.signalservice.api.username.UsernameApi @@ -42,6 +44,11 @@ object SignalNetwork { val cdsApi: CdsApi get() = AppDependencies.cdsApi + @JvmStatic + @get:JvmName("certificate") + val certificate: CertificateApi + get() = AppDependencies.certificateApi + @JvmStatic @get:JvmName("keys") val keys: KeysApi @@ -60,6 +67,9 @@ object SignalNetwork { val payments: PaymentsApi get() = AppDependencies.paymentsApi + val provisioning: ProvisioningApi + get() = AppDependencies.provisioningApi + @JvmStatic @get:JvmName("rateLimitChallenge") val rateLimitChallenge: RateLimitChallengeApi diff --git a/app/src/main/java/org/thoughtcrime/securesms/registrationv3/data/QuickRegistrationRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/registrationv3/data/QuickRegistrationRepository.kt index 5029ac58ab..d4613b3a4e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registrationv3/data/QuickRegistrationRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registrationv3/data/QuickRegistrationRepository.kt @@ -19,8 +19,9 @@ import org.signal.registration.proto.RegistrationProvisionMessage import org.thoughtcrime.securesms.backup.v2.MessageBackupTier import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.net.SignalNetwork import org.whispersystems.signalservice.api.NetworkResult -import org.whispersystems.signalservice.api.registration.RestoreMethod +import org.whispersystems.signalservice.api.provisioning.RestoreMethod import java.io.IOException import kotlin.coroutines.coroutineContext import kotlin.time.Duration.Companion.seconds @@ -71,9 +72,8 @@ object QuickRegistrationRepository { return TransferAccountResult.FAILED } - AppDependencies - .signalServiceAccountManager - .registrationApi + SignalNetwork + .provisioning .sendReRegisterDeviceProvisioningMessage( ephemeralId, publicKey, @@ -147,8 +147,7 @@ object QuickRegistrationRepository { Log.d(TAG, "Waiting for restore method with token: ***${restoreMethodToken.takeLast(4)}") while (retries-- > 0 && result !is NetworkResult.Success && coroutineContext.isActive) { Log.d(TAG, "Waiting, remaining tries: $retries") - val api = AppDependencies.registrationApi - result = api.waitForRestoreMethod(restoreMethodToken) + result = SignalNetwork.provisioning.waitForRestoreMethod(restoreMethodToken) Log.d(TAG, "Result: $result") } diff --git a/app/src/main/java/org/thoughtcrime/securesms/registrationv3/olddevice/TransferAccountActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/registrationv3/olddevice/TransferAccountActivity.kt index e606da80e7..2a760e6293 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registrationv3/olddevice/TransferAccountActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registrationv3/olddevice/TransferAccountActivity.kt @@ -69,7 +69,7 @@ import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme import org.thoughtcrime.securesms.util.DynamicTheme import org.thoughtcrime.securesms.util.SpanUtil import org.thoughtcrime.securesms.util.viewModel -import org.whispersystems.signalservice.api.registration.RestoreMethod +import org.whispersystems.signalservice.api.provisioning.RestoreMethod /** * Launched after scanning QR code from new device to start the transfer/reregistration process from diff --git a/app/src/main/java/org/thoughtcrime/securesms/registrationv3/olddevice/TransferAccountViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/registrationv3/olddevice/TransferAccountViewModel.kt index ae3273b595..aab30b3d24 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registrationv3/olddevice/TransferAccountViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registrationv3/olddevice/TransferAccountViewModel.kt @@ -14,7 +14,7 @@ import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.registrationv3.data.QuickRegistrationRepository -import org.whispersystems.signalservice.api.registration.RestoreMethod +import org.whispersystems.signalservice.api.provisioning.RestoreMethod import java.util.UUID class TransferAccountViewModel(reRegisterUri: String) : ViewModel() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/RemoteRestoreViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/RemoteRestoreViewModel.kt index 8f94417b7c..51e8cdbbd2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/RemoteRestoreViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/RemoteRestoreViewModel.kt @@ -32,7 +32,7 @@ import org.thoughtcrime.securesms.keyvalue.Completed import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.keyvalue.Skipped import org.thoughtcrime.securesms.registrationv3.data.QuickRegistrationRepository -import org.whispersystems.signalservice.api.registration.RestoreMethod +import org.whispersystems.signalservice.api.provisioning.RestoreMethod class RemoteRestoreViewModel(isOnlyRestoreOption: Boolean) : ViewModel() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/RestoreViaQrViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/RestoreViaQrViewModel.kt index 44d8082c5b..03427e4d99 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/RestoreViaQrViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/RestoreViaQrViewModel.kt @@ -23,7 +23,7 @@ import org.thoughtcrime.securesms.crypto.IdentityKeyUtil import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.registration.data.network.RegisterAccountResult -import org.whispersystems.signalservice.api.registration.ProvisioningSocket +import org.whispersystems.signalservice.api.provisioning.ProvisioningSocket import org.whispersystems.signalservice.internal.crypto.SecondaryProvisioningCipher import java.io.Closeable diff --git a/app/src/main/java/org/thoughtcrime/securesms/restore/RestoreViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/restore/RestoreViewModel.kt index 2a2009f37c..b363b1ff87 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/restore/RestoreViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/restore/RestoreViewModel.kt @@ -25,7 +25,7 @@ import org.thoughtcrime.securesms.registrationv3.data.QuickRegistrationRepositor import org.thoughtcrime.securesms.registrationv3.ui.restore.RestoreMethod import org.thoughtcrime.securesms.registrationv3.ui.restore.StorageServiceRestore import org.thoughtcrime.securesms.restore.transferorrestore.BackupRestorationType -import org.whispersystems.signalservice.api.registration.RestoreMethod as ApiRestoreMethod +import org.whispersystems.signalservice.api.provisioning.RestoreMethod as ApiRestoreMethod /** * Shared view model for the restore flow. diff --git a/app/src/main/java/org/thoughtcrime/securesms/restore/selection/SelectRestoreMethodFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/restore/selection/SelectRestoreMethodFragment.kt index 85b595b6cb..d0963f1728 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/restore/selection/SelectRestoreMethodFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/restore/selection/SelectRestoreMethodFragment.kt @@ -22,7 +22,7 @@ import org.thoughtcrime.securesms.registrationv3.ui.restore.RestoreMethod import org.thoughtcrime.securesms.registrationv3.ui.restore.SelectRestoreMethodScreen import org.thoughtcrime.securesms.restore.RestoreViewModel import org.thoughtcrime.securesms.util.navigation.safeNavigate -import org.whispersystems.signalservice.api.registration.RestoreMethod as ApiRestoreMethod +import org.whispersystems.signalservice.api.provisioning.RestoreMethod as ApiRestoreMethod /** * Provide options to select restore/transfer operation during quick/post registration. diff --git a/app/src/test/java/org/thoughtcrime/securesms/dependencies/MockApplicationDependencyProvider.kt b/app/src/test/java/org/thoughtcrime/securesms/dependencies/MockApplicationDependencyProvider.kt index ae078938cf..4c40824f66 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/dependencies/MockApplicationDependencyProvider.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/dependencies/MockApplicationDependencyProvider.kt @@ -41,11 +41,13 @@ import org.whispersystems.signalservice.api.archive.ArchiveApi import org.whispersystems.signalservice.api.attachment.AttachmentApi import org.whispersystems.signalservice.api.calling.CallingApi import org.whispersystems.signalservice.api.cds.CdsApi +import org.whispersystems.signalservice.api.certificate.CertificateApi import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations import org.whispersystems.signalservice.api.keys.KeysApi import org.whispersystems.signalservice.api.link.LinkDeviceApi import org.whispersystems.signalservice.api.message.MessageApi import org.whispersystems.signalservice.api.payments.PaymentsApi +import org.whispersystems.signalservice.api.provisioning.ProvisioningApi import org.whispersystems.signalservice.api.ratelimit.RateLimitChallengeApi import org.whispersystems.signalservice.api.registration.RegistrationApi import org.whispersystems.signalservice.api.services.DonationsService @@ -71,9 +73,9 @@ class MockApplicationDependencyProvider : AppDependencies.Provider { } override fun provideSignalServiceMessageSender( - authWebSocket: SignalWebSocket.AuthenticatedWebSocket, protocolStore: SignalServiceDataStore, pushServiceSocket: PushServiceSocket, + attachmentApi: AttachmentApi, messageApi: MessageApi, keysApi: KeysApi ): SignalServiceMessageSender { @@ -276,4 +278,12 @@ class MockApplicationDependencyProvider : AppDependencies.Provider { override fun provideMessageApi(authWebSocket: SignalWebSocket.AuthenticatedWebSocket, unauthWebSocket: SignalWebSocket.UnauthenticatedWebSocket): MessageApi { return mockk(relaxed = true) } + + override fun provideProvisioningApi(authWebSocket: SignalWebSocket.AuthenticatedWebSocket): ProvisioningApi { + return mockk(relaxed = true) + } + + override fun provideCertificateApi(authWebSocket: SignalWebSocket.AuthenticatedWebSocket): CertificateApi { + return mockk(relaxed = true) + } } diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceAccountManager.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceAccountManager.java index d8ef18ab11..d0bebf928a 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceAccountManager.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceAccountManager.java @@ -106,14 +106,6 @@ public class SignalServiceAccountManager { this.configuration = pushServiceSocket.getConfiguration(); } - public byte[] getSenderCertificate() throws IOException { - return this.pushServiceSocket.getSenderCertificate(); - } - - public byte[] getSenderCertificateForPhoneNumberPrivacy() throws IOException { - return this.pushServiceSocket.getUuidOnlySenderCertificate(); - } - public SecureValueRecoveryV2 getSecureValueRecoveryV2(String mrEnclave) { return new SecureValueRecoveryV2(configuration, mrEnclave, authWebSocket); } diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java index eecd6e7121..9e23b57811 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java @@ -21,6 +21,7 @@ import org.signal.libsignal.protocol.message.SenderKeyDistributionMessage; import org.signal.libsignal.protocol.state.PreKeyBundle; import org.signal.libsignal.protocol.state.SessionRecord; import org.signal.libsignal.zkgroup.groupsend.GroupSendFullToken; +import org.whispersystems.signalservice.api.attachment.AttachmentApi; import org.whispersystems.signalservice.api.crypto.AttachmentCipherStreamUtil; import org.whispersystems.signalservice.api.crypto.ContentHint; import org.whispersystems.signalservice.api.crypto.EnvelopeContent; @@ -77,14 +78,12 @@ import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException import org.whispersystems.signalservice.api.push.exceptions.RateLimitException; import org.whispersystems.signalservice.api.push.exceptions.ServerRejectedException; import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException; -import org.whispersystems.signalservice.api.services.AttachmentService; import org.whispersystems.signalservice.api.util.AttachmentPointerUtil; import org.whispersystems.signalservice.api.util.CredentialsProvider; import org.whispersystems.signalservice.api.util.Preconditions; import org.whispersystems.signalservice.api.util.Uint64RangeException; import org.whispersystems.signalservice.api.util.Uint64Util; import org.whispersystems.signalservice.api.util.UuidUtil; -import org.whispersystems.signalservice.api.websocket.SignalWebSocket; import org.whispersystems.signalservice.api.websocket.WebSocketUnavailableException; import org.whispersystems.signalservice.internal.crypto.AttachmentDigest; import org.whispersystems.signalservice.internal.crypto.PaddingInputStream; @@ -175,9 +174,9 @@ public class SignalServiceMessageSender { private final Optional eventListener; private final IdentityKeyPair localPniIdentity; - private final AttachmentService attachmentService; - private final MessageApi messageApi; - private final KeysApi keysApi; + private final AttachmentApi attachmentApi; + private final MessageApi messageApi; + private final KeysApi keysApi; private final Scheduler scheduler; private final long maxEnvelopeSize; @@ -185,7 +184,7 @@ public class SignalServiceMessageSender { public SignalServiceMessageSender(PushServiceSocket pushServiceSocket, SignalServiceDataStore store, SignalSessionLock sessionLock, - SignalWebSocket.AuthenticatedWebSocket authWebSocket, + AttachmentApi attachmentApi, MessageApi messageApi, KeysApi keysApi, Optional eventListener, @@ -194,19 +193,19 @@ public class SignalServiceMessageSender { { CredentialsProvider credentialsProvider = pushServiceSocket.getCredentialsProvider(); - this.socket = pushServiceSocket; - this.aciStore = store.aci(); - this.sessionLock = sessionLock; - this.localAddress = new SignalServiceAddress(credentialsProvider.getAci(), credentialsProvider.getE164()); - this.localDeviceId = credentialsProvider.getDeviceId(); - this.localPni = credentialsProvider.getPni(); - this.attachmentService = new AttachmentService(authWebSocket); - this.messageApi = messageApi; - this.eventListener = eventListener; - this.maxEnvelopeSize = maxEnvelopeSize; - this.localPniIdentity = store.pni().getIdentityKeyPair(); - this.scheduler = Schedulers.from(executor, false, false); - this.keysApi = keysApi; + this.socket = pushServiceSocket; + this.aciStore = store.aci(); + this.sessionLock = sessionLock; + this.localAddress = new SignalServiceAddress(credentialsProvider.getAci(), credentialsProvider.getE164()); + this.localDeviceId = credentialsProvider.getDeviceId(); + this.localPni = credentialsProvider.getPni(); + this.attachmentApi = attachmentApi; + this.messageApi = messageApi; + this.eventListener = eventListener; + this.maxEnvelopeSize = maxEnvelopeSize; + this.localPniIdentity = store.pni().getIdentityKeyPair(); + this.scheduler = Schedulers.from(executor, false, false); + this.keysApi = keysApi; } /** @@ -810,24 +809,8 @@ public class SignalServiceMessageSender { } public ResumableUploadSpec getResumableUploadSpec() throws IOException { - AttachmentUploadForm v4UploadAttributes = null; - Log.d(TAG, "Using pipe to retrieve attachment upload attributes..."); - try { - v4UploadAttributes = new AttachmentService.AttachmentAttributesResponseProcessor<>(attachmentService.getAttachmentV4UploadAttributes().blockingGet()).getResultOrThrow(); - } catch (WebSocketUnavailableException e) { - Log.w(TAG, "[getResumableUploadSpec] Pipe unavailable, falling back... (" + e.getClass().getSimpleName() + ": " + e.getMessage() + ")"); - } catch (IOException e) { - if (e instanceof RateLimitException) { - throw e; - } - Log.w(TAG, "Failed to retrieve attachment upload attributes using pipe. Falling back..."); - } - - if (v4UploadAttributes == null) { - Log.d(TAG, "Not using pipe to retrieve attachment upload attributes..."); - v4UploadAttributes = socket.getAttachmentV4UploadAttributes(); - } + AttachmentUploadForm v4UploadAttributes = NetworkResultUtil.toBasicLegacy(attachmentApi.getAttachmentV4UploadForm()); return socket.getResumableUploadSpec(v4UploadAttributes); } diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/certificate/CertificateApi.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/certificate/CertificateApi.kt new file mode 100644 index 0000000000..131867f399 --- /dev/null +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/certificate/CertificateApi.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api.certificate + +import org.whispersystems.signalservice.api.NetworkResult +import org.whispersystems.signalservice.api.websocket.SignalWebSocket +import org.whispersystems.signalservice.internal.get +import org.whispersystems.signalservice.internal.push.SenderCertificate +import org.whispersystems.signalservice.internal.websocket.WebSocketRequestMessage + +/** + * Endpoints to get [SenderCertificate]s. + */ +class CertificateApi(private val authWebSocket: SignalWebSocket.AuthenticatedWebSocket) { + + /** + * GET /v1/certificate/delivery + * - 200: Success + */ + fun getSenderCertificate(): NetworkResult { + val request = WebSocketRequestMessage.get("/v1/certificate/delivery") + return NetworkResult.fromWebSocketRequest(authWebSocket, request, SenderCertificate::class) + .map { it.certificate } + } + + /** + * GET /v1/certificate/delivery?includeE164=false + * - 200: Success + */ + fun getSenderCertificateForPhoneNumberPrivacy(): NetworkResult { + val request = WebSocketRequestMessage.get("/v1/certificate/delivery?includeE164=false") + return NetworkResult.fromWebSocketRequest(authWebSocket, request, SenderCertificate::class) + .map { it.certificate } + } +} diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/link/LinkDeviceApi.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/link/LinkDeviceApi.kt index 41f296fa20..2a1b41d517 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/link/LinkDeviceApi.kt +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/link/LinkDeviceApi.kt @@ -16,6 +16,7 @@ import org.whispersystems.signalservice.api.backup.MediaRootBackupKey import org.whispersystems.signalservice.api.backup.MessageBackupKey import org.whispersystems.signalservice.api.kbs.MasterKey import org.whispersystems.signalservice.api.messages.multidevice.DeviceInfo +import org.whispersystems.signalservice.api.provisioning.ProvisioningMessage import org.whispersystems.signalservice.api.push.ServiceId.ACI import org.whispersystems.signalservice.api.push.ServiceId.PNI import org.whispersystems.signalservice.api.websocket.SignalWebSocket @@ -24,7 +25,6 @@ import org.whispersystems.signalservice.internal.delete import org.whispersystems.signalservice.internal.get import org.whispersystems.signalservice.internal.push.DeviceInfoList import org.whispersystems.signalservice.internal.push.ProvisionMessage -import org.whispersystems.signalservice.internal.push.ProvisioningMessage import org.whispersystems.signalservice.internal.push.ProvisioningVersion import org.whispersystems.signalservice.internal.put import org.whispersystems.signalservice.internal.websocket.WebSocketRequestMessage diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/provisioning/ProvisioningApi.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/provisioning/ProvisioningApi.kt new file mode 100644 index 0000000000..43dedc8c76 --- /dev/null +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/provisioning/ProvisioningApi.kt @@ -0,0 +1,64 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api.provisioning + +import org.signal.core.util.Base64 +import org.signal.core.util.urlEncode +import org.signal.libsignal.protocol.ecc.ECPublicKey +import org.signal.registration.proto.RegistrationProvisionMessage +import org.whispersystems.signalservice.api.NetworkResult +import org.whispersystems.signalservice.api.registration.RestoreMethodBody +import org.whispersystems.signalservice.api.websocket.SignalWebSocket +import org.whispersystems.signalservice.internal.crypto.PrimaryProvisioningCipher +import org.whispersystems.signalservice.internal.get +import org.whispersystems.signalservice.internal.put +import org.whispersystems.signalservice.internal.websocket.WebSocketRequestMessage +import kotlin.time.Duration.Companion.seconds + +/** + * Linked and new device provisioning endpoints. + */ +class ProvisioningApi(private val authWebSocket: SignalWebSocket.AuthenticatedWebSocket) { + + /** + * Encrypts and sends the [registrationProvisionMessage] from the current primary (old device) to the new device over + * the provisioning web socket identified by [deviceIdentifier]. + * + * PUT /v1/provisioning/[deviceIdentifier] + * - 204: Success + * - 400: Message was too large + * - 404: No provisioning socket for [deviceIdentifier] + */ + fun sendReRegisterDeviceProvisioningMessage( + deviceIdentifier: String, + deviceKey: ECPublicKey, + registrationProvisionMessage: RegistrationProvisionMessage + ): NetworkResult { + val cipherText = PrimaryProvisioningCipher(deviceKey).encrypt(registrationProvisionMessage) + + val request = WebSocketRequestMessage.put("/v1/provisioning/${deviceIdentifier.urlEncode()}", ProvisioningMessage(Base64.encodeWithPadding(cipherText))) + return NetworkResult.fromWebSocketRequest(authWebSocket, request) + } + + /** + * Wait for the [RestoreMethod] to be set on the server by the new device. This is a long polling operation. + * + * GET /v1/devices/restore_account/[token]?timeout=[timeout] + * - 200: A request was received for the given token + * - 204: No request given yet, may repeat call to continue waiting + * - 400: Invalid [token] or [timeout] + * - 429: Rate limited + */ + fun waitForRestoreMethod(token: String, timeout: Int = 30): NetworkResult { + val request = WebSocketRequestMessage.get("/v1/devices/restore_account/${token.urlEncode()}?timeout=$timeout") + + return NetworkResult.fromWebSocket(NetworkResult.LongPollingWebSocketConverter(RestoreMethodBody::class)) { + authWebSocket.request(request, timeout.seconds) + }.map { + it.method ?: RestoreMethod.DECLINE + } + } +} diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/ProvisioningMessage.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/provisioning/ProvisioningMessage.java similarity index 77% rename from libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/ProvisioningMessage.java rename to libsignal-service/src/main/java/org/whispersystems/signalservice/api/provisioning/ProvisioningMessage.java index 2475bdbe9f..e20147cde5 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/ProvisioningMessage.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/provisioning/ProvisioningMessage.java @@ -1,4 +1,4 @@ -package org.whispersystems.signalservice.internal.push; +package org.whispersystems.signalservice.api.provisioning; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/registration/ProvisioningSocket.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/provisioning/ProvisioningSocket.kt similarity index 99% rename from libsignal-service/src/main/java/org/whispersystems/signalservice/api/registration/ProvisioningSocket.kt rename to libsignal-service/src/main/java/org/whispersystems/signalservice/api/provisioning/ProvisioningSocket.kt index 86dc268aeb..3049101ede 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/registration/ProvisioningSocket.kt +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/provisioning/ProvisioningSocket.kt @@ -1,9 +1,9 @@ /* - * Copyright 2024 Signal Messenger, LLC + * Copyright 2025 Signal Messenger, LLC * SPDX-License-Identifier: AGPL-3.0-only */ -package org.whispersystems.signalservice.api.registration +package org.whispersystems.signalservice.api.provisioning import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CompletableDeferred diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/registration/RestoreMethod.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/provisioning/RestoreMethod.kt similarity index 82% rename from libsignal-service/src/main/java/org/whispersystems/signalservice/api/registration/RestoreMethod.kt rename to libsignal-service/src/main/java/org/whispersystems/signalservice/api/provisioning/RestoreMethod.kt index 93dcbd7d61..f640c1b481 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/registration/RestoreMethod.kt +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/provisioning/RestoreMethod.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -package org.whispersystems.signalservice.api.registration +package org.whispersystems.signalservice.api.provisioning /** * Restore method chosen by user on new device after performing a quick-restore. diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/registration/RegistrationApi.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/registration/RegistrationApi.kt index 85bdca2357..eafb34e22b 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/registration/RegistrationApi.kt +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/registration/RegistrationApi.kt @@ -5,12 +5,10 @@ package org.whispersystems.signalservice.api.registration -import org.signal.libsignal.protocol.ecc.ECPublicKey -import org.signal.registration.proto.RegistrationProvisionMessage import org.whispersystems.signalservice.api.NetworkResult import org.whispersystems.signalservice.api.account.AccountAttributes import org.whispersystems.signalservice.api.account.PreKeyCollection -import org.whispersystems.signalservice.internal.crypto.PrimaryProvisioningCipher +import org.whispersystems.signalservice.api.provisioning.RestoreMethod import org.whispersystems.signalservice.internal.push.BackupV2AuthCheckResponse import org.whispersystems.signalservice.internal.push.BackupV3AuthCheckResponse import org.whispersystems.signalservice.internal.push.PushServiceSocket @@ -127,22 +125,6 @@ class RegistrationApi( } } - /** - * Encrypts and sends the [RegistrationProvisionMessage] from the current primary (old device) to the new device over - * the provisioning web socket identified by [deviceIdentifier]. - */ - fun sendReRegisterDeviceProvisioningMessage( - deviceIdentifier: String, - deviceKey: ECPublicKey, - registrationProvisionMessage: RegistrationProvisionMessage - ): NetworkResult { - val cipherText = PrimaryProvisioningCipher(deviceKey).encrypt(registrationProvisionMessage) - - return NetworkResult.fromFetch { - pushServiceSocket.sendProvisioningMessage(deviceIdentifier, cipherText) - } - } - /** * Set [RestoreMethod] enum on the server for use by the old device to update UX. */ @@ -151,13 +133,4 @@ class RegistrationApi( pushServiceSocket.setRestoreMethodChosen(token, RestoreMethodBody(method = method)) } } - - /** - * Wait for the [RestoreMethod] to be set on the server by the new device. This is a long polling operation. - */ - fun waitForRestoreMethod(token: String, timeout: Int = 30): NetworkResult { - return NetworkResult.fromFetch { - pushServiceSocket.waitForRestoreMethodChosen(token, timeout).method ?: RestoreMethod.DECLINE - } - } } diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/registration/RestoreMethodBody.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/registration/RestoreMethodBody.kt index 46f69041d3..5660c3f45a 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/registration/RestoreMethodBody.kt +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/registration/RestoreMethodBody.kt @@ -7,6 +7,7 @@ package org.whispersystems.signalservice.api.registration import com.fasterxml.jackson.annotation.JsonCreator import com.fasterxml.jackson.annotation.JsonProperty +import org.whispersystems.signalservice.api.provisioning.RestoreMethod /** * Request and response body used to communicate a quick restore method selection during registration. diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/services/AttachmentService.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/services/AttachmentService.kt deleted file mode 100644 index 0f276a3133..0000000000 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/services/AttachmentService.kt +++ /dev/null @@ -1,32 +0,0 @@ -package org.whispersystems.signalservice.api.services - -import io.reactivex.rxjava3.core.Single -import org.whispersystems.signalservice.api.websocket.SignalWebSocket -import org.whispersystems.signalservice.internal.ServiceResponse -import org.whispersystems.signalservice.internal.ServiceResponseProcessor -import org.whispersystems.signalservice.internal.push.AttachmentUploadForm -import org.whispersystems.signalservice.internal.websocket.DefaultResponseMapper -import org.whispersystems.signalservice.internal.websocket.WebSocketRequestMessage -import org.whispersystems.signalservice.internal.websocket.WebsocketResponse -import java.security.SecureRandom - -/** - * Provide WebSocket based interface to attachment upload endpoints. - * - * Note: To be expanded to have REST fallback and other attachment related operations. - */ -class AttachmentService(private val authWebSocket: SignalWebSocket.AuthenticatedWebSocket) { - fun getAttachmentV4UploadAttributes(): Single> { - val requestMessage = WebSocketRequestMessage( - id = SecureRandom().nextLong(), - verb = "GET", - path = "/v4/attachments/form/upload" - ) - - return authWebSocket.request(requestMessage) - .map { response: WebsocketResponse? -> DefaultResponseMapper.getDefault(AttachmentUploadForm::class.java).map(response) } - .onErrorReturn { throwable: Throwable? -> ServiceResponse.forUnknownError(throwable) } - } - - class AttachmentAttributesResponseProcessor(response: ServiceResponse) : ServiceResponseProcessor(response) -} diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java index 4caef37436..84ceb5ccb5 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java @@ -185,19 +185,13 @@ public class PushServiceSocket { private static final String DELETE_ACCOUNT_PATH = "/v1/accounts/me"; - private static final String PROVISIONING_MESSAGE_PATH = "/v1/provisioning/%s"; private static final String SET_RESTORE_METHOD_PATH = "/v1/devices/restore_account/%s"; - private static final String WAIT_RESTORE_METHOD_PATH = "/v1/devices/restore_account/%s?timeout=%s"; private static final String GROUP_MESSAGE_PATH = "/v1/messages/multi_recipient?ts=%s&online=%s&urgent=%s&story=%s"; - private static final String ATTACHMENT_V4_PATH = "/v4/attachments/form/upload"; private static final String PROFILE_PATH = "/v1/profile/%s"; private static final String PROFILE_BATCH_CHECK_PATH = "/v1/profile/identity_check/batch"; - private static final String SENDER_CERTIFICATE_PATH = "/v1/certificate/delivery"; - private static final String SENDER_CERTIFICATE_NO_E164_PATH = "/v1/certificate/delivery?includeE164=false"; - private static final String ATTACHMENT_KEY_DOWNLOAD_PATH = "attachments/%s"; private static final String ATTACHMENT_ID_DOWNLOAD_PATH = "attachments/%d"; private static final String AVATAR_UPLOAD_PATH = ""; @@ -242,11 +236,8 @@ public class PushServiceSocket { private static final String ARCHIVE_MEDIA_DOWNLOAD_PATH = "backups/%s/%s"; - private static final String SERVER_DELIVERED_TIMESTAMP_HEADER = "X-Signal-Timestamp"; - private static final Map NO_HEADERS = Collections.emptyMap(); private static final ResponseCodeHandler NO_HANDLER = new EmptyResponseCodeHandler(); - private static final ResponseCodeHandler LONG_POLL_HANDLER = new LongPollingResponseCodeHandler(); private static final ResponseCodeHandler UNOPINIONATED_HANDLER = new UnopinionatedResponseCodeHandler(); private static final ResponseCodeHandler UNOPINIONATED_BINARY_ERROR_HANDLER = new UnopinionatedBinaryErrorResponseCodeHandler(); @@ -404,33 +395,10 @@ public class PushServiceSocket { makeServiceRequest(String.format(Locale.US, SET_RESTORE_METHOD_PATH, urlEncode(token)), "PUT", body, NO_HEADERS, UNOPINIONATED_HANDLER, SealedSenderAccess.NONE); } - /** - * This is a long-polling endpoint that relies on the fact that our normal connection timeout is already 30s. - */ - public @Nonnull RestoreMethodBody waitForRestoreMethodChosen(@Nonnull String token, int timeoutSeconds) throws IOException { - String response = makeServiceRequest(String.format(Locale.US, WAIT_RESTORE_METHOD_PATH, urlEncode(token), timeoutSeconds), "GET", null, NO_HEADERS, LONG_POLL_HANDLER, SealedSenderAccess.NONE); - return JsonUtil.fromJsonResponse(response, RestoreMethodBody.class); - } - - public void sendProvisioningMessage(String destination, byte[] body) throws IOException { - makeServiceRequest(String.format(PROVISIONING_MESSAGE_PATH, urlEncode(destination)), "PUT", - JsonUtil.toJson(new ProvisioningMessage(Base64.encodeWithPadding(body)))); - } - public void requestPushChallenge(String sessionId, String gcmRegistrationId) throws IOException { patchVerificationSession(sessionId, gcmRegistrationId, null, null, null, null); } - public byte[] getSenderCertificate() throws IOException { - String responseText = makeServiceRequest(SENDER_CERTIFICATE_PATH, "GET", null); - return JsonUtil.fromJson(responseText, SenderCertificate.class).getCertificate(); - } - - public byte[] getUuidOnlySenderCertificate() throws IOException { - String responseText = makeServiceRequest(SENDER_CERTIFICATE_NO_E164_PATH, "GET", null); - return JsonUtil.fromJson(responseText, SenderCertificate.class).getCertificate(); - } - public SendGroupMessageResponse sendGroupMessage(byte[] body, @Nonnull SealedSenderAccess sealedSenderAccess, long timestamp, boolean online, boolean urgent, boolean story) throws NonSuccessfulResponseCodeException, PushNetworkException, MalformedResponseException { @@ -888,18 +856,6 @@ public class PushServiceSocket { } } - public AttachmentUploadForm getAttachmentV4UploadAttributes() - throws NonSuccessfulResponseCodeException, PushNetworkException, MalformedResponseException - { - String response = makeServiceRequest(ATTACHMENT_V4_PATH, "GET", null); - try { - return JsonUtil.fromJson(response, AttachmentUploadForm.class); - } catch (IOException e) { - Log.w(TAG, e); - throw new MalformedResponseException("Unable to parse entity", e); - } - } - public AttachmentDigest uploadGroupV2Avatar(byte[] avatarCipherText, AvatarUploadAttributes uploadAttributes) throws IOException {