├── .github ├── stale.yml └── FUNDING.yml ├── .java-version ├── service └── src │ ├── main │ ├── resources │ │ ├── org │ │ │ ├── signal │ │ │ │ ├── badges │ │ │ │ │ └── Badges_en.properties │ │ │ │ └── bankmandate │ │ │ │ │ └── BankMandate.properties │ │ │ └── whispersystems │ │ │ │ └── textsecuregcm │ │ │ │ └── storage │ │ │ │ └── devicecheck │ │ │ │ └── apple_device_check.pem │ │ ├── META-INF │ │ │ ├── services │ │ │ │ ├── org.whispersystems.textsecuregcm.configuration.AwsCredentialsProviderFactory │ │ │ │ ├── io.dropwizard.logging.common.AppenderFactory │ │ │ │ ├── io.dropwizard.logging.common.filter.FilterFactory │ │ │ │ └── io.dropwizard.jackson.Discoverable │ │ │ ├── validation.xml │ │ │ └── validation │ │ │ │ └── constraints-custom.xml │ │ ├── lua │ │ │ ├── get_delivery_attempt_count.lua │ │ │ ├── unlock_queue.lua │ │ │ ├── get_queues_to_persist.lua │ │ │ ├── remove_recipient_view_from_mrm_data.lua │ │ │ ├── apn │ │ │ │ └── schedule_background_notification.lua │ │ │ └── insert_shared_multirecipient_message_data.lua │ │ └── banner.txt │ ├── graphql │ │ └── braintree │ │ │ ├── VaultPaymentMethod.graphql │ │ │ ├── CreatePayPalBillingAgreement.graphql │ │ │ ├── TokenizePayPalBillingAgreement.graphql │ │ │ ├── CreatePayPalOneTimePayment.graphql │ │ │ ├── ChargePayPalOneTimePayment.graphql │ │ │ └── TokenizePayPalOneTimePayment.graphql │ ├── java │ │ └── org │ │ │ ├── whispersystems │ │ │ └── textsecuregcm │ │ │ │ ├── entities │ │ │ │ ├── CreateCallLinkCredential.java │ │ │ │ ├── AvatarChange.java │ │ │ │ ├── PhoneNumberDiscoverabilityRequest.java │ │ │ │ ├── GetCreateCallLinkCredentialsRequest.java │ │ │ │ ├── DeviceInfoList.java │ │ │ │ ├── LinkDeviceResponse.java │ │ │ │ ├── PreKey.java │ │ │ │ ├── ApnRegistrationId.java │ │ │ │ ├── GcmRegistrationId.java │ │ │ │ ├── OutgoingMessageEntityList.java │ │ │ │ ├── SubmitVerificationCodeRequest.java │ │ │ │ ├── RegistrationLock.java │ │ │ │ ├── SendMessageResponse.java │ │ │ │ ├── ProvisioningMessage.java │ │ │ │ ├── RemoteConfigurationResponse.java │ │ │ │ ├── UsernameLinkHandle.java │ │ │ │ ├── SpamReport.java │ │ │ │ ├── StaleDevicesResponse.java │ │ │ │ ├── AccountCreationResponse.java │ │ │ │ ├── AnswerPushChallengeRequest.java │ │ │ │ ├── SignedPreKey.java │ │ │ │ ├── GroupCredentials.java │ │ │ │ ├── AnswerChallengeRequest.java │ │ │ │ ├── DeliveryCertificate.java │ │ │ │ ├── RemoteAttachmentError.java │ │ │ │ ├── TransferArchiveResult.java │ │ │ │ ├── CredentialProfileResponse.java │ │ │ │ ├── CurrencyConversionEntity.java │ │ │ │ ├── RemoteAttachment.java │ │ │ │ ├── CurrencyConversionEntityList.java │ │ │ │ ├── DeviceName.java │ │ │ │ ├── RateLimitChallenge.java │ │ │ │ ├── AccountStaleDevices.java │ │ │ │ ├── KeyTransparencyMonitorResponse.java │ │ │ │ ├── KeyTransparencySearchResponse.java │ │ │ │ ├── MismatchedDevicesResponse.java │ │ │ │ ├── SetPublicKeyRequest.java │ │ │ │ ├── IncomingWebsocketMessage.java │ │ │ │ ├── ReserveUsernameHashResponse.java │ │ │ │ ├── KeyTransparencyDistinguishedKeyResponse.java │ │ │ │ └── AccountMismatchedDevices.java │ │ │ │ ├── storage │ │ │ │ ├── LinkDeviceTokenAlreadyUsedException.java │ │ │ │ ├── devicecheck │ │ │ │ │ ├── TooManyKeysException.java │ │ │ │ │ ├── ChallengeNotFoundException.java │ │ │ │ │ ├── DuplicatePublicKeyException.java │ │ │ │ │ ├── DeviceCheckKeyIdNotFoundException.java │ │ │ │ │ ├── RequestReuseException.java │ │ │ │ │ └── DeviceCheckVerificationFailedException.java │ │ │ │ ├── UsernameHashNotAvailableException.java │ │ │ │ ├── UsernameReservationNotFoundException.java │ │ │ │ ├── OptimisticLockRetryLimitExceededException.java │ │ │ │ ├── MessagePersistenceException.java │ │ │ │ ├── ContestedOptimisticLockException.java │ │ │ │ ├── RefreshingAccountNotFoundException.java │ │ │ │ ├── AccountAlreadyExistsException.java │ │ │ │ ├── ClientRelease.java │ │ │ │ ├── ReportedMessageListener.java │ │ │ │ ├── ChunkProcessingFailedException.java │ │ │ │ ├── ConflictingMessageConsumerException.java │ │ │ │ ├── MrmDataMissingException.java │ │ │ │ ├── VerificationSessions.java │ │ │ │ ├── AccountUtil.java │ │ │ │ └── RemoteConfigsManager.java │ │ │ │ ├── backup │ │ │ │ ├── PublicKeyConflictException.java │ │ │ │ ├── UsageInfo.java │ │ │ │ ├── InvalidLengthException.java │ │ │ │ ├── BackupUploadDescriptor.java │ │ │ │ ├── SourceObjectNotFoundException.java │ │ │ │ ├── BackupLevelUtil.java │ │ │ │ └── MediaEncryptionParameters.java │ │ │ │ ├── spam │ │ │ │ ├── ChallengeType.java │ │ │ │ ├── MessageType.java │ │ │ │ ├── RateLimitChallengeListener.java │ │ │ │ └── RegistrationRecoveryChecker.java │ │ │ │ ├── metrics │ │ │ │ ├── TrafficSource.java │ │ │ │ └── NoopAwsSdkMetricPublisher.java │ │ │ │ ├── subscriptions │ │ │ │ ├── BankTransferType.java │ │ │ │ ├── BraintreePlanMetadata.java │ │ │ │ ├── PaymentStatus.java │ │ │ │ ├── SubscriptionPrice.java │ │ │ │ ├── SubscriptionForbiddenException.java │ │ │ │ ├── SubscriptionInvalidLevelException.java │ │ │ │ ├── SubscriptionNotFoundException.java │ │ │ │ ├── SubscriptionPaymentRequiredException.java │ │ │ │ ├── SubscriptionProcessorConflictException.java │ │ │ │ ├── SubscriptionPaymentRequiresActionException.java │ │ │ │ ├── SubscriptionInvalidArgumentsException.java │ │ │ │ ├── SubscriptionInvalidAmountException.java │ │ │ │ ├── SubscriptionReceiptRequestedForOpenPaymentException.java │ │ │ │ ├── PaymentMethod.java │ │ │ │ ├── SubscriptionInformation.java │ │ │ │ ├── ProcessorCustomer.java │ │ │ │ ├── AppleAppStoreDecodedTransaction.java │ │ │ │ ├── SubscriptionProcessorException.java │ │ │ │ ├── SubscriptionException.java │ │ │ │ └── SubscriptionChargeFailurePaymentRequiredException.java │ │ │ │ ├── controllers │ │ │ │ ├── ServerRejectedException.java │ │ │ │ ├── GetCallingRelaysResponse.java │ │ │ │ ├── MismatchedDevicesException.java │ │ │ │ ├── MismatchedDevices.java │ │ │ │ └── DeviceLimitExceededException.java │ │ │ │ ├── util │ │ │ │ ├── ua │ │ │ │ │ ├── ClientPlatform.java │ │ │ │ │ ├── UserAgent.java │ │ │ │ │ └── UnrecognizedUserAgentException.java │ │ │ │ ├── ExactlySizeValidatorForArraysOfByte.java │ │ │ │ ├── ExactlySizeValidatorForString.java │ │ │ │ ├── Pair.java │ │ │ │ ├── ImpossiblePhoneNumberException.java │ │ │ │ ├── RegistrationIdValidator.java │ │ │ │ ├── ExactlySizeValidatorForCollection.java │ │ │ │ ├── ObsoletePhoneNumberFormatException.java │ │ │ │ ├── ExactlySizeValidatorForSecretBytes.java │ │ │ │ ├── UsernameHashZkProofVerifier.java │ │ │ │ ├── AsyncTimerUtil.java │ │ │ │ ├── Constants.java │ │ │ │ ├── logging │ │ │ │ │ ├── UnknownKeepaliveOptionFilterFactory.java │ │ │ │ │ ├── UriInfoUtil.java │ │ │ │ │ ├── RequestLogEnabledFilterFactory.java │ │ │ │ │ ├── RequestLogManager.java │ │ │ │ │ └── RequestLogEnabledFilter.java │ │ │ │ ├── Futures.java │ │ │ │ ├── NonNormalizedPhoneNumberException.java │ │ │ │ ├── AbstractPublicKeySerializer.java │ │ │ │ ├── HostnameUtil.java │ │ │ │ ├── Optionals.java │ │ │ │ ├── InstantAdapter.java │ │ │ │ └── HttpServletRequestUtil.java │ │ │ │ ├── configuration │ │ │ │ ├── ShortCodeExpanderConfiguration.java │ │ │ │ ├── TurnConfiguration.java │ │ │ │ ├── dynamic │ │ │ │ │ ├── DynamicRegistrationConfiguration.java │ │ │ │ │ ├── DynamicRestDeprecationConfiguration.java │ │ │ │ │ ├── DynamicPaymentsConfiguration.java │ │ │ │ │ └── DynamicMetricsConfiguration.java │ │ │ │ ├── IdlePrimaryDeviceReminderConfiguration.java │ │ │ │ ├── LinkDeviceSecretConfiguration.java │ │ │ │ ├── ClientReleaseConfiguration.java │ │ │ │ ├── RemoteConfigConfiguration.java │ │ │ │ ├── FcmConfiguration.java │ │ │ │ ├── GenericZkConfig.java │ │ │ │ ├── MessageByteLimitCardinalityEstimatorConfiguration.java │ │ │ │ ├── CallQualitySurveyConfiguration.java │ │ │ │ ├── TlsKeyStoreConfiguration.java │ │ │ │ ├── secrets │ │ │ │ │ ├── SecretString.java │ │ │ │ │ ├── Secret.java │ │ │ │ │ └── SecretBytes.java │ │ │ │ ├── GrpcConfiguration.java │ │ │ │ ├── URLSerializationConverter.java │ │ │ │ ├── ZkConfig.java │ │ │ │ ├── PagedSingleUseKEMPreKeyStoreConfiguration.java │ │ │ │ ├── DirectoryV2ClientConfiguration.java │ │ │ │ ├── DeviceCheckConfiguration.java │ │ │ │ ├── AwsCredentialsProviderFactory.java │ │ │ │ ├── PubSubPublisherFactory.java │ │ │ │ ├── DefaultAwsCredentialsFactory.java │ │ │ │ ├── CdnConfiguration.java │ │ │ │ ├── AppleDeviceCheckConfiguration.java │ │ │ │ ├── ApnConfiguration.java │ │ │ │ ├── MaxDeviceConfiguration.java │ │ │ │ ├── FaultTolerantRedisClientFactory.java │ │ │ │ ├── ExternalRequestFilterConfiguration.java │ │ │ │ ├── VirtualThreadConfiguration.java │ │ │ │ ├── FaultTolerantRedisClusterFactory.java │ │ │ │ ├── SpamFilterConfiguration.java │ │ │ │ ├── ReportMessageConfiguration.java │ │ │ │ ├── PaymentsServiceConfiguration.java │ │ │ │ ├── MessageCacheConfiguration.java │ │ │ │ ├── SubscriptionPriceConfiguration.java │ │ │ │ ├── PaymentsServiceClientsFactory.java │ │ │ │ ├── S3ObjectMonitorFactory.java │ │ │ │ ├── DirectoryV2Configuration.java │ │ │ │ └── KeyTransparencyServiceConfiguration.java │ │ │ │ ├── auth │ │ │ │ ├── ExternalServiceCredentials.java │ │ │ │ ├── grpc │ │ │ │ │ └── AuthenticatedDevice.java │ │ │ │ ├── TurnToken.java │ │ │ │ ├── InvalidAuthorizationHeaderException.java │ │ │ │ ├── AuthenticatedDevice.java │ │ │ │ ├── AuthenticatedBackupUser.java │ │ │ │ ├── DisconnectionRequestListener.java │ │ │ │ ├── ChangesPhoneNumber.java │ │ │ │ ├── ChangesLinkedDevices.java │ │ │ │ └── Anonymous.java │ │ │ │ ├── registration │ │ │ │ ├── ClientType.java │ │ │ │ ├── MessageTransport.java │ │ │ │ ├── RegistrationFraudException.java │ │ │ │ └── TransportNotAllowedException.java │ │ │ │ ├── push │ │ │ │ ├── NotPushRegisteredException.java │ │ │ │ ├── MessageTooLargeException.java │ │ │ │ ├── PushNotificationSender.java │ │ │ │ └── SendPushNotificationResult.java │ │ │ │ ├── badges │ │ │ │ ├── LevelTranslator.java │ │ │ │ ├── BadgeTranslator.java │ │ │ │ └── ProfileBadgeConverter.java │ │ │ │ ├── grpc │ │ │ │ ├── ChannelNotFoundException.java │ │ │ │ ├── StatusConstants.java │ │ │ │ ├── validators │ │ │ │ │ ├── Range.java │ │ │ │ │ └── FieldValidator.java │ │ │ │ ├── RateLimitUtil.java │ │ │ │ ├── RequestAttributes.java │ │ │ │ ├── ConvertibleToGrpcStatus.java │ │ │ │ ├── net │ │ │ │ │ ├── ManagedNioEventLoopGroup.java │ │ │ │ │ └── ManagedGrpcServer.java │ │ │ │ ├── DeviceIdUtil.java │ │ │ │ └── AvatarChangeUtil.java │ │ │ │ ├── securestorage │ │ │ │ └── SecureStorageException.java │ │ │ │ ├── workers │ │ │ │ ├── JobSchedulerFactory.java │ │ │ │ └── PushNotificationExperimentFactory.java │ │ │ │ ├── attachments │ │ │ │ ├── AttachmentGenerator.java │ │ │ │ └── TusConfiguration.java │ │ │ │ ├── limits │ │ │ │ ├── NoopMessageDeliveryLoopMonitor.java │ │ │ │ ├── RateLimitChallengeOption.java │ │ │ │ ├── RateLimitedByIp.java │ │ │ │ └── RateLimiterDescriptor.java │ │ │ │ ├── websocket │ │ │ │ ├── InvalidWebsocketAddressException.java │ │ │ │ └── NoContextTakeoverPerMessageDeflateExtension.java │ │ │ │ ├── asn │ │ │ │ ├── AsnInfo.java │ │ │ │ └── AsnInfoProvider.java │ │ │ │ ├── s3 │ │ │ │ └── ManagedSupplier.java │ │ │ │ ├── experiment │ │ │ │ └── PushNotificationExperimentSample.java │ │ │ │ ├── securevaluerecovery │ │ │ │ └── SecureValueRecoveryException.java │ │ │ │ ├── redis │ │ │ │ ├── RedisUriUtil.java │ │ │ │ └── FaultTolerantPubSubConnection.java │ │ │ │ ├── mappers │ │ │ │ ├── ServerRejectedExceptionMapper.java │ │ │ │ └── InvalidWebsocketAddressExceptionMapper.java │ │ │ │ └── identity │ │ │ │ └── IdentityType.java │ │ │ └── signal │ │ │ └── i18n │ │ │ └── ResourceBundleFactory.java │ ├── proto │ │ ├── DisconnectionRequests.proto │ │ ├── PubSubMessage.proto │ │ └── org │ │ │ └── signal │ │ │ └── chat │ │ │ └── payments.proto │ └── java-templates │ │ └── org │ │ └── whispersystems │ │ └── textsecuregcm │ │ ├── WhisperServerVersion.java │ │ └── storage │ │ └── FoundationDbVersion.java │ └── test │ ├── resources │ ├── fixtures │ │ ├── mismatched_registration_id.json │ │ ├── missing_device_response2.json │ │ ├── missing_device_response.json │ │ ├── prekey_v2.json │ │ ├── current_message_sync.json │ │ ├── current_message_single_device.json │ │ ├── current_message_null_message_in_list.json │ │ ├── current_message_single_device_bad_type.json │ │ ├── current_message_single_device_server_receipt_type.json │ │ ├── current_message_single_device_not_urgent.json │ │ ├── fixer.res.json │ │ ├── current_message_extra_device.json │ │ ├── current_message_duplicate_device.json │ │ ├── current_message_multi_device.json │ │ ├── current_message_multi_device_not_urgent.json │ │ ├── current_message_multi_device_pni.json │ │ └── current_message_registration_id.json │ ├── META-INF │ │ └── services │ │ │ ├── org.whispersystems.textsecuregcm.configuration.DynamicConfigurationManagerFactory │ │ │ ├── org.whispersystems.textsecuregcm.configuration.DynamoDbClientFactory │ │ │ ├── org.whispersystems.textsecuregcm.configuration.PubSubPublisherFactory │ │ │ ├── org.whispersystems.textsecuregcm.configuration.S3ObjectMonitorFactory │ │ │ ├── org.whispersystems.textsecuregcm.configuration.PaymentsServiceClientsFactory │ │ │ ├── org.whispersystems.textsecuregcm.configuration.FaultTolerantRedisClientFactory │ │ │ ├── org.whispersystems.textsecuregcm.configuration.FaultTolerantRedisClusterFactory │ │ │ └── org.whispersystems.textsecuregcm.configuration.RegistrationServiceClientFactory │ ├── org │ │ └── whispersystems │ │ │ └── textsecuregcm │ │ │ ├── storage │ │ │ └── devicecheck │ │ │ │ ├── apple-sample-attestation │ │ │ │ ├── webauthn4j-sample-assertion │ │ │ │ └── webauthn4j-sample-attestation │ │ │ └── asn │ │ │ └── ip2asn-test.tsv │ └── logback-test.xml │ ├── java │ └── org │ │ └── whispersystems │ │ └── textsecuregcm │ │ ├── util │ │ ├── redis │ │ │ └── RedisCommandsHandler.java │ │ └── TestRandomUtil.java │ │ ├── storage │ │ └── FoundationDbDatabaseLifecycleManager.java │ │ ├── tests │ │ └── util │ │ │ ├── ProfileTestHelper.java │ │ │ ├── TestPrincipal.java │ │ │ ├── FakeDynamicConfigurationManager.java │ │ │ └── TestRecipient.java │ │ ├── subscriptions │ │ └── ProcessorCustomerTest.java │ │ ├── auth │ │ └── RegistrationLockError.java │ │ └── configuration │ │ └── StubPubSubPublisherFactory.java │ ├── proto │ ├── test_tree_head.proto │ └── echo_service.proto │ └── java-templates │ └── org │ └── whispersystems │ └── textsecuregcm │ └── util │ └── TestcontainersImages.java ├── integration-tests ├── .gitignore └── src │ └── main │ └── java │ └── org │ └── signal │ └── integration │ └── config │ ├── DynamoDbTables.java │ └── Config.java ├── .mvn ├── wrapper │ └── maven-wrapper.jar ├── extensions.xml └── jgitver.config.xml ├── api-doc └── src │ └── main │ └── resources │ ├── META-INF │ └── services │ │ └── io.swagger.v3.jaxrs2.ext.OpenAPIExtension │ └── openapi │ └── openapi-configuration.yaml ├── .gitmodules ├── websocket-resources └── src │ └── main │ └── java │ └── org │ └── whispersystems │ └── websocket │ ├── auth │ ├── InvalidCredentialsException.java │ └── AuthenticatedWebSocketUpgradeFilter.java │ ├── setup │ └── WebSocketConnectListener.java │ ├── WebsocketHeaders.java │ ├── messages │ ├── InvalidMessageException.java │ ├── WebSocketResponseMessage.java │ ├── WebSocketMessage.java │ └── WebSocketRequestMessage.java │ ├── logging │ ├── layout │ │ ├── converters │ │ │ ├── NAConverter.java │ │ │ ├── RemoteHostConverter.java │ │ │ ├── LineSeparatorConverter.java │ │ │ ├── StatusCodeConverter.java │ │ │ ├── ContentLengthConverter.java │ │ │ └── RequestUrlConverter.java │ │ └── WebsocketEventLayoutFactory.java │ └── AsyncWebsocketEventAppenderFactory.java │ └── session │ └── WebSocketSession.java └── .gitignore /.github/stale.yml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.java-version: -------------------------------------------------------------------------------- 1 | temurin-24 2 | -------------------------------------------------------------------------------- /service/src/main/resources/org/signal/badges/Badges_en.properties: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /integration-tests/.gitignore: -------------------------------------------------------------------------------- 1 | .libs 2 | src/main/resources/config.yml 3 | -------------------------------------------------------------------------------- /service/src/test/resources/fixtures/mismatched_registration_id.json: -------------------------------------------------------------------------------- 1 | { 2 | "staleDevices" : [2] 3 | } -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/signalapp/Signal-Server/HEAD/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /api-doc/src/main/resources/META-INF/services/io.swagger.v3.jaxrs2.ext.OpenAPIExtension: -------------------------------------------------------------------------------- 1 | org.signal.openapi.OpenApiExtension 2 | -------------------------------------------------------------------------------- /service/src/test/resources/META-INF/services/org.whispersystems.textsecuregcm.configuration.DynamicConfigurationManagerFactory: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /service/src/test/resources/fixtures/missing_device_response2.json: -------------------------------------------------------------------------------- 1 | { 2 | "missingDevices" : [2], 3 | "extraDevices" : [4] 4 | } 5 | -------------------------------------------------------------------------------- /service/src/test/resources/fixtures/missing_device_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "missingDevices" : [2, 3], 3 | "extraDevices" : [] 4 | } 5 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Signal Messenger, LLC 2 | # SPDX-License-Identifier: AGPL-3.0-only 3 | 4 | custom: https://signal.org/donate/ 5 | -------------------------------------------------------------------------------- /service/src/test/resources/fixtures/prekey_v2.json: -------------------------------------------------------------------------------- 1 | { 2 | "keyId" : 1234, 3 | "publicKey" : "BQ+NbroQtVKyFaCSfqzSw8Wy72Ff22RSa5ERKTv5DIk2" 4 | } -------------------------------------------------------------------------------- /service/src/test/resources/META-INF/services/org.whispersystems.textsecuregcm.configuration.DynamoDbClientFactory: -------------------------------------------------------------------------------- 1 | org.whispersystems.textsecuregcm.configuration.LocalDynamoDbFactory 2 | -------------------------------------------------------------------------------- /service/src/test/resources/META-INF/services/org.whispersystems.textsecuregcm.configuration.PubSubPublisherFactory: -------------------------------------------------------------------------------- 1 | org.whispersystems.textsecuregcm.configuration.StubPubSubPublisherFactory 2 | -------------------------------------------------------------------------------- /service/src/test/resources/META-INF/services/org.whispersystems.textsecuregcm.configuration.S3ObjectMonitorFactory: -------------------------------------------------------------------------------- 1 | org.whispersystems.textsecuregcm.configuration.StaticS3ObjectMonitorFactory 2 | -------------------------------------------------------------------------------- /service/src/main/resources/META-INF/services/org.whispersystems.textsecuregcm.configuration.AwsCredentialsProviderFactory: -------------------------------------------------------------------------------- 1 | org.whispersystems.textsecuregcm.configuration.StaticAwsCredentialsFactory 2 | -------------------------------------------------------------------------------- /service/src/test/resources/META-INF/services/org.whispersystems.textsecuregcm.configuration.PaymentsServiceClientsFactory: -------------------------------------------------------------------------------- 1 | org.whispersystems.textsecuregcm.configuration.StubPaymentsServiceClientsFactory 2 | -------------------------------------------------------------------------------- /service/src/test/resources/META-INF/services/org.whispersystems.textsecuregcm.configuration.FaultTolerantRedisClientFactory: -------------------------------------------------------------------------------- 1 | org.whispersystems.textsecuregcm.configuration.LocalFaultTolerantRedisClientFactory 2 | -------------------------------------------------------------------------------- /service/src/test/resources/META-INF/services/org.whispersystems.textsecuregcm.configuration.FaultTolerantRedisClusterFactory: -------------------------------------------------------------------------------- 1 | org.whispersystems.textsecuregcm.configuration.LocalFaultTolerantRedisClusterFactory 2 | -------------------------------------------------------------------------------- /service/src/test/resources/META-INF/services/org.whispersystems.textsecuregcm.configuration.RegistrationServiceClientFactory: -------------------------------------------------------------------------------- 1 | org.whispersystems.textsecuregcm.configuration.StubRegistrationServiceClientFactory 2 | -------------------------------------------------------------------------------- /service/src/test/resources/fixtures/current_message_sync.json: -------------------------------------------------------------------------------- 1 | { 2 | "timestamp" : 1234, 3 | "messages" : [{ 4 | "type" : 1, 5 | "destinationDeviceId" : 2, 6 | "content" : "Zm9vYmFyego" 7 | }] 8 | } 9 | -------------------------------------------------------------------------------- /service/src/main/graphql/braintree/VaultPaymentMethod.graphql: -------------------------------------------------------------------------------- 1 | mutation VaultPaymentMethod($input: VaultPaymentMethodInput!) { 2 | vaultPaymentMethod(input: $input) { 3 | paymentMethod { 4 | id 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/entities/CreateCallLinkCredential.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.entities; 2 | 3 | public record CreateCallLinkCredential(byte[] credential, long redemptionTime){} 4 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/storage/LinkDeviceTokenAlreadyUsedException.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.storage; 2 | 3 | public class LinkDeviceTokenAlreadyUsedException extends Exception { 4 | } 5 | -------------------------------------------------------------------------------- /service/src/main/resources/META-INF/services/io.dropwizard.logging.common.AppenderFactory: -------------------------------------------------------------------------------- 1 | org.whispersystems.textsecuregcm.metrics.LogstashTcpSocketAppenderFactory 2 | org.whispersystems.textsecuregcm.metrics.OpenTelemetryAppenderFactory 3 | -------------------------------------------------------------------------------- /service/src/test/resources/fixtures/current_message_single_device.json: -------------------------------------------------------------------------------- 1 | { 2 | "timestamp" : 1234, 3 | "messages" : [{ 4 | "type" : 1, 5 | "destinationDeviceId" : 1, 6 | "content" : "Zm9vYmFyego" 7 | }] 8 | } 9 | -------------------------------------------------------------------------------- /service/src/test/resources/fixtures/current_message_null_message_in_list.json: -------------------------------------------------------------------------------- 1 | { 2 | "timestamp" : 1234, 3 | "messages" : [ { 4 | "type" : 1, 5 | "destinationDeviceId" : 1, 6 | "content" : "Zm9vYmFyego" 7 | }, null ] 8 | } 9 | -------------------------------------------------------------------------------- /service/src/test/resources/fixtures/current_message_single_device_bad_type.json: -------------------------------------------------------------------------------- 1 | { 2 | "timestamp" : 1234, 3 | "messages" : [{ 4 | "type" : 7, 5 | "destinationDeviceId" : 1, 6 | "content" : "Zm9vYmFyego" 7 | }] 8 | } 9 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/backup/PublicKeyConflictException.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.backup; 2 | 3 | import java.io.IOException; 4 | 5 | public class PublicKeyConflictException extends IOException { 6 | } 7 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/entities/AvatarChange.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.entities; 2 | 3 | public enum AvatarChange { 4 | AVATAR_CHANGE_UNCHANGED, 5 | AVATAR_CHANGE_CLEAR, 6 | AVATAR_CHANGE_UPDATE 7 | } 8 | -------------------------------------------------------------------------------- /service/src/main/resources/META-INF/services/io.dropwizard.logging.common.filter.FilterFactory: -------------------------------------------------------------------------------- 1 | org.whispersystems.textsecuregcm.util.logging.RequestLogEnabledFilterFactory 2 | org.whispersystems.textsecuregcm.util.logging.UnknownKeepaliveOptionFilterFactory 3 | -------------------------------------------------------------------------------- /service/src/test/resources/org/whispersystems/textsecuregcm/storage/devicecheck/apple-sample-attestation: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/signalapp/Signal-Server/HEAD/service/src/test/resources/org/whispersystems/textsecuregcm/storage/devicecheck/apple-sample-attestation -------------------------------------------------------------------------------- /service/src/test/resources/org/whispersystems/textsecuregcm/storage/devicecheck/webauthn4j-sample-assertion: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/signalapp/Signal-Server/HEAD/service/src/test/resources/org/whispersystems/textsecuregcm/storage/devicecheck/webauthn4j-sample-assertion -------------------------------------------------------------------------------- /service/src/main/graphql/braintree/CreatePayPalBillingAgreement.graphql: -------------------------------------------------------------------------------- 1 | mutation CreatePayPalBillingAgreement($input: CreatePayPalBillingAgreementInput!) { 2 | createPayPalBillingAgreement(input: $input) { 3 | approvalUrl, 4 | billingAgreementToken 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /service/src/test/resources/fixtures/current_message_single_device_server_receipt_type.json: -------------------------------------------------------------------------------- 1 | { 2 | "timestamp": 1234, 3 | "messages": [ 4 | { 5 | "type": 5, 6 | "destinationDeviceId": 1, 7 | "content": "Zm9vYmFyego" 8 | } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /service/src/test/resources/org/whispersystems/textsecuregcm/storage/devicecheck/webauthn4j-sample-attestation: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/signalapp/Signal-Server/HEAD/service/src/test/resources/org/whispersystems/textsecuregcm/storage/devicecheck/webauthn4j-sample-attestation -------------------------------------------------------------------------------- /service/src/main/graphql/braintree/TokenizePayPalBillingAgreement.graphql: -------------------------------------------------------------------------------- 1 | mutation TokenizePayPalBillingAgreement($input: TokenizePayPalBillingAgreementInput!) { 2 | tokenizePayPalBillingAgreement(input: $input) { 3 | paymentMethod { 4 | id 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /service/src/test/resources/fixtures/current_message_single_device_not_urgent.json: -------------------------------------------------------------------------------- 1 | { 2 | "urgent": false, 3 | "timestamp": 1234, 4 | "messages": [ 5 | { 6 | "type": 1, 7 | "destinationDeviceId": 1, 8 | "content": "Zm9vYmFyego" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/backup/UsageInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.textsecuregcm.backup; 6 | 7 | public record UsageInfo(long bytesUsed, long numObjects) {} 8 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/entities/PhoneNumberDiscoverabilityRequest.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.entities; 2 | 3 | import jakarta.validation.constraints.NotNull; 4 | 5 | public record PhoneNumberDiscoverabilityRequest(@NotNull Boolean discoverableByPhoneNumber) {} 6 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/entities/GetCreateCallLinkCredentialsRequest.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.entities; 2 | 3 | import jakarta.validation.constraints.NotEmpty; 4 | 5 | 6 | public record GetCreateCallLinkCredentialsRequest(@NotEmpty byte[] createCallLinkCredentialRequest) {} 7 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/spam/ChallengeType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2021 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.spam; 7 | 8 | public enum ChallengeType { 9 | PUSH, 10 | CAPTCHA 11 | } 12 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/metrics/TrafficSource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.metrics; 7 | 8 | public enum TrafficSource { 9 | HTTP, 10 | WEBSOCKET 11 | } 12 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/storage/devicecheck/TooManyKeysException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.textsecuregcm.storage.devicecheck; 6 | 7 | public class TooManyKeysException extends Exception {} 8 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/subscriptions/BankTransferType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.subscriptions; 7 | 8 | public enum BankTransferType { 9 | SEPA_DEBIT 10 | } 11 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/backup/InvalidLengthException.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.backup; 2 | 3 | import java.io.IOException; 4 | 5 | public class InvalidLengthException extends IOException { 6 | 7 | public InvalidLengthException(String s) { 8 | super(s); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /service/src/main/graphql/braintree/CreatePayPalOneTimePayment.graphql: -------------------------------------------------------------------------------- 1 | # https://graphql.braintreepayments.com/reference/#Mutation--createPayPalOneTimePayment 2 | mutation CreatePayPalOneTimePayment($input: CreatePayPalOneTimePaymentInput!) { 3 | createPayPalOneTimePayment(input: $input) { 4 | approvalUrl, 5 | paymentId 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/storage/devicecheck/ChallengeNotFoundException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.textsecuregcm.storage.devicecheck; 6 | 7 | public class ChallengeNotFoundException extends Exception {} 8 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/subscriptions/BraintreePlanMetadata.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.subscriptions; 7 | 8 | public record BraintreePlanMetadata(long level) { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/controllers/ServerRejectedException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.controllers; 7 | 8 | public class ServerRejectedException extends Exception { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/storage/devicecheck/DuplicatePublicKeyException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.textsecuregcm.storage.devicecheck; 6 | 7 | public class DuplicatePublicKeyException extends Exception {} 8 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/util/ua/ClientPlatform.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.util.ua; 7 | 8 | public enum ClientPlatform { 9 | ANDROID, 10 | DESKTOP, 11 | IOS; 12 | } 13 | -------------------------------------------------------------------------------- /service/src/main/graphql/braintree/ChargePayPalOneTimePayment.graphql: -------------------------------------------------------------------------------- 1 | # https://graphql.braintreepayments.com/reference/#Mutation--chargePaymentMethod 2 | mutation ChargePayPalOneTimePayment($input: ChargePaymentMethodInput!) { 3 | chargePaymentMethod(input: $input) { 4 | transaction { 5 | id, 6 | status 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/ShortCodeExpanderConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.configuration; 7 | 8 | public record ShortCodeExpanderConfiguration(String baseUrl) { 9 | } 10 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/TurnConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.configuration; 7 | 8 | public record TurnConfiguration(CloudflareTurnConfiguration cloudflare) { 9 | } 10 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/storage/UsernameHashNotAvailableException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2021 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.storage; 7 | 8 | public class UsernameHashNotAvailableException extends Exception { 9 | } 10 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/storage/devicecheck/DeviceCheckKeyIdNotFoundException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.textsecuregcm.storage.devicecheck; 6 | 7 | public class DeviceCheckKeyIdNotFoundException extends Exception {} 8 | -------------------------------------------------------------------------------- /service/src/main/graphql/braintree/TokenizePayPalOneTimePayment.graphql: -------------------------------------------------------------------------------- 1 | # https://graphql.braintreepayments.com/reference/#Mutation--tokenizePayPalOneTimePayment 2 | mutation TokenizePayPalOneTimePayment($input: TokenizePayPalOneTimePaymentInput!) { 3 | tokenizePayPalOneTimePayment(input: $input) { 4 | paymentMethod { 5 | id 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | # Note that the implementation of the spam filter is private; internal 2 | # developers will need to override this URL with: 3 | # 4 | # ``` 5 | # git config submodule.spam-filter.url PRIVATE_URL 6 | # ``` 7 | # 8 | # External developers may safely ignore this submodule. 9 | [submodule "spam-filter"] 10 | path = spam-filter 11 | url = REDACTED 12 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/auth/ExternalServiceCredentials.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.auth; 7 | 8 | 9 | public record ExternalServiceCredentials(String username, String password) { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/entities/DeviceInfoList.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.entities; 7 | 8 | import java.util.List; 9 | 10 | public record DeviceInfoList(List devices) { 11 | } 12 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/storage/UsernameReservationNotFoundException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.storage; 7 | 8 | public class UsernameReservationNotFoundException extends Exception { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/subscriptions/PaymentStatus.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.textsecuregcm.subscriptions; 6 | 7 | public enum PaymentStatus { 8 | SUCCEEDED, 9 | PROCESSING, 10 | FAILED, 11 | UNKNOWN, 12 | } 13 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/dynamic/DynamicRegistrationConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.textsecuregcm.configuration.dynamic; 6 | 7 | public record DynamicRegistrationConfiguration(boolean squashDeclinedAttemptErrors) {} 8 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/registration/ClientType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.registration; 7 | 8 | public enum ClientType { 9 | IOS, 10 | ANDROID_WITH_FCM, 11 | ANDROID_WITHOUT_FCM, 12 | UNKNOWN 13 | } 14 | -------------------------------------------------------------------------------- /service/src/test/resources/fixtures/fixer.res.json: -------------------------------------------------------------------------------- 1 | { 2 | "success": true, 3 | "timestamp": 1519296206, 4 | "base": "EUR", 5 | "date": "2021-08-01", 6 | "rates": { 7 | "AUD": 1.566015, 8 | "CAD": 1.560132, 9 | "CHF": 1.154727, 10 | "CNY": 7.827874, 11 | "GBP": 0.882047, 12 | "JPY": 132.360679, 13 | "USD": 1.23396 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/entities/LinkDeviceResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.entities; 7 | 8 | import java.util.UUID; 9 | 10 | public record LinkDeviceResponse(UUID uuid, UUID pni, byte deviceId) { 11 | } 12 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/subscriptions/SubscriptionPrice.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.textsecuregcm.subscriptions; 6 | 7 | import java.math.BigDecimal; 8 | 9 | public record SubscriptionPrice(String currency, BigDecimal amount) {} 10 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/auth/grpc/AuthenticatedDevice.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.auth.grpc; 7 | 8 | import java.util.UUID; 9 | 10 | public record AuthenticatedDevice(UUID accountIdentifier, byte deviceId) { 11 | } 12 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/entities/PreKey.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.entities; 7 | 8 | public interface PreKey { 9 | 10 | long keyId(); 11 | 12 | K publicKey(); 13 | 14 | byte[] serializedPublicKey(); 15 | } 16 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/storage/OptimisticLockRetryLimitExceededException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2021 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.storage; 7 | 8 | public class OptimisticLockRetryLimitExceededException extends RuntimeException { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/entities/ApnRegistrationId.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.textsecuregcm.entities; 6 | 7 | import jakarta.validation.constraints.NotEmpty; 8 | 9 | public record ApnRegistrationId(@NotEmpty String apnRegistrationId) { 10 | } 11 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/entities/GcmRegistrationId.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.textsecuregcm.entities; 6 | 7 | import jakarta.validation.constraints.NotEmpty; 8 | 9 | public record GcmRegistrationId(@NotEmpty String gcmRegistrationId) { 10 | } 11 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/entities/OutgoingMessageEntityList.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.entities; 7 | 8 | import java.util.List; 9 | 10 | public record OutgoingMessageEntityList(List messages, boolean more) { 11 | } 12 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/push/NotPushRegisteredException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.push; 7 | 8 | public class NotPushRegisteredException extends Exception { 9 | public NotPushRegisteredException() { 10 | super(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/entities/SubmitVerificationCodeRequest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.entities; 7 | 8 | import jakarta.validation.constraints.NotBlank; 9 | 10 | public record SubmitVerificationCodeRequest(@NotBlank String code) { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/IdlePrimaryDeviceReminderConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.configuration; 7 | 8 | import java.time.Duration; 9 | 10 | public record IdlePrimaryDeviceReminderConfiguration(Duration minIdleDuration) { 11 | } 12 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/push/MessageTooLargeException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.push; 7 | 8 | import org.whispersystems.textsecuregcm.util.NoStackTraceException; 9 | 10 | public class MessageTooLargeException extends NoStackTraceException { 11 | } 12 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/storage/devicecheck/RequestReuseException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.textsecuregcm.storage.devicecheck; 6 | 7 | public class RequestReuseException extends Exception { 8 | 9 | public RequestReuseException(String s) { 10 | super(s); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /service/src/main/java/org/signal/i18n/ResourceBundleFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.signal.i18n; 7 | 8 | import java.util.Locale; 9 | import java.util.ResourceBundle; 10 | 11 | public interface ResourceBundleFactory { 12 | ResourceBundle createBundle(String baseName, Locale locale, ResourceBundle.Control control); 13 | } 14 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/badges/LevelTranslator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.badges; 7 | 8 | import java.util.List; 9 | import java.util.Locale; 10 | 11 | public interface LevelTranslator { 12 | String translate(List acceptableLanguages, String badgeId); 13 | } 14 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/grpc/ChannelNotFoundException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.grpc; 7 | 8 | /** 9 | * Indicates that a remote channel was not found for a given server call or remote address. 10 | */ 11 | public class ChannelNotFoundException extends Exception { 12 | } 13 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/storage/MessagePersistenceException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.storage; 7 | 8 | public class MessagePersistenceException extends Exception { 9 | 10 | public MessagePersistenceException(String message) { 11 | super(message); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /websocket-resources/src/main/java/org/whispersystems/websocket/auth/InvalidCredentialsException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.websocket.auth; 6 | 7 | public class InvalidCredentialsException extends Exception { 8 | 9 | public InvalidCredentialsException() { 10 | super(null, null, true, false); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/LinkDeviceSecretConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.configuration; 7 | 8 | import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes; 9 | 10 | public record LinkDeviceSecretConfiguration(SecretBytes secret) { 11 | } 12 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/controllers/GetCallingRelaysResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.controllers; 7 | 8 | import java.util.List; 9 | import org.whispersystems.textsecuregcm.auth.TurnToken; 10 | 11 | public record GetCallingRelaysResponse(List relays) { 12 | } 13 | -------------------------------------------------------------------------------- /service/src/test/java/org/whispersystems/textsecuregcm/util/redis/RedisCommandsHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.util.redis; 7 | 8 | import java.util.List; 9 | 10 | @FunctionalInterface 11 | public interface RedisCommandsHandler { 12 | 13 | Object redisCommand(String command, List args); 14 | } 15 | -------------------------------------------------------------------------------- /service/src/test/resources/org/whispersystems/textsecuregcm/asn/ip2asn-test.tsv: -------------------------------------------------------------------------------- 1 | 1.3.0.0 1.4.127.255 0 None Not routed 2 | 1.0.0.0 1.0.0.255 13335 US CLOUDFLARENET 3 | 2.16.112.0 2.16.113.255 16625 US AKAMAI-AS 4 | 2001:4:113:: 2001:1ff:ffff:ffff:ffff:ffff:ffff:ffff 0 None Not routed 5 | 2001:200:e00:: 2001:200:eff:ffff:ffff:ffff:ffff:ffff 4690 JP WIDE-SFO WIDE Project 6 | 2001:420:4488:: 2001:420:c0ef:ffff:ffff:ffff:ffff:ffff 109 US CISCOSYSTEMS 7 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/backup/BackupUploadDescriptor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.backup; 7 | 8 | import java.util.Map; 9 | 10 | public record BackupUploadDescriptor( 11 | int cdn, 12 | String key, 13 | Map headers, 14 | String signedUploadLocation) {} 15 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/ClientReleaseConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.configuration; 7 | 8 | import jakarta.validation.constraints.NotNull; 9 | import java.time.Duration; 10 | 11 | public record ClientReleaseConfiguration(@NotNull Duration refreshInterval) { 12 | } 13 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/securestorage/SecureStorageException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.securestorage; 7 | 8 | public class SecureStorageException extends RuntimeException { 9 | 10 | public SecureStorageException(final String message) { 11 | super(message); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.mvn/extensions.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | fr.brouillard.oss 6 | jgitver-maven-plugin 7 | 1.9.0 8 | 9 | 10 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/grpc/StatusConstants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.grpc; 7 | 8 | import io.grpc.Status; 9 | 10 | public abstract class StatusConstants { 11 | public static final Status UPGRADE_NEEDED_STATUS = Status.INVALID_ARGUMENT.withDescription("signal-upgrade-required"); 12 | } 13 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/registration/MessageTransport.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.registration; 7 | 8 | /** 9 | * A message transport is a medium via which verification codes can be delivered to a destination phone. 10 | */ 11 | public enum MessageTransport { 12 | SMS, 13 | VOICE 14 | } 15 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/spam/MessageType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.spam; 7 | 8 | public enum MessageType { 9 | INDIVIDUAL_IDENTIFIED_SENDER, 10 | SYNC, 11 | INDIVIDUAL_SEALED_SENDER, 12 | MULTI_RECIPIENT_SEALED_SENDER, 13 | INDIVIDUAL_STORY, 14 | MULTI_RECIPIENT_STORY, 15 | } 16 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/storage/ContestedOptimisticLockException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2021 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.storage; 7 | 8 | import org.whispersystems.textsecuregcm.util.NoStackTraceRuntimeException; 9 | 10 | public class ContestedOptimisticLockException extends NoStackTraceRuntimeException { 11 | } 12 | -------------------------------------------------------------------------------- /service/src/main/proto/DisconnectionRequests.proto: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | syntax = "proto3"; 6 | 7 | package org.signal.chat.auth; 8 | 9 | option java_package = "org.whispersystems.textsecuregcm.auth"; 10 | option java_multiple_files = true; 11 | 12 | message DisconnectionRequest { 13 | bytes account_identifier = 1; 14 | repeated uint32 device_ids = 2; 15 | } 16 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/RemoteConfigConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.configuration; 7 | 8 | import jakarta.validation.constraints.NotNull; 9 | import java.util.Map; 10 | 11 | public record RemoteConfigConfiguration(@NotNull Map globalConfig) { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /service/src/test/java/org/whispersystems/textsecuregcm/util/TestRandomUtil.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.util; 2 | 3 | import java.util.concurrent.ThreadLocalRandom; 4 | 5 | public class TestRandomUtil { 6 | private TestRandomUtil() {} 7 | 8 | public static byte[] nextBytes(int numBytes) { 9 | final byte[] bytes = new byte[numBytes]; 10 | ThreadLocalRandom.current().nextBytes(bytes); 11 | return bytes; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /service/src/main/java-templates/org/whispersystems/textsecuregcm/WhisperServerVersion.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2021 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm; 7 | 8 | public class WhisperServerVersion { 9 | 10 | private static final String VERSION = "${project.version}"; 11 | 12 | public static String getServerVersion() { 13 | return VERSION; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/workers/JobSchedulerFactory.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.workers; 2 | 3 | import org.whispersystems.textsecuregcm.WhisperServerConfiguration; 4 | import org.whispersystems.textsecuregcm.scheduler.JobScheduler; 5 | 6 | public interface JobSchedulerFactory { 7 | 8 | JobScheduler buildJobScheduler(CommandDependencies commandDependencies, WhisperServerConfiguration configuration); 9 | } 10 | -------------------------------------------------------------------------------- /websocket-resources/src/main/java/org/whispersystems/websocket/setup/WebSocketConnectListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.websocket.setup; 6 | 7 | import org.whispersystems.websocket.session.WebSocketSessionContext; 8 | 9 | public interface WebSocketConnectListener { 10 | public void onWebSocketConnect(WebSocketSessionContext context); 11 | } 12 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/push/PushNotificationSender.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2022 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.push; 7 | 8 | import java.util.concurrent.CompletableFuture; 9 | 10 | public interface PushNotificationSender { 11 | 12 | CompletableFuture sendNotification(PushNotification notification); 13 | } 14 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/storage/RefreshingAccountNotFoundException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.storage; 7 | 8 | public class RefreshingAccountNotFoundException extends RuntimeException { 9 | 10 | public RefreshingAccountNotFoundException(final String message) { 11 | super(message); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/subscriptions/SubscriptionForbiddenException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.textsecuregcm.subscriptions; 6 | 7 | public class SubscriptionForbiddenException extends SubscriptionException { 8 | 9 | public SubscriptionForbiddenException(final String message) { 10 | super(null, message); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/subscriptions/SubscriptionInvalidLevelException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.textsecuregcm.subscriptions; 6 | 7 | public class SubscriptionInvalidLevelException extends SubscriptionInvalidArgumentsException { 8 | 9 | public SubscriptionInvalidLevelException() { 10 | super(null, null); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/FcmConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.configuration; 7 | 8 | import jakarta.validation.constraints.NotNull; 9 | import org.whispersystems.textsecuregcm.configuration.secrets.SecretString; 10 | 11 | public record FcmConfiguration(@NotNull SecretString credentials) { 12 | } 13 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/GenericZkConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.configuration; 7 | 8 | import jakarta.validation.constraints.NotNull; 9 | import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes; 10 | 11 | public record GenericZkConfig(@NotNull SecretBytes serverSecret) { 12 | } 13 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/MessageByteLimitCardinalityEstimatorConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.configuration; 7 | 8 | import jakarta.validation.constraints.NotNull; 9 | import java.time.Duration; 10 | 11 | public record MessageByteLimitCardinalityEstimatorConfiguration(@NotNull Duration period) {} 12 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/registration/RegistrationFraudException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.registration; 7 | 8 | public class RegistrationFraudException extends Exception { 9 | public RegistrationFraudException(final RegistrationServiceSenderException cause) { 10 | super(null, cause, true, false); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/util/ua/UserAgent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.util.ua; 7 | 8 | import com.vdurmont.semver4j.Semver; 9 | import javax.annotation.Nullable; 10 | import java.util.Objects; 11 | 12 | public record UserAgent(ClientPlatform platform, Semver version, @Nullable String additionalSpecifiers) { 13 | } 14 | -------------------------------------------------------------------------------- /service/src/test/proto/test_tree_head.proto: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | syntax = "proto3"; 7 | 8 | option java_multiple_files = true; 9 | 10 | package org.signal.chat.rpc; 11 | 12 | message TestTreeHead { 13 | uint64 tree_size = 1; 14 | int64 timestamp = 2; 15 | bytes signature = 3; 16 | // Test that the deserializer properly ignores unknown fields 17 | bytes unknown_field = 4; 18 | } 19 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/CallQualitySurveyConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.configuration; 7 | 8 | import jakarta.validation.Valid; 9 | import jakarta.validation.constraints.NotNull; 10 | 11 | public record CallQualitySurveyConfiguration (@Valid @NotNull PubSubPublisherFactory pubSubPublisher) { 12 | } 13 | -------------------------------------------------------------------------------- /service/src/main/resources/META-INF/validation.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | META-INF/validation/constraints-custom.xml 8 | 9 | -------------------------------------------------------------------------------- /websocket-resources/src/main/java/org/whispersystems/websocket/WebsocketHeaders.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.websocket; 2 | 3 | /** 4 | * Class containing constants and shared logic for headers used in websocket upgrade requests. 5 | */ 6 | public class WebsocketHeaders { 7 | public final static String X_SIGNAL_RECEIVE_STORIES = "X-Signal-Receive-Stories"; 8 | 9 | public static boolean parseReceiveStoriesHeader(String s) { 10 | return "true".equals(s); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/badges/BadgeTranslator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.badges; 7 | 8 | import java.util.List; 9 | import java.util.Locale; 10 | import org.whispersystems.textsecuregcm.entities.Badge; 11 | 12 | public interface BadgeTranslator { 13 | Badge translate(List acceptableLanguages, String badgeId); 14 | } 15 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/TlsKeyStoreConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.configuration; 7 | 8 | import jakarta.validation.constraints.NotNull; 9 | import org.whispersystems.textsecuregcm.configuration.secrets.SecretString; 10 | 11 | public record TlsKeyStoreConfiguration(@NotNull SecretString password) { 12 | } 13 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/storage/AccountAlreadyExistsException.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.storage; 2 | 3 | class AccountAlreadyExistsException extends Exception { 4 | private final Account existingAccount; 5 | 6 | public AccountAlreadyExistsException(final Account existingAccount) { 7 | this.existingAccount = existingAccount; 8 | } 9 | 10 | public Account getExistingAccount() { 11 | return existingAccount; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/util/ExactlySizeValidatorForArraysOfByte.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.util; 7 | 8 | public class ExactlySizeValidatorForArraysOfByte extends ExactlySizeValidator { 9 | 10 | @Override 11 | protected int size(final byte[] value) { 12 | return value == null ? 0 : value.length; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/util/ExactlySizeValidatorForString.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.util; 7 | 8 | 9 | public class ExactlySizeValidatorForString extends ExactlySizeValidator { 10 | 11 | @Override 12 | protected int size(final String value) { 13 | return value == null ? 0 : value.length(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /websocket-resources/src/main/java/org/whispersystems/websocket/messages/InvalidMessageException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.websocket.messages; 6 | 7 | public class InvalidMessageException extends Exception { 8 | public InvalidMessageException(String s) { 9 | super(s); 10 | } 11 | 12 | public InvalidMessageException(Exception e) { 13 | super(e); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/attachments/AttachmentGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.attachments; 7 | import java.util.Map; 8 | 9 | public interface AttachmentGenerator { 10 | 11 | record Descriptor(Map headers, String signedUploadLocation) {} 12 | 13 | Descriptor generateAttachment(final String key); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /service/src/test/resources/fixtures/current_message_extra_device.json: -------------------------------------------------------------------------------- 1 | { 2 | "timestamp" : 1234, 3 | "messages" : [{ 4 | "type" : 1, 5 | "destinationDeviceId" : 1, 6 | "content" : "Zm9vYmFyego" 7 | }, 8 | { 9 | "type" : 1, 10 | "destinationDeviceId" : 3, 11 | "content" : "Zm9vYmFyego" 12 | }, 13 | { 14 | "type" : 1, 15 | "destinationDeviceId" : 4, 16 | "content" : "Zm9vYmFyego" 17 | }] 18 | } 19 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/grpc/validators/Range.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.grpc.validators; 7 | 8 | public record Range(long min, long max) { 9 | public Range { 10 | if (min > max) { 11 | throw new IllegalArgumentException("invalid range values: expected min <= max but have [%d, %d],".formatted(min, max)); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/metrics/NoopAwsSdkMetricPublisher.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.metrics; 2 | 3 | import software.amazon.awssdk.metrics.MetricCollection; 4 | import software.amazon.awssdk.metrics.MetricPublisher; 5 | 6 | public class NoopAwsSdkMetricPublisher implements MetricPublisher { 7 | 8 | @Override 9 | public void publish(final MetricCollection metricCollection) { 10 | } 11 | 12 | @Override 13 | public void close() { 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/spam/RateLimitChallengeListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2021 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.spam; 7 | 8 | 9 | import org.whispersystems.textsecuregcm.storage.Account; 10 | import java.io.IOException; 11 | 12 | public interface RateLimitChallengeListener { 13 | 14 | void handleRateLimitChallengeAnswered(Account account, ChallengeType type); 15 | } 16 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/storage/ClientRelease.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.storage; 7 | 8 | import com.vdurmont.semver4j.Semver; 9 | import org.whispersystems.textsecuregcm.util.ua.ClientPlatform; 10 | import java.time.Instant; 11 | 12 | public record ClientRelease(ClientPlatform platform, Semver version, Instant release, Instant expiration) { 13 | } 14 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/storage/ReportedMessageListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2022 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.storage; 7 | 8 | import java.util.Optional; 9 | import java.util.UUID; 10 | 11 | public interface ReportedMessageListener { 12 | 13 | void handleMessageReported(String sourceNumber, UUID messageGuid, UUID reporterUuid, Optional reportSpamToken); 14 | } 15 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/util/Pair.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.textsecuregcm.util; 6 | 7 | import java.util.Map; 8 | 9 | public record Pair(T1 first, T2 second) { 10 | public Pair(kotlin.Pair p) { 11 | this(p.getFirst(), p.getSecond()); 12 | } 13 | 14 | public Pair(Map.Entry e) { 15 | this(e.getKey(), e.getValue()); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /integration-tests/src/main/java/org/signal/integration/config/DynamoDbTables.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.signal.integration.config; 7 | 8 | import jakarta.validation.constraints.NotBlank; 9 | 10 | public record DynamoDbTables(@NotBlank String registrationRecovery, 11 | @NotBlank String verificationSessions, 12 | @NotBlank String phoneNumberIdentifiers) { 13 | } 14 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/limits/NoopMessageDeliveryLoopMonitor.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.limits; 2 | 3 | import java.util.UUID; 4 | 5 | public class NoopMessageDeliveryLoopMonitor implements MessageDeliveryLoopMonitor { 6 | 7 | public NoopMessageDeliveryLoopMonitor() { 8 | } 9 | 10 | public void recordDeliveryAttempt(final UUID accountIdentifier, final byte deviceId, final UUID messageGuid, final String userAgent, final String context) { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/secrets/SecretString.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.configuration.secrets; 7 | 8 | import org.apache.commons.lang3.Validate; 9 | 10 | public class SecretString extends Secret { 11 | public SecretString(final String value) { 12 | super(Validate.notBlank(value, "SecretString value must not be blank")); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/util/ImpossiblePhoneNumberException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2021 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.util; 7 | 8 | public class ImpossiblePhoneNumberException extends Exception { 9 | 10 | public ImpossiblePhoneNumberException() { 11 | super(); 12 | } 13 | 14 | public ImpossiblePhoneNumberException(final Throwable cause) { 15 | super(cause); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/workers/PushNotificationExperimentFactory.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.workers; 2 | 3 | import org.whispersystems.textsecuregcm.WhisperServerConfiguration; 4 | import org.whispersystems.textsecuregcm.experiment.PushNotificationExperiment; 5 | 6 | public interface PushNotificationExperimentFactory { 7 | 8 | PushNotificationExperiment buildExperiment(CommandDependencies commandDependencies, 9 | WhisperServerConfiguration configuration); 10 | } 11 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/subscriptions/SubscriptionNotFoundException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.textsecuregcm.subscriptions; 6 | 7 | public class SubscriptionNotFoundException extends SubscriptionException { 8 | 9 | public SubscriptionNotFoundException() { 10 | super(null); 11 | } 12 | 13 | public SubscriptionNotFoundException(Exception cause) { 14 | super(cause); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/util/RegistrationIdValidator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | * 5 | */ 6 | 7 | package org.whispersystems.textsecuregcm.util; 8 | 9 | import org.whispersystems.textsecuregcm.storage.Device; 10 | 11 | public class RegistrationIdValidator { 12 | public static boolean validRegistrationId(int registrationId) { 13 | return registrationId > 0 && registrationId <= Device.MAX_REGISTRATION_ID; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /websocket-resources/src/main/java/org/whispersystems/websocket/logging/layout/converters/NAConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.websocket.logging.layout.converters; 6 | 7 | import org.whispersystems.websocket.logging.WebsocketEvent; 8 | 9 | public class NAConverter extends WebSocketEventConverter { 10 | @Override 11 | public String convert(WebsocketEvent event) { 12 | return WebsocketEvent.NA; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/entities/RegistrationLock.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.entities; 7 | 8 | import com.fasterxml.jackson.annotation.JsonProperty; 9 | import jakarta.validation.constraints.NotEmpty; 10 | import jakarta.validation.constraints.Size; 11 | 12 | public record RegistrationLock(@JsonProperty @Size(min = 64, max = 64) @NotEmpty String registrationLock) { 13 | } 14 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/storage/ChunkProcessingFailedException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2021 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.textsecuregcm.storage; 6 | 7 | public class ChunkProcessingFailedException extends Exception { 8 | 9 | public ChunkProcessingFailedException(String message) { 10 | super(message); 11 | } 12 | 13 | public ChunkProcessingFailedException(Exception cause) { 14 | super(cause); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/util/ExactlySizeValidatorForCollection.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.util; 7 | 8 | import java.util.Collection; 9 | 10 | public class ExactlySizeValidatorForCollection extends ExactlySizeValidator> { 11 | 12 | @Override 13 | protected int size(final Collection value) { 14 | return value == null ? 0 : value.size(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/backup/SourceObjectNotFoundException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.backup; 7 | 8 | import java.io.IOException; 9 | 10 | public class SourceObjectNotFoundException extends IOException { 11 | public SourceObjectNotFoundException() { 12 | super(); 13 | } 14 | public SourceObjectNotFoundException(String message) { 15 | super(message); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/storage/ConflictingMessageConsumerException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.storage; 7 | 8 | import org.whispersystems.textsecuregcm.util.NoStackTraceException; 9 | 10 | /// Indicates that more than one consumer is trying to read a specific message queue at the same time. 11 | public class ConflictingMessageConsumerException extends NoStackTraceException { 12 | } 13 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/websocket/InvalidWebsocketAddressException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.websocket; 7 | 8 | public class InvalidWebsocketAddressException extends Exception { 9 | public InvalidWebsocketAddressException(String serialized) { 10 | super(serialized); 11 | } 12 | 13 | public InvalidWebsocketAddressException(Exception e) { 14 | super(e); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /websocket-resources/src/main/java/org/whispersystems/websocket/logging/layout/converters/RemoteHostConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.websocket.logging.layout.converters; 6 | 7 | import org.whispersystems.websocket.logging.WebsocketEvent; 8 | 9 | public class RemoteHostConverter extends WebSocketEventConverter { 10 | @Override 11 | public String convert(WebsocketEvent event) { 12 | return event.getRemoteHost(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/GrpcConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.textsecuregcm.configuration; 6 | 7 | import jakarta.validation.constraints.NotNull; 8 | 9 | public record GrpcConfiguration(@NotNull String bindAddress, @NotNull Integer port) { 10 | public GrpcConfiguration { 11 | if (bindAddress == null || bindAddress.isEmpty()) { 12 | bindAddress = "localhost"; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/URLSerializationConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.configuration; 7 | 8 | import com.fasterxml.jackson.databind.util.StdConverter; 9 | import java.net.URL; 10 | 11 | final class URLSerializationConverter extends StdConverter { 12 | 13 | @Override 14 | public String convert(final URL value) { 15 | return value.toString(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/util/ObsoletePhoneNumberFormatException.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.util; 2 | 3 | public class ObsoletePhoneNumberFormatException extends Exception { 4 | 5 | private final String regionCode; 6 | 7 | public ObsoletePhoneNumberFormatException(final String regionCode) { 8 | super("The provided format is obsolete in %s".formatted(regionCode)); 9 | this.regionCode = regionCode; 10 | } 11 | 12 | public String getRegionCode() { 13 | return regionCode; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/grpc/validators/FieldValidator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.grpc.validators; 7 | 8 | import com.google.protobuf.Descriptors; 9 | import com.google.protobuf.Message; 10 | import io.grpc.StatusException; 11 | 12 | public interface FieldValidator { 13 | 14 | void validate(Object extensionValue, Descriptors.FieldDescriptor fd, Message msg) 15 | throws StatusException; 16 | } 17 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/ZkConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.configuration; 7 | 8 | import jakarta.validation.constraints.NotEmpty; 9 | import jakarta.validation.constraints.NotNull; 10 | import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes; 11 | 12 | public record ZkConfig(@NotNull SecretBytes serverSecret, 13 | @NotEmpty byte[] serverPublic) { 14 | } 15 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/asn/AsnInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2021 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.asn; 7 | 8 | import static java.util.Objects.requireNonNull; 9 | 10 | import com.google.i18n.phonenumbers.PhoneNumberUtil; 11 | import javax.annotation.Nonnull; 12 | 13 | public record AsnInfo(long asn, @Nonnull String regionCode) { 14 | 15 | public AsnInfo { 16 | requireNonNull(regionCode, "regionCode must not be null"); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/entities/SendMessageResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.entities; 7 | 8 | import com.fasterxml.jackson.annotation.JsonProperty; 9 | 10 | public class SendMessageResponse { 11 | 12 | @JsonProperty 13 | private boolean needsSync; 14 | 15 | public SendMessageResponse() {} 16 | 17 | public SendMessageResponse(boolean needsSync) { 18 | this.needsSync = needsSync; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/storage/devicecheck/DeviceCheckVerificationFailedException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.textsecuregcm.storage.devicecheck; 6 | 7 | public class DeviceCheckVerificationFailedException extends Exception { 8 | 9 | public DeviceCheckVerificationFailedException(Exception cause) { 10 | super(cause); 11 | } 12 | 13 | public DeviceCheckVerificationFailedException(String s) { 14 | super(s); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/subscriptions/SubscriptionPaymentRequiredException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.textsecuregcm.subscriptions; 6 | 7 | public class SubscriptionPaymentRequiredException extends SubscriptionException { 8 | 9 | public SubscriptionPaymentRequiredException() { 10 | super(null, null); 11 | } 12 | 13 | public SubscriptionPaymentRequiredException(String message) { 14 | super(null, message); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/PagedSingleUseKEMPreKeyStoreConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.configuration; 7 | 8 | import jakarta.validation.constraints.NotBlank; 9 | import javax.annotation.Nullable; 10 | import java.net.URI; 11 | 12 | public record PagedSingleUseKEMPreKeyStoreConfiguration( 13 | @NotBlank String bucket, 14 | @NotBlank String region, 15 | @Nullable URI endpointOverride) { 16 | } 17 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/secrets/Secret.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.configuration.secrets; 7 | 8 | public class Secret { 9 | 10 | private final T value; 11 | 12 | 13 | public Secret(final T value) { 14 | this.value = value; 15 | } 16 | 17 | public T value() { 18 | return value; 19 | } 20 | 21 | @Override 22 | public String toString() { 23 | return "[REDACTED]"; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/entities/ProvisioningMessage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.entities; 7 | 8 | import io.swagger.v3.oas.annotations.media.Schema; 9 | import jakarta.validation.constraints.NotEmpty; 10 | 11 | public record ProvisioningMessage( 12 | @Schema(description = "The MIME base64-encoded body of the provisioning message to send to the destination device") 13 | @NotEmpty 14 | String body) { 15 | } 16 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/grpc/RateLimitUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.grpc; 7 | 8 | import org.whispersystems.textsecuregcm.limits.RateLimiter; 9 | import reactor.core.publisher.Mono; 10 | 11 | class RateLimitUtil { 12 | 13 | static Mono rateLimitByRemoteAddress(final RateLimiter rateLimiter) { 14 | return rateLimiter.validateReactive(RequestAttributesUtil.getRemoteAddress().getHostAddress()); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/util/ExactlySizeValidatorForSecretBytes.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.util; 7 | 8 | import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes; 9 | 10 | public class ExactlySizeValidatorForSecretBytes extends ExactlySizeValidator { 11 | @Override 12 | protected int size(final SecretBytes value) { 13 | return value == null ? 0 : value.value().length; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/util/UsernameHashZkProofVerifier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.util; 7 | 8 | import org.signal.libsignal.usernames.BaseUsernameException; 9 | import org.signal.libsignal.usernames.Username; 10 | 11 | public class UsernameHashZkProofVerifier { 12 | public void verifyProof(final byte[] proof, final byte[] hash) throws BaseUsernameException { 13 | Username.verifyProof(proof, hash); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/limits/RateLimitChallengeOption.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.limits; 7 | 8 | public enum RateLimitChallengeOption { 9 | CAPTCHA("captcha"), 10 | PUSH_CHALLENGE("pushChallenge"); 11 | 12 | private final String apiName; 13 | 14 | RateLimitChallengeOption(final String apiName) { 15 | this.apiName = apiName; 16 | } 17 | 18 | public String getApiName() { 19 | return apiName; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/subscriptions/SubscriptionProcessorConflictException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.textsecuregcm.subscriptions; 6 | 7 | public class SubscriptionProcessorConflictException extends SubscriptionException { 8 | 9 | public SubscriptionProcessorConflictException() { 10 | super(null, null); 11 | } 12 | 13 | public SubscriptionProcessorConflictException(final String message) { 14 | super(null, message); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/grpc/RequestAttributes.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.grpc; 7 | 8 | import java.net.InetAddress; 9 | import java.util.List; 10 | import java.util.Locale; 11 | import javax.annotation.Nullable; 12 | 13 | public record RequestAttributes(InetAddress remoteAddress, 14 | @Nullable String userAgent, 15 | List acceptLanguage) { 16 | } 17 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/s3/ManagedSupplier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.s3; 7 | 8 | import io.dropwizard.lifecycle.Managed; 9 | import java.util.function.Supplier; 10 | 11 | public interface ManagedSupplier extends Supplier, Managed { 12 | 13 | @Override 14 | default void start() throws Exception { 15 | // noop 16 | } 17 | 18 | @Override 19 | default void stop() throws Exception { 20 | // noop 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /service/src/test/java/org/whispersystems/textsecuregcm/storage/FoundationDbDatabaseLifecycleManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.storage; 7 | 8 | import com.apple.foundationdb.Database; 9 | import com.apple.foundationdb.FDB; 10 | import java.io.IOException; 11 | 12 | interface FoundationDbDatabaseLifecycleManager { 13 | 14 | void initializeDatabase(final FDB fdb) throws IOException; 15 | 16 | Database getDatabase(); 17 | 18 | void closeDatabase(); 19 | } 20 | -------------------------------------------------------------------------------- /service/src/test/java/org/whispersystems/textsecuregcm/tests/util/ProfileTestHelper.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.tests.util; 2 | 3 | import java.util.Base64; 4 | import org.whispersystems.textsecuregcm.util.TestRandomUtil; 5 | 6 | public class ProfileTestHelper { 7 | public static String generateRandomBase64FromByteArray(final int byteArrayLength) { 8 | return encodeToBase64(TestRandomUtil.nextBytes(byteArrayLength)); 9 | } 10 | 11 | public static String encodeToBase64(final byte[] input) { 12 | return Base64.getEncoder().encodeToString(input); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/entities/RemoteConfigurationResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.entities; 7 | 8 | import com.fasterxml.jackson.annotation.JsonProperty; 9 | import io.swagger.v3.oas.annotations.media.Schema; 10 | import java.util.Map; 11 | 12 | public record RemoteConfigurationResponse( 13 | @JsonProperty 14 | @Schema(description = "Remote configurations applicable to the user and client") 15 | Map config) { 16 | } 17 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/grpc/ConvertibleToGrpcStatus.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.grpc; 7 | 8 | import io.grpc.Metadata; 9 | import io.grpc.Status; 10 | import java.util.Optional; 11 | 12 | /** 13 | * Interface to be implemented by our custom exceptions that are consistently mapped to a gRPC status. 14 | */ 15 | public interface ConvertibleToGrpcStatus { 16 | 17 | Status grpcStatus(); 18 | 19 | Optional grpcMetadata(); 20 | } 21 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/attachments/TusConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.attachments; 7 | 8 | import jakarta.validation.constraints.NotEmpty; 9 | import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes; 10 | import org.whispersystems.textsecuregcm.util.ExactlySize; 11 | 12 | public record TusConfiguration( 13 | @ExactlySize(32) SecretBytes userAuthenticationTokenSharedSecret, 14 | @NotEmpty String uploadUri 15 | ){} 16 | -------------------------------------------------------------------------------- /service/src/main/proto/PubSubMessage.proto: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | syntax = "proto2"; 6 | 7 | package textsecure; 8 | 9 | option java_package = "org.whispersystems.textsecuregcm.storage"; 10 | option java_outer_classname = "PubSubProtos"; 11 | 12 | message PubSubMessage { 13 | enum Type { 14 | UNKNOWN = 0; 15 | QUERY_DB = 1; 16 | DELIVER = 2; 17 | KEEPALIVE = 3; 18 | CLOSE = 4; 19 | CONNECTED = 5; 20 | } 21 | 22 | optional Type type = 1; 23 | optional bytes content = 2; 24 | } 25 | -------------------------------------------------------------------------------- /websocket-resources/src/main/java/org/whispersystems/websocket/session/WebSocketSession.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.websocket.session; 6 | 7 | import java.lang.annotation.ElementType; 8 | import java.lang.annotation.Retention; 9 | import java.lang.annotation.RetentionPolicy; 10 | import java.lang.annotation.Target; 11 | 12 | @Retention(RetentionPolicy.RUNTIME) 13 | @Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD }) 14 | public @interface WebSocketSession { 15 | } 16 | 17 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/entities/UsernameLinkHandle.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.entities; 7 | 8 | import io.swagger.v3.oas.annotations.media.Schema; 9 | import jakarta.validation.constraints.NotNull; 10 | import java.util.UUID; 11 | 12 | public record UsernameLinkHandle( 13 | @Schema(description = "A handle that can be included in username links to retrieve the stored encrypted username") 14 | @NotNull 15 | UUID usernameLinkHandle) { 16 | } 17 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/experiment/PushNotificationExperimentSample.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.experiment; 2 | 3 | import javax.annotation.Nullable; 4 | import java.util.UUID; 5 | 6 | public record PushNotificationExperimentSample(UUID accountIdentifier, 7 | byte deviceId, 8 | boolean inExperimentGroup, 9 | T initialState, 10 | @Nullable T finalState) { 11 | } 12 | -------------------------------------------------------------------------------- /service/src/test/java/org/whispersystems/textsecuregcm/subscriptions/ProcessorCustomerTest.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.subscriptions; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import static org.junit.jupiter.api.Assertions.*; 6 | 7 | class ProcessorCustomerTest { 8 | 9 | @Test 10 | void toDynamoBytes() { 11 | final ProcessorCustomer processorCustomer = new ProcessorCustomer("Test", PaymentProvider.BRAINTREE); 12 | 13 | assertArrayEquals(new byte[] { PaymentProvider.BRAINTREE.getId(), 'T', 'e', 's', 't' }, 14 | processorCustomer.toDynamoBytes()); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /websocket-resources/src/main/java/org/whispersystems/websocket/messages/WebSocketResponseMessage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.websocket.messages; 6 | 7 | 8 | import java.util.Map; 9 | import java.util.Optional; 10 | 11 | public interface WebSocketResponseMessage { 12 | public long getRequestId(); 13 | public int getStatus(); 14 | public String getMessage(); 15 | public Map getHeaders(); 16 | public Optional getBody(); 17 | } 18 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/entities/SpamReport.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.entities; 2 | 3 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 4 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 5 | import javax.annotation.Nullable; 6 | import org.whispersystems.textsecuregcm.util.ByteArrayAdapter; 7 | 8 | public record SpamReport(@JsonSerialize(using = ByteArrayAdapter.Serializing.class) 9 | @JsonDeserialize(using = ByteArrayAdapter.Deserializing.class) 10 | @Nullable byte[] token) {} 11 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/subscriptions/SubscriptionPaymentRequiresActionException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.textsecuregcm.subscriptions; 6 | 7 | public class SubscriptionPaymentRequiresActionException extends SubscriptionInvalidArgumentsException { 8 | 9 | public SubscriptionPaymentRequiresActionException(String message) { 10 | super(message, null); 11 | } 12 | 13 | public SubscriptionPaymentRequiresActionException() { 14 | super(null, null); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /service/src/main/resources/lua/get_delivery_attempt_count.lua: -------------------------------------------------------------------------------- 1 | local firstMessageGuidKey = KEYS[1] 2 | local firstMessageAttemptsKey = KEYS[2] 3 | 4 | local firstMessageGuid = ARGV[1] 5 | local ttlSeconds = ARGV[2] 6 | 7 | if firstMessageGuid ~= redis.call("GET", firstMessageGuidKey) then 8 | -- This is the first time we've attempted to deliver this message as the first message in a "page" 9 | redis.call("SET", firstMessageGuidKey, firstMessageGuid, "EX", ttlSeconds) 10 | redis.call("SET", firstMessageAttemptsKey, 0, "EX", ttlSeconds) 11 | end 12 | 13 | return redis.call("INCR", firstMessageAttemptsKey) 14 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/push/SendPushNotificationResult.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2022 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.push; 7 | 8 | import java.time.Instant; 9 | import java.util.Optional; 10 | 11 | public record SendPushNotificationResult(boolean accepted, 12 | Optional errorCode, 13 | boolean unregistered, 14 | Optional unregisteredTimestamp) { 15 | } 16 | -------------------------------------------------------------------------------- /service/src/main/resources/lua/unlock_queue.lua: -------------------------------------------------------------------------------- 1 | -- Unlocks a message queue when a persist-to-DynamoDB run has finished and publishes an event notifying listeners that 2 | -- messages have been persisted 3 | 4 | local persistInProgressKey = KEYS[1] -- simple string key whose presence indicates a lock 5 | local eventChannelKey = KEYS[2] -- the channel on which to publish the "messages persisted" event 6 | local eventPayload = ARGV[1] -- [bytes] a protobuf payload for a "message persisted" pub/sub event 7 | 8 | redis.call("DEL", persistInProgressKey) 9 | redis.call("SPUBLISH", eventChannelKey, eventPayload) 10 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/entities/StaleDevicesResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.entities; 7 | 8 | import com.fasterxml.jackson.annotation.JsonProperty; 9 | import io.swagger.v3.oas.annotations.media.Schema; 10 | 11 | import java.util.Set; 12 | 13 | public record StaleDevicesResponse(@JsonProperty 14 | @Schema(description = "Devices that are no longer active") 15 | Set staleDevices) { 16 | } 17 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/subscriptions/SubscriptionInvalidArgumentsException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.textsecuregcm.subscriptions; 6 | 7 | public class SubscriptionInvalidArgumentsException extends SubscriptionException { 8 | 9 | public SubscriptionInvalidArgumentsException(final String message, final Exception cause) { 10 | super(cause, message); 11 | } 12 | 13 | public SubscriptionInvalidArgumentsException(final String message) { 14 | this(message, null); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/util/AsyncTimerUtil.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.util; 2 | 3 | import io.micrometer.core.instrument.Timer; 4 | import javax.annotation.Nonnull; 5 | import java.util.concurrent.CompletionStage; 6 | import java.util.function.Supplier; 7 | 8 | public class AsyncTimerUtil { 9 | @Nonnull 10 | public static CompletionStage record(final Timer timer, final Supplier> toRecord) { 11 | final Timer.Sample sample = Timer.start(); 12 | return toRecord.get().whenComplete((ignoreT, ignoreE) -> sample.stop(timer)); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/util/Constants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.util; 7 | 8 | import io.dropwizard.util.DataSize; 9 | 10 | public class Constants { 11 | public static final String METRICS_NAME = "textsecure"; 12 | public static final int MAXIMUM_STICKER_SIZE_BYTES = (int) DataSize.kibibytes(300 + 1).toBytes(); // add 1 kiB for encryption overhead 13 | public static final int MAXIMUM_STICKER_MANIFEST_SIZE_BYTES = (int) DataSize.kibibytes(10).toBytes(); 14 | } 15 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/util/ua/UnrecognizedUserAgentException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.util.ua; 7 | 8 | public class UnrecognizedUserAgentException extends Exception { 9 | 10 | public UnrecognizedUserAgentException() { 11 | } 12 | 13 | public UnrecognizedUserAgentException(final String message) { 14 | super(message); 15 | } 16 | 17 | public UnrecognizedUserAgentException(final Throwable cause) { 18 | super(cause); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /service/src/test/resources/fixtures/current_message_duplicate_device.json: -------------------------------------------------------------------------------- 1 | { 2 | "timestamp" : 1234, 3 | "messages" : [{ 4 | "type" : 1, 5 | "destinationDeviceId" : 1, 6 | "destinationRegistrationId" : 222, 7 | "content" : "Zm9vYmFyego" 8 | }, 9 | { 10 | "type" : 1, 11 | "destinationDeviceId" : 2, 12 | "destinationRegistrationId" : 333, 13 | "content" : "Zm9vYmFyego" 14 | }, 15 | { 16 | "type" : 1, 17 | "destinationDeviceId" : 1, 18 | "destinationRegistrationId" : 222, 19 | "content" : "Zm9vYmFyego" 20 | }] 21 | } 22 | -------------------------------------------------------------------------------- /service/src/test/resources/fixtures/current_message_multi_device.json: -------------------------------------------------------------------------------- 1 | { 2 | "timestamp" : 1234, 3 | "messages" : [{ 4 | "type" : 1, 5 | "destinationDeviceId" : 1, 6 | "destinationRegistrationId" : 222, 7 | "content" : "Zm9vYmFyego" 8 | }, 9 | { 10 | "type" : 1, 11 | "destinationDeviceId" : 2, 12 | "destinationRegistrationId" : 333, 13 | "content" : "Zm9vYmFyego" 14 | }, 15 | { 16 | "type" : 1, 17 | "destinationDeviceId" : 3, 18 | "destinationRegistrationId" : 444, 19 | "content" : "Zm9vYmFyego" 20 | }] 21 | } 22 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/DirectoryV2ClientConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.textsecuregcm.configuration; 6 | 7 | import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes; 8 | import org.whispersystems.textsecuregcm.util.ExactlySize; 9 | 10 | public record DirectoryV2ClientConfiguration(@ExactlySize(32) SecretBytes userAuthenticationTokenSharedSecret, 11 | @ExactlySize(32) SecretBytes userIdTokenSharedSecret) { 12 | } 13 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/limits/RateLimitedByIp.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.limits; 7 | 8 | import java.lang.annotation.ElementType; 9 | import java.lang.annotation.Retention; 10 | import java.lang.annotation.RetentionPolicy; 11 | import java.lang.annotation.Target; 12 | 13 | @Target(ElementType.METHOD) 14 | @Retention(RetentionPolicy.RUNTIME) 15 | public @interface RateLimitedByIp { 16 | 17 | RateLimiters.For value(); 18 | 19 | boolean failOnUnresolvedIp() default true; 20 | } 21 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/subscriptions/SubscriptionInvalidAmountException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.textsecuregcm.subscriptions; 6 | 7 | public class SubscriptionInvalidAmountException extends SubscriptionInvalidArgumentsException { 8 | 9 | private String errorCode; 10 | 11 | public SubscriptionInvalidAmountException(String errorCode) { 12 | super(null, null); 13 | this.errorCode = errorCode; 14 | } 15 | 16 | public String getErrorCode() { 17 | return errorCode; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /service/src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | _____ _ _ _____ 2 | / ___|(_) | |/ ___| 3 | \ `--. _ __ _ _ __ __ _ | |\ `--. ___ _ __ __ __ ___ _ __ 4 | `--. \| | / _` || '_ \ / _` || | `--. \ / _ \| '__|\ \ / // _ \| '__| 5 | /\__/ /| || (_| || | | || (_| || |/\__/ /| __/| | \ V /| __/| | 6 | \____/ |_| \__, ||_| |_| \__,_||_|\____/ \___||_| \_/ \___||_| 7 | __/ | 8 | |___/ 9 | 10 | -------------------------------------------------------------------------------- /service/src/test/resources/fixtures/current_message_multi_device_not_urgent.json: -------------------------------------------------------------------------------- 1 | { 2 | "urgent": false, 3 | "timestamp": 1234, 4 | "messages": [ 5 | { 6 | "type": 1, 7 | "destinationDeviceId": 1, 8 | "destinationRegistrationId": 222, 9 | "content": "Zm9vYmFyego" 10 | }, 11 | { 12 | "type": 1, 13 | "destinationDeviceId": 2, 14 | "destinationRegistrationId": 333, 15 | "content": "Zm9vYmFyego" 16 | }, 17 | { 18 | "type": 1, 19 | "destinationDeviceId": 3, 20 | "destinationRegistrationId": 444, 21 | "content": "Zm9vYmFyego" 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /service/src/test/resources/fixtures/current_message_multi_device_pni.json: -------------------------------------------------------------------------------- 1 | { 2 | "timestamp" : 1234, 3 | "messages" : [{ 4 | "type" : 1, 5 | "destinationDeviceId" : 1, 6 | "destinationRegistrationId" : 2222, 7 | "content" : "Zm9vYmFyego" 8 | }, 9 | { 10 | "type" : 1, 11 | "destinationDeviceId" : 2, 12 | "destinationRegistrationId" : 3333, 13 | "content" : "Zm9vYmFyego" 14 | }, 15 | { 16 | "type" : 1, 17 | "destinationDeviceId" : 3, 18 | "destinationRegistrationId" : 4444, 19 | "content" : "Zm9vYmFyego" 20 | }] 21 | } 22 | -------------------------------------------------------------------------------- /service/src/test/resources/fixtures/current_message_registration_id.json: -------------------------------------------------------------------------------- 1 | { 2 | "timestamp" : 1234, 3 | "messages" : [{ 4 | "type" : 1, 5 | "destinationDeviceId" : 1, 6 | "destinationRegistrationId" : 222, 7 | "content" : "Zm9vYmFyego" 8 | }, 9 | { 10 | "type" : 1, 11 | "destinationDeviceId" : 2, 12 | "destinationRegistrationId" : 999, 13 | "content" : "Zm9vYmFyego" 14 | }, 15 | { 16 | "type" : 1, 17 | "destinationDeviceId" : 3, 18 | "destinationRegistrationId" : 444, 19 | "content" : "Zm9vYmFyego" 20 | }] 21 | } 22 | -------------------------------------------------------------------------------- /service/src/main/java-templates/org/whispersystems/textsecuregcm/storage/FoundationDbVersion.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.storage; 7 | 8 | public class FoundationDbVersion { 9 | 10 | private static final String VERSION = "${foundationdb.version}"; 11 | private static final int API_VERSION = ${foundationdb.api-version}; 12 | 13 | public static String getFoundationDbVersion() { 14 | return VERSION; 15 | } 16 | 17 | public static int getFoundationDbApiVersion() { 18 | return API_VERSION; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/entities/AccountCreationResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.entities; 7 | 8 | import com.fasterxml.jackson.annotation.JsonUnwrapped; 9 | import io.swagger.v3.oas.annotations.media.Schema; 10 | 11 | public record AccountCreationResponse( 12 | 13 | @JsonUnwrapped 14 | AccountIdentityResponse identityResponse, 15 | 16 | @Schema(description = "If true, there was an existing account registered for this number") 17 | boolean reregistration) { 18 | } 19 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/subscriptions/SubscriptionReceiptRequestedForOpenPaymentException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.textsecuregcm.subscriptions; 6 | 7 | /** 8 | * Attempted to retrieve a receipt for a subscription that hasn't yet been charged or the invoice is in the open state 9 | */ 10 | public class SubscriptionReceiptRequestedForOpenPaymentException extends SubscriptionException { 11 | 12 | public SubscriptionReceiptRequestedForOpenPaymentException() { 13 | super(null, null); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/backup/BackupLevelUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.textsecuregcm.backup; 6 | 7 | import org.signal.libsignal.zkgroup.backups.BackupLevel; 8 | 9 | public class BackupLevelUtil { 10 | public static BackupLevel fromReceiptLevel(long receiptLevel) { 11 | try { 12 | return BackupLevel.fromValue(Math.toIntExact(receiptLevel)); 13 | } catch (ArithmeticException e) { 14 | throw new IllegalArgumentException("Invalid receipt level: " + receiptLevel); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/securevaluerecovery/SecureValueRecoveryException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.securevaluerecovery; 7 | 8 | public class SecureValueRecoveryException extends RuntimeException { 9 | private final String statusCode; 10 | 11 | public SecureValueRecoveryException(final String message, final String statusCode) { 12 | super(message); 13 | this.statusCode = statusCode; 14 | } 15 | 16 | public String getStatusCode() { 17 | return statusCode; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /service/src/test/java/org/whispersystems/textsecuregcm/auth/RegistrationLockError.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.auth; 7 | 8 | public enum RegistrationLockError { 9 | MISMATCH(RegistrationLockVerificationManager.FAILURE_HTTP_STATUS), 10 | RATE_LIMITED(429) 11 | ; 12 | 13 | private final int expectedStatus; 14 | 15 | RegistrationLockError(final int expectedStatus) { 16 | this.expectedStatus = expectedStatus; 17 | } 18 | 19 | public int getExpectedStatus() { 20 | return expectedStatus; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /websocket-resources/src/main/java/org/whispersystems/websocket/messages/WebSocketMessage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.websocket.messages; 6 | 7 | public interface WebSocketMessage { 8 | 9 | public enum Type { 10 | UNKNOWN_MESSAGE, 11 | REQUEST_MESSAGE, 12 | RESPONSE_MESSAGE 13 | } 14 | 15 | public Type getType(); 16 | public WebSocketRequestMessage getRequestMessage(); 17 | public WebSocketResponseMessage getResponseMessage(); 18 | public byte[] toByteArray(); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/auth/TurnToken.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.auth; 7 | 8 | import com.fasterxml.jackson.annotation.JsonProperty; 9 | import java.util.List; 10 | import javax.annotation.Nonnull; 11 | import javax.annotation.Nullable; 12 | 13 | public record TurnToken( 14 | String username, 15 | String password, 16 | @JsonProperty("ttl") long ttlSeconds, 17 | @Nonnull List urls, 18 | @Nonnull List urlsWithIps, 19 | @Nullable String hostname) { 20 | } 21 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/dynamic/DynamicRestDeprecationConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.configuration.dynamic; 7 | 8 | import com.vdurmont.semver4j.Semver; 9 | import java.util.Map; 10 | import org.whispersystems.textsecuregcm.util.ua.ClientPlatform; 11 | 12 | public record DynamicRestDeprecationConfiguration(Map platforms) { 13 | public record PlatformConfiguration(Semver minimumRestFreeVersion, int universalRolloutPercent) {} 14 | } 15 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/redis/RedisUriUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.redis; 7 | 8 | import io.lettuce.core.RedisURI; 9 | import java.time.Duration; 10 | 11 | public class RedisUriUtil { 12 | 13 | public static RedisURI createRedisUriWithTimeout(final String uri, final Duration timeout) { 14 | final RedisURI redisUri = RedisURI.create(uri); 15 | // for synchronous commands and the initial connection 16 | redisUri.setTimeout(timeout); 17 | return redisUri; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /websocket-resources/src/main/java/org/whispersystems/websocket/logging/layout/converters/LineSeparatorConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.websocket.logging.layout.converters; 6 | 7 | import org.whispersystems.websocket.logging.WebsocketEvent; 8 | 9 | import ch.qos.logback.core.CoreConstants; 10 | 11 | public class LineSeparatorConverter extends WebSocketEventConverter { 12 | public LineSeparatorConverter() { 13 | } 14 | 15 | public String convert(WebsocketEvent event) { 16 | return CoreConstants.LINE_SEPARATOR; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/controllers/MismatchedDevicesException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.controllers; 7 | 8 | public class MismatchedDevicesException extends Exception { 9 | 10 | private final MismatchedDevices mismatchedDevices; 11 | 12 | public MismatchedDevicesException(final MismatchedDevices mismatchedDevices) { 13 | this.mismatchedDevices = mismatchedDevices; 14 | } 15 | 16 | public MismatchedDevices getMismatchedDevices() { 17 | return mismatchedDevices; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/util/logging/UnknownKeepaliveOptionFilterFactory.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.util.logging; 2 | 3 | import ch.qos.logback.classic.spi.ILoggingEvent; 4 | import ch.qos.logback.core.filter.Filter; 5 | import com.fasterxml.jackson.annotation.JsonTypeName; 6 | import io.dropwizard.logging.common.filter.FilterFactory; 7 | 8 | @JsonTypeName("unknownKeepaliveOption") 9 | public class UnknownKeepaliveOptionFilterFactory implements FilterFactory { 10 | 11 | @Override 12 | public Filter build() { 13 | return new UnknownKeepaliveOptionFilter(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /websocket-resources/src/main/java/org/whispersystems/websocket/messages/WebSocketRequestMessage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.websocket.messages; 6 | 7 | import java.util.Map; 8 | import java.util.Optional; 9 | 10 | public interface WebSocketRequestMessage { 11 | 12 | public String getVerb(); 13 | public String getPath(); 14 | public Map getHeaders(); 15 | public Optional getBody(); 16 | public long getRequestId(); 17 | public boolean hasRequestId(); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/grpc/net/ManagedNioEventLoopGroup.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.grpc.net; 2 | 3 | import io.dropwizard.lifecycle.Managed; 4 | import io.netty.channel.nio.NioEventLoopGroup; 5 | 6 | /** 7 | * A wrapper for a Netty {@link NioEventLoopGroup} that implements Dropwizard's {@link Managed} interface, allowing 8 | * Dropwizard to manage the lifecycle of the event loop group. 9 | */ 10 | public class ManagedNioEventLoopGroup extends NioEventLoopGroup implements Managed { 11 | 12 | @Override 13 | public void stop() throws Exception { 14 | this.shutdownGracefully().await(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /service/src/test/proto/echo_service.proto: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | syntax = "proto3"; 7 | 8 | option java_multiple_files = true; 9 | 10 | package org.signal.chat.rpc; 11 | 12 | // A simple service for testing gRPC interceptors 13 | service EchoService { 14 | rpc echo (EchoRequest) returns (EchoResponse) {} 15 | rpc echo2 (EchoRequest) returns (EchoResponse) {} 16 | rpc echoStream (stream EchoRequest) returns (stream EchoResponse) {} 17 | } 18 | 19 | message EchoRequest { 20 | bytes payload = 1; 21 | } 22 | 23 | message EchoResponse { 24 | bytes payload = 1; 25 | } 26 | -------------------------------------------------------------------------------- /.mvn/jgitver.config.xml: -------------------------------------------------------------------------------- 1 | 4 | true 5 | false 6 | 7 | 8 | (.*) 9 | 10 | IGNORE 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/dynamic/DynamicPaymentsConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.configuration.dynamic; 7 | 8 | import com.fasterxml.jackson.annotation.JsonProperty; 9 | import java.util.Collections; 10 | import java.util.List; 11 | 12 | public class DynamicPaymentsConfiguration { 13 | 14 | @JsonProperty 15 | private List disallowedPrefixes = Collections.emptyList(); 16 | 17 | public List getDisallowedPrefixes() { 18 | return disallowedPrefixes; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/secrets/SecretBytes.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.configuration.secrets; 7 | 8 | import org.apache.commons.lang3.Validate; 9 | 10 | public class SecretBytes extends Secret { 11 | 12 | public SecretBytes(final byte[] value) { 13 | super(requireNotEmpty(value)); 14 | } 15 | 16 | private static byte[] requireNotEmpty(final byte[] value) { 17 | Validate.isTrue(value.length > 0, "SecretBytes value must not be empty"); 18 | return value; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/entities/AnswerPushChallengeRequest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.entities; 7 | 8 | import io.swagger.v3.oas.annotations.media.Schema; 9 | import jakarta.validation.constraints.NotBlank; 10 | 11 | public class AnswerPushChallengeRequest extends AnswerChallengeRequest { 12 | 13 | @Schema(description = "A token provided to the client via a push payload") 14 | @NotBlank 15 | private String challenge; 16 | 17 | public String getChallenge() { 18 | return challenge; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/entities/SignedPreKey.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.entities; 7 | 8 | import org.signal.libsignal.protocol.IdentityKey; 9 | 10 | public interface SignedPreKey extends PreKey { 11 | 12 | byte[] signature(); 13 | 14 | default boolean signatureValid(final IdentityKey identityKey) { 15 | try { 16 | return identityKey.getPublicKey().verifySignature(serializedPublicKey(), signature()); 17 | } catch (final Exception e) { 18 | return false; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /service/src/main/resources/lua/get_queues_to_persist.lua: -------------------------------------------------------------------------------- 1 | -- returns a list of queues that meet persistence criteria 2 | 3 | local queueTotalIndexKey = KEYS[1] -- sorted set of all queues in the shard, by timestamp of oldest message 4 | local maxTime = ARGV[1] -- [number] the most recent queue timestamp that may be fetched 5 | local limit = ARGV[2] -- [number] the maximum number of queues to fetch 6 | 7 | local results = redis.call("ZRANGE", queueTotalIndexKey, 0, maxTime, "BYSCORE", "LIMIT", 0, limit) 8 | 9 | if results and next(results) then 10 | redis.call("ZREM", queueTotalIndexKey, unpack(results)) 11 | end 12 | 13 | return results 14 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/redis/FaultTolerantPubSubConnection.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.redis; 7 | 8 | import io.lettuce.core.pubsub.StatefulRedisPubSubConnection; 9 | 10 | public class FaultTolerantPubSubConnection extends AbstractFaultTolerantPubSubConnection> { 11 | 12 | protected FaultTolerantPubSubConnection(final String name, 13 | final StatefulRedisPubSubConnection pubSubConnection) { 14 | 15 | super(name, pubSubConnection); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/subscriptions/PaymentMethod.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.subscriptions; 7 | 8 | public enum PaymentMethod { 9 | UNKNOWN, 10 | /** 11 | * A credit card or debit card, including those from Apple Pay and Google Pay 12 | */ 13 | CARD, 14 | /** 15 | * A PayPal account 16 | */ 17 | PAYPAL, 18 | /** 19 | * A SEPA debit account 20 | */ 21 | SEPA_DEBIT, 22 | /** 23 | * An iDEAL account 24 | */ 25 | IDEAL, 26 | GOOGLE_PLAY_BILLING, 27 | APPLE_APP_STORE 28 | } 29 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/DeviceCheckConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.textsecuregcm.configuration; 6 | 7 | import java.time.Duration; 8 | 9 | /** 10 | * Configuration for Device Check operations 11 | * 12 | * @param backupRedemptionDuration How long to grant backup access for redemptions via device check 13 | * @param backupRedemptionLevel What backup level to grant redemptions via device check 14 | */ 15 | public record DeviceCheckConfiguration(Duration backupRedemptionDuration, long backupRedemptionLevel) {} 16 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/auth/InvalidAuthorizationHeaderException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.textsecuregcm.auth; 6 | 7 | 8 | import jakarta.ws.rs.WebApplicationException; 9 | import jakarta.ws.rs.core.Response.Status; 10 | 11 | public class InvalidAuthorizationHeaderException extends WebApplicationException { 12 | public InvalidAuthorizationHeaderException(String s) { 13 | super(s, Status.UNAUTHORIZED); 14 | } 15 | 16 | public InvalidAuthorizationHeaderException(Exception e) { 17 | super(e, Status.UNAUTHORIZED); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/entities/GroupCredentials.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.entities; 7 | 8 | import java.util.List; 9 | import java.util.UUID; 10 | import javax.annotation.Nullable; 11 | 12 | public record GroupCredentials(List credentials, List callLinkAuthCredentials, @Nullable UUID pni) { 13 | 14 | public record GroupCredential(byte[] credential, long redemptionTime) { 15 | } 16 | 17 | public record CallLinkAuthCredential(byte[] credential, long redemptionTime) { 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /service/src/main/resources/META-INF/services/io.dropwizard.jackson.Discoverable: -------------------------------------------------------------------------------- 1 | org.whispersystems.textsecuregcm.configuration.AwsCredentialsProviderFactory 2 | org.whispersystems.textsecuregcm.configuration.DynamoDbClientFactory 3 | org.whispersystems.textsecuregcm.configuration.FaultTolerantRedisClusterFactory 4 | org.whispersystems.textsecuregcm.configuration.FaultTolerantRedisClientFactory 5 | org.whispersystems.textsecuregcm.configuration.PaymentsServiceClientsFactory 6 | org.whispersystems.textsecuregcm.configuration.PubSubPublisherFactory 7 | org.whispersystems.textsecuregcm.configuration.RegistrationServiceClientFactory 8 | org.whispersystems.textsecuregcm.configuration.S3ObjectMonitorFactory 9 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/AwsCredentialsProviderFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.configuration; 7 | 8 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 9 | import io.dropwizard.jackson.Discoverable; 10 | import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; 11 | 12 | @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = DefaultAwsCredentialsFactory.class) 13 | public interface AwsCredentialsProviderFactory extends Discoverable { 14 | 15 | AwsCredentialsProvider build(); 16 | } 17 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/controllers/MismatchedDevices.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.controllers; 7 | 8 | import java.util.Set; 9 | 10 | public record MismatchedDevices(Set missingDeviceIds, Set extraDeviceIds, Set staleDeviceIds) { 11 | 12 | public MismatchedDevices { 13 | if (missingDeviceIds.isEmpty() && extraDeviceIds.isEmpty() && staleDeviceIds.isEmpty()) { 14 | throw new IllegalArgumentException("At least one of missingDevices, extraDevices, or staleDevices must be non-empty"); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/mappers/ServerRejectedExceptionMapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.mappers; 7 | 8 | import jakarta.ws.rs.core.Response; 9 | import jakarta.ws.rs.ext.ExceptionMapper; 10 | import org.whispersystems.textsecuregcm.controllers.ServerRejectedException; 11 | 12 | public class ServerRejectedExceptionMapper implements ExceptionMapper { 13 | 14 | @Override 15 | public Response toResponse(final ServerRejectedException exception) { 16 | return Response.status(508).build(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/storage/MrmDataMissingException.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.storage; 2 | 3 | import org.whispersystems.textsecuregcm.util.NoStackTraceRuntimeException; 4 | 5 | class MrmDataMissingException extends NoStackTraceRuntimeException { 6 | 7 | enum Type { 8 | SHARED, 9 | RECIPIENT_VIEW 10 | } 11 | 12 | private final Type type; 13 | 14 | MrmDataMissingException(final Type type) { 15 | this.type = type; 16 | } 17 | 18 | Type getType() { 19 | return type; 20 | } 21 | 22 | @Override 23 | public String toString() { 24 | return "MrmDataMissingException{type=%s}".formatted(type); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /websocket-resources/src/main/java/org/whispersystems/websocket/logging/layout/converters/StatusCodeConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.websocket.logging.layout.converters; 6 | 7 | import org.whispersystems.websocket.logging.WebsocketEvent; 8 | 9 | public class StatusCodeConverter extends WebSocketEventConverter { 10 | @Override 11 | public String convert(WebsocketEvent event) { 12 | if (event.getStatusCode() == WebsocketEvent.SENTINEL) { 13 | return WebsocketEvent.NA; 14 | } else { 15 | return Integer.toString(event.getStatusCode()); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /service/src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /websocket-resources/src/main/java/org/whispersystems/websocket/logging/layout/converters/ContentLengthConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.websocket.logging.layout.converters; 6 | 7 | import org.whispersystems.websocket.logging.WebsocketEvent; 8 | 9 | public class ContentLengthConverter extends WebSocketEventConverter { 10 | @Override 11 | public String convert(WebsocketEvent event) { 12 | if (event.getContentLength() == WebsocketEvent.SENTINEL) { 13 | return WebsocketEvent.NA; 14 | } else { 15 | return Long.toString(event.getContentLength()); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/PubSubPublisherFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.configuration; 7 | 8 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 9 | import com.google.cloud.pubsub.v1.PublisherInterface; 10 | import io.dropwizard.jackson.Discoverable; 11 | import java.io.IOException; 12 | 13 | @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = DefaultPubSubPublisherFactory.class) 14 | public interface PubSubPublisherFactory extends Discoverable { 15 | 16 | PublisherInterface build() throws IOException; 17 | } 18 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/registration/TransportNotAllowedException.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.registration; 2 | 3 | import org.whispersystems.textsecuregcm.entities.RegistrationServiceSession; 4 | 5 | /** 6 | * Indicates that a request to send a verification code failed because the destination number does not support the 7 | * requested transport (e.g. the caller asked to send an SMS to a landline number). 8 | */ 9 | public class TransportNotAllowedException extends RegistrationServiceException { 10 | 11 | public TransportNotAllowedException(RegistrationServiceSession registrationServiceSession) { 12 | super(registrationServiceSession); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/entities/AnswerChallengeRequest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.entities; 7 | 8 | import com.fasterxml.jackson.annotation.JsonSubTypes; 9 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 10 | 11 | @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") 12 | @JsonSubTypes({ 13 | @JsonSubTypes.Type(value = AnswerPushChallengeRequest.class, name = "rateLimitPushChallenge"), 14 | @JsonSubTypes.Type(value = AnswerCaptchaChallengeRequest.class, name = "captcha") 15 | }) 16 | public abstract class AnswerChallengeRequest { 17 | } 18 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/util/Futures.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.util; 7 | 8 | import java.util.concurrent.CompletionStage; 9 | import org.apache.commons.lang3.function.TriFunction; 10 | 11 | public class Futures { 12 | 13 | public static CompletionStage zipWith( 14 | CompletionStage futureT, 15 | CompletionStage futureU, 16 | CompletionStage futureV, 17 | TriFunction fun) { 18 | 19 | return futureT.thenCompose(t -> futureU.thenCombine(futureV, (u, v) -> fun.apply(t, u, v))); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/auth/AuthenticatedDevice.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.auth; 7 | 8 | import java.security.Principal; 9 | import java.time.Instant; 10 | import java.util.UUID; 11 | import javax.security.auth.Subject; 12 | 13 | public record AuthenticatedDevice(UUID accountIdentifier, byte deviceId, Instant primaryDeviceLastSeen) 14 | implements Principal { 15 | 16 | @Override 17 | public String getName() { 18 | return null; 19 | } 20 | 21 | @Override 22 | public boolean implies(final Subject subject) { 23 | return false; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/entities/DeliveryCertificate.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.entities; 7 | 8 | import com.fasterxml.jackson.annotation.JsonCreator; 9 | import com.fasterxml.jackson.annotation.JsonProperty; 10 | 11 | public class DeliveryCertificate { 12 | 13 | private final byte[] certificate; 14 | 15 | @JsonCreator 16 | public DeliveryCertificate( 17 | @JsonProperty("certificate") byte[] certificate) { 18 | this.certificate = certificate; 19 | } 20 | 21 | public byte[] getCertificate() { 22 | return certificate; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/storage/VerificationSessions.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.storage; 7 | 8 | import java.time.Clock; 9 | import org.whispersystems.textsecuregcm.registration.VerificationSession; 10 | import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient; 11 | 12 | public class VerificationSessions extends SerializedExpireableJsonDynamoStore { 13 | 14 | public VerificationSessions(final DynamoDbAsyncClient dynamoDbClient, final String tableName, final Clock clock) { 15 | super(dynamoDbClient, tableName, clock); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/auth/AuthenticatedBackupUser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.auth; 7 | 8 | import org.signal.libsignal.zkgroup.backups.BackupCredentialType; 9 | import org.signal.libsignal.zkgroup.backups.BackupLevel; 10 | import org.whispersystems.textsecuregcm.util.ua.UserAgent; 11 | import javax.annotation.Nullable; 12 | 13 | public record AuthenticatedBackupUser( 14 | byte[] backupId, 15 | BackupCredentialType credentialType, 16 | BackupLevel backupLevel, 17 | String backupDir, 18 | String mediaDir, 19 | @Nullable UserAgent userAgent) { 20 | } 21 | -------------------------------------------------------------------------------- /service/src/test/java/org/whispersystems/textsecuregcm/configuration/StubPubSubPublisherFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.configuration; 7 | 8 | import com.fasterxml.jackson.annotation.JsonTypeName; 9 | import com.google.api.core.ApiFutures; 10 | import com.google.cloud.pubsub.v1.PublisherInterface; 11 | import java.util.UUID; 12 | 13 | @JsonTypeName("stub") 14 | public class StubPubSubPublisherFactory implements PubSubPublisherFactory { 15 | 16 | @Override 17 | public PublisherInterface build() { 18 | return message -> ApiFutures.immediateFuture(UUID.randomUUID().toString()); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/auth/DisconnectionRequestListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.auth; 7 | 8 | /** 9 | * A disconnection request listener receives and handles a request to close an authenticated network connection for a 10 | * specific client. 11 | */ 12 | public interface DisconnectionRequestListener { 13 | 14 | /** 15 | * Handles a request to close an authenticated network connection for a specific authenticated device. Requests are 16 | * dispatched on dedicated threads, and implementations may safely block. 17 | */ 18 | void handleDisconnectionRequest(); 19 | } 20 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/util/logging/UriInfoUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2021 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.util.logging; 7 | 8 | import org.glassfish.jersey.server.ExtendedUriInfo; 9 | 10 | public class UriInfoUtil { 11 | 12 | public static String getPathTemplate(final ExtendedUriInfo uriInfo) { 13 | final StringBuilder pathBuilder = new StringBuilder(); 14 | 15 | for (int i = uriInfo.getMatchedTemplates().size() - 1; i >= 0; i--) { 16 | pathBuilder.append(uriInfo.getMatchedTemplates().get(i).getTemplate()); 17 | } 18 | 19 | return pathBuilder.toString(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/asn/AsnInfoProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2021 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.asn; 7 | 8 | import java.util.Optional; 9 | import javax.annotation.Nonnull; 10 | 11 | public interface AsnInfoProvider { 12 | 13 | /// Gets ASN information for an IP address. 14 | /// 15 | /// @param ipString a string representation of an IP address 16 | /// 17 | /// @return ASN information for the given IP address or empty if no ASN information was found for the given IP address 18 | Optional lookup(@Nonnull String ipString); 19 | 20 | AsnInfoProvider EMPTY = _ -> Optional.empty(); 21 | } 22 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/identity/IdentityType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.identity; 7 | 8 | public enum IdentityType { 9 | ACI((byte) 0x00, "ACI:"), 10 | PNI((byte) 0x01, "PNI:"); 11 | 12 | private final byte bytePrefix; 13 | private final String stringPrefix; 14 | 15 | IdentityType(final byte bytePrefix, final String stringPrefix) { 16 | this.bytePrefix = bytePrefix; 17 | this.stringPrefix = stringPrefix; 18 | } 19 | 20 | byte getBytePrefix() { 21 | return bytePrefix; 22 | } 23 | 24 | String getStringPrefix() { 25 | return stringPrefix; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /service/src/test/java/org/whispersystems/textsecuregcm/tests/util/TestPrincipal.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.textsecuregcm.tests.util; 6 | 7 | import java.security.Principal; 8 | import java.util.Optional; 9 | 10 | public class TestPrincipal implements Principal { 11 | 12 | private final String name; 13 | 14 | private TestPrincipal(String name) { 15 | this.name = name; 16 | } 17 | 18 | @Override 19 | public String getName() { 20 | return name; 21 | } 22 | 23 | public static Optional authenticatedTestPrincipal(final String name) { 24 | return Optional.of(new TestPrincipal(name)); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/badges/ProfileBadgeConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.badges; 7 | 8 | import java.util.List; 9 | import java.util.Locale; 10 | import org.whispersystems.textsecuregcm.entities.Badge; 11 | import org.whispersystems.textsecuregcm.storage.AccountBadge; 12 | 13 | public interface ProfileBadgeConverter { 14 | 15 | /** 16 | * Converts the {@link AccountBadge}s for an account into the objects 17 | * that can be returned on a profile fetch. 18 | */ 19 | List convert(List acceptableLanguages, List accountBadges, boolean isSelf); 20 | } 21 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/DefaultAwsCredentialsFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.configuration; 7 | 8 | import com.fasterxml.jackson.annotation.JsonTypeName; 9 | import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; 10 | import software.amazon.awssdk.auth.credentials.WebIdentityTokenFileCredentialsProvider; 11 | 12 | @JsonTypeName("default") 13 | public record DefaultAwsCredentialsFactory() implements AwsCredentialsProviderFactory { 14 | 15 | public AwsCredentialsProvider build() { 16 | return WebIdentityTokenFileCredentialsProvider.create(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/CdnConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.configuration; 7 | 8 | import jakarta.validation.Valid; 9 | import jakarta.validation.constraints.NotBlank; 10 | import jakarta.validation.constraints.NotNull; 11 | import javax.annotation.Nullable; 12 | import java.net.URI; 13 | 14 | public record CdnConfiguration(@NotNull @Valid StaticAwsCredentialsFactory credentials, 15 | @NotBlank String bucket, 16 | @NotBlank String region, 17 | @Nullable URI endpointOverride) { 18 | } 19 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/grpc/DeviceIdUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.grpc; 7 | 8 | import io.grpc.Status; 9 | import org.whispersystems.textsecuregcm.storage.Device; 10 | 11 | public class DeviceIdUtil { 12 | 13 | public static boolean isValid(int deviceId) { 14 | return deviceId >= Device.PRIMARY_ID && deviceId <= Byte.MAX_VALUE; 15 | } 16 | 17 | static byte validate(int deviceId) { 18 | if (!isValid(deviceId)) { 19 | throw Status.INVALID_ARGUMENT.withDescription("Device ID is out of range").asRuntimeException(); 20 | } 21 | 22 | return (byte) deviceId; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /websocket-resources/src/main/java/org/whispersystems/websocket/logging/layout/converters/RequestUrlConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.websocket.logging.layout.converters; 6 | 7 | import org.whispersystems.websocket.logging.WebsocketEvent; 8 | 9 | public class RequestUrlConverter extends WebSocketEventConverter { 10 | @Override 11 | public String convert(WebsocketEvent event) { 12 | return 13 | event.getMethod() + 14 | WebSocketEventConverter.SPACE_CHAR + 15 | event.getPath() + 16 | WebSocketEventConverter.SPACE_CHAR + 17 | event.getProtocol(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/AppleDeviceCheckConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.textsecuregcm.configuration; 6 | 7 | import java.time.Duration; 8 | 9 | /** 10 | * Configuration for Apple DeviceCheck 11 | * 12 | * @param production Whether this is for production or sandbox attestations 13 | * @param teamId The teamId to validate attestations against 14 | * @param bundleId The bundleId to validation attestations against 15 | */ 16 | public record AppleDeviceCheckConfiguration( 17 | boolean production, 18 | String teamId, 19 | String bundleId) {} 20 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/subscriptions/SubscriptionInformation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.textsecuregcm.subscriptions; 6 | 7 | import java.time.Instant; 8 | import javax.annotation.Nullable; 9 | 10 | public record SubscriptionInformation( 11 | SubscriptionPrice price, 12 | long level, 13 | Instant billingCycleAnchor, 14 | Instant endOfCurrentPeriod, 15 | boolean active, 16 | boolean cancelAtPeriodEnd, 17 | SubscriptionStatus status, 18 | PaymentProvider paymentProvider, 19 | PaymentMethod paymentMethod, 20 | boolean paymentProcessing, 21 | @Nullable ChargeFailure chargeFailure) {} 22 | -------------------------------------------------------------------------------- /service/src/main/resources/lua/remove_recipient_view_from_mrm_data.lua: -------------------------------------------------------------------------------- 1 | -- Removes the given recipient view from the shared MRM data. If the only field remaining after the removal is the 2 | -- `data` field, then the key will be deleted 3 | 4 | local sharedMrmKeys = KEYS -- KEYS: list of all keys in a single slot to update 5 | local recipientViewToRemove = ARGV[1] -- the recipient view to remove from the hash 6 | 7 | local keysDeleted = 0 8 | 9 | for _, sharedMrmKey in ipairs(sharedMrmKeys) do 10 | redis.call("HDEL", sharedMrmKey, recipientViewToRemove) 11 | if redis.call("HLEN", sharedMrmKey) == 1 then 12 | redis.call("DEL", sharedMrmKey) 13 | keysDeleted = keysDeleted + 1 14 | end 15 | end 16 | 17 | return keysDeleted 18 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/ApnConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.textsecuregcm.configuration; 6 | 7 | import jakarta.validation.constraints.NotBlank; 8 | import jakarta.validation.constraints.NotNull; 9 | import org.whispersystems.textsecuregcm.configuration.secrets.SecretString; 10 | 11 | 12 | public record ApnConfiguration(@NotNull SecretString teamId, 13 | @NotNull SecretString keyId, 14 | @NotNull SecretString signingKey, 15 | @NotBlank String bundleId, 16 | boolean sandbox) { 17 | } 18 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/entities/RemoteAttachmentError.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.textsecuregcm.entities; 6 | 7 | import io.swagger.v3.oas.annotations.media.Schema; 8 | import jakarta.validation.Valid; 9 | import jakarta.validation.constraints.NotNull; 10 | 11 | @Schema(description = "Indicates an attachment failed to upload") 12 | public record RemoteAttachmentError( 13 | @Schema(description = "The type of error encountered") 14 | @Valid @NotNull ErrorType error) 15 | implements TransferArchiveResult { 16 | 17 | public enum ErrorType { 18 | RELINK_REQUESTED, 19 | CONTINUE_WITHOUT_UPLOAD; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/entities/TransferArchiveResult.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.entities; 7 | 8 | import com.fasterxml.jackson.annotation.JsonSubTypes; 9 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 10 | import io.swagger.v3.oas.annotations.media.Schema; 11 | 12 | @JsonTypeInfo(use = JsonTypeInfo.Id.DEDUCTION) 13 | @JsonSubTypes({ 14 | @JsonSubTypes.Type(value = RemoteAttachment.class, name = "success"), 15 | @JsonSubTypes.Type(value = RemoteAttachmentError.class, name = "error"), 16 | }) 17 | public sealed interface TransferArchiveResult permits RemoteAttachment, RemoteAttachmentError {} 18 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/MaxDeviceConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.configuration; 7 | 8 | import com.fasterxml.jackson.annotation.JsonProperty; 9 | import jakarta.validation.constraints.NotEmpty; 10 | import jakarta.validation.constraints.NotNull; 11 | 12 | public class MaxDeviceConfiguration { 13 | 14 | @JsonProperty 15 | @NotEmpty 16 | private String number; 17 | 18 | @JsonProperty 19 | @NotNull 20 | private int count; 21 | 22 | public String getNumber() { 23 | return number; 24 | } 25 | 26 | public int getCount() { 27 | return count; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/storage/AccountUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.storage; 7 | 8 | import org.whispersystems.textsecuregcm.util.SystemMapper; 9 | import java.io.IOException; 10 | 11 | public class AccountUtil { 12 | 13 | static Account cloneAccountAsNotStale(final Account account) { 14 | try { 15 | return SystemMapper.jsonMapper().readValue( 16 | SystemMapper.jsonMapper().writeValueAsBytes(account), Account.class); 17 | } catch (final IOException e) { 18 | // this should really, truly, never happen 19 | throw new IllegalArgumentException(e); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/controllers/DeviceLimitExceededException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.controllers; 7 | 8 | 9 | public class DeviceLimitExceededException extends Exception { 10 | 11 | private final int currentDevices; 12 | private final int maxDevices; 13 | 14 | public DeviceLimitExceededException(int currentDevices, int maxDevices) { 15 | this.currentDevices = currentDevices; 16 | this.maxDevices = maxDevices; 17 | } 18 | 19 | public int getCurrentDevices() { 20 | return currentDevices; 21 | } 22 | 23 | public int getMaxDevices() { 24 | return maxDevices; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/util/logging/RequestLogEnabledFilterFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.util.logging; 7 | 8 | import ch.qos.logback.access.common.spi.IAccessEvent; 9 | import ch.qos.logback.core.filter.Filter; 10 | import com.fasterxml.jackson.annotation.JsonTypeName; 11 | import io.dropwizard.logging.common.filter.FilterFactory; 12 | 13 | @JsonTypeName("requestLogEnabled") 14 | class RequestLogEnabledFilterFactory implements FilterFactory { 15 | 16 | @Override 17 | public Filter build() { 18 | return RequestLogManager.getHttpRequestLogFilter(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /service/src/main/resources/lua/apn/schedule_background_notification.lua: -------------------------------------------------------------------------------- 1 | local lastBackgroundNotificationTimestampKey = KEYS[1] 2 | local queueKey = KEYS[2] 3 | 4 | local accountDevicePair = ARGV[1] 5 | local currentTimeMillis = tonumber(ARGV[2]) 6 | local backgroundNotificationPeriod = tonumber(ARGV[3]) 7 | 8 | local lastBackgroundNotificationTimestamp = redis.call("GET", lastBackgroundNotificationTimestampKey) 9 | local nextNotificationTimestamp 10 | 11 | if (lastBackgroundNotificationTimestamp) then 12 | nextNotificationTimestamp = tonumber(lastBackgroundNotificationTimestamp) + backgroundNotificationPeriod 13 | else 14 | nextNotificationTimestamp = currentTimeMillis 15 | end 16 | 17 | redis.call("ZADD", queueKey, "NX", nextNotificationTimestamp, accountDevicePair) 18 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/auth/ChangesPhoneNumber.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.auth; 7 | 8 | import java.lang.annotation.ElementType; 9 | import java.lang.annotation.Retention; 10 | import java.lang.annotation.RetentionPolicy; 11 | import java.lang.annotation.Target; 12 | 13 | /** 14 | * Indicates that an endpoint changes the phone number and PNI keys associated with an account, and that 15 | * any websockets associated with the account may need to be refreshed after a call to that endpoint. 16 | */ 17 | @Target(ElementType.METHOD) 18 | @Retention(RetentionPolicy.RUNTIME) 19 | public @interface ChangesPhoneNumber { 20 | } 21 | -------------------------------------------------------------------------------- /websocket-resources/src/main/java/org/whispersystems/websocket/auth/AuthenticatedWebSocketUpgradeFilter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.websocket.auth; 7 | 8 | import java.security.Principal; 9 | import java.util.Optional; 10 | import org.eclipse.jetty.websocket.server.JettyServerUpgradeRequest; 11 | import org.eclipse.jetty.websocket.server.JettyServerUpgradeResponse; 12 | 13 | public interface AuthenticatedWebSocketUpgradeFilter { 14 | 15 | void handleAuthentication(@SuppressWarnings("OptionalUsedAsFieldOrParameterType") Optional authenticated, 16 | JettyServerUpgradeRequest request, 17 | JettyServerUpgradeResponse response); 18 | } 19 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/FaultTolerantRedisClientFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.configuration; 7 | 8 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 9 | import io.dropwizard.jackson.Discoverable; 10 | import io.lettuce.core.resource.ClientResources; 11 | import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisClient; 12 | 13 | @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = RedisConfiguration.class) 14 | public interface FaultTolerantRedisClientFactory extends Discoverable { 15 | 16 | FaultTolerantRedisClient build(String name, ClientResources clientResources); 17 | } 18 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/subscriptions/ProcessorCustomer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.subscriptions; 7 | 8 | import java.nio.charset.StandardCharsets; 9 | 10 | public record ProcessorCustomer(String customerId, PaymentProvider processor) { 11 | 12 | public byte[] toDynamoBytes() { 13 | final byte[] customerIdBytes = customerId.getBytes(StandardCharsets.UTF_8); 14 | final byte[] combinedBytes = new byte[customerIdBytes.length + 1]; 15 | 16 | combinedBytes[0] = processor.getId(); 17 | System.arraycopy(customerIdBytes, 0, combinedBytes, 1, customerIdBytes.length); 18 | 19 | return combinedBytes; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /service/src/main/resources/lua/insert_shared_multirecipient_message_data.lua: -------------------------------------------------------------------------------- 1 | -- inserts shared multi-recipient message data 2 | 3 | local sharedMrmKey = KEYS[1] -- [string] the key containing the shared MRM data 4 | local mrmData = ARGV[1] -- [bytes] the serialized multi-recipient message data 5 | -- the remainder of ARGV is list of recipient keys and view data 6 | 7 | if 1 == redis.call("EXISTS", sharedMrmKey) then 8 | return redis.error_reply("ERR key exists") 9 | end 10 | 11 | redis.call("HSET", sharedMrmKey, "data", mrmData); 12 | redis.call("EXPIRE", sharedMrmKey, 604800) -- 7 days 13 | 14 | -- unpack() fails with "too many results" at very large table sizes, so we loop 15 | for i = 2, #ARGV, 2 do 16 | redis.call("HSET", sharedMrmKey, ARGV[i], ARGV[i + 1]) 17 | end 18 | -------------------------------------------------------------------------------- /websocket-resources/src/main/java/org/whispersystems/websocket/logging/AsyncWebsocketEventAppenderFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.websocket.logging; 6 | 7 | import ch.qos.logback.core.AsyncAppenderBase; 8 | import io.dropwizard.logging.common.async.AsyncAppenderFactory; 9 | 10 | public class AsyncWebsocketEventAppenderFactory implements AsyncAppenderFactory { 11 | @Override 12 | public AsyncAppenderBase build() { 13 | return new AsyncAppenderBase() { 14 | @Override 15 | protected void preprocess(WebsocketEvent event) { 16 | event.prepareForDeferredProcessing(); 17 | } 18 | }; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/ExternalRequestFilterConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.configuration; 7 | 8 | import jakarta.validation.Valid; 9 | import jakarta.validation.constraints.NotNull; 10 | import java.util.Set; 11 | import org.whispersystems.textsecuregcm.util.InetAddressRange; 12 | 13 | public record ExternalRequestFilterConfiguration(@Valid @NotNull Set<@NotNull String> paths, 14 | @Valid @NotNull Set<@NotNull InetAddressRange> permittedInternalRanges, 15 | @Valid @NotNull Set<@NotNull String> grpcMethods) { 16 | } 17 | -------------------------------------------------------------------------------- /service/src/test/java/org/whispersystems/textsecuregcm/tests/util/FakeDynamicConfigurationManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.tests.util; 7 | 8 | import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager; 9 | 10 | public class FakeDynamicConfigurationManager extends DynamicConfigurationManager { 11 | 12 | T staticConfiguration; 13 | 14 | public FakeDynamicConfigurationManager(T staticConfiguration) { 15 | super(null, (Class) staticConfiguration.getClass()); 16 | this.staticConfiguration = staticConfiguration; 17 | } 18 | 19 | @Override 20 | public T getConfiguration() { 21 | return staticConfiguration; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/auth/ChangesLinkedDevices.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2021 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.auth; 7 | 8 | import java.lang.annotation.ElementType; 9 | import java.lang.annotation.Retention; 10 | import java.lang.annotation.RetentionPolicy; 11 | import java.lang.annotation.Target; 12 | 13 | /** 14 | * Indicates that an endpoint may change the "enabled" state of one or more devices associated with an account, and that 15 | * any websockets associated with the account may need to be refreshed after a call to that endpoint. 16 | */ 17 | @Target(ElementType.METHOD) 18 | @Retention(RetentionPolicy.RUNTIME) 19 | public @interface ChangesLinkedDevices { 20 | } 21 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/VirtualThreadConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.textsecuregcm.configuration; 6 | 7 | import java.time.Duration; 8 | 9 | public record VirtualThreadConfiguration( 10 | Duration pinEventThreshold, 11 | Integer maxConcurrentThreadsPerExecutor) { 12 | 13 | public VirtualThreadConfiguration() { 14 | this(null, null); 15 | } 16 | 17 | public VirtualThreadConfiguration { 18 | if (maxConcurrentThreadsPerExecutor == null) { 19 | maxConcurrentThreadsPerExecutor = 1_000_000; 20 | } 21 | if (pinEventThreshold == null) { 22 | pinEventThreshold = Duration.ofMillis(1); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/mappers/InvalidWebsocketAddressExceptionMapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.mappers; 7 | 8 | import jakarta.ws.rs.core.Response; 9 | import jakarta.ws.rs.ext.ExceptionMapper; 10 | import jakarta.ws.rs.ext.Provider; 11 | import org.whispersystems.textsecuregcm.websocket.InvalidWebsocketAddressException; 12 | 13 | @Provider 14 | public class InvalidWebsocketAddressExceptionMapper implements ExceptionMapper { 15 | @Override 16 | public Response toResponse(InvalidWebsocketAddressException exception) { 17 | return Response.status(Response.Status.BAD_REQUEST).build(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /websocket-resources/src/main/java/org/whispersystems/websocket/logging/layout/WebsocketEventLayoutFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.websocket.logging.layout; 6 | 7 | import ch.qos.logback.classic.LoggerContext; 8 | import ch.qos.logback.core.pattern.PatternLayoutBase; 9 | import io.dropwizard.logging.common.layout.LayoutFactory; 10 | import org.whispersystems.websocket.logging.WebsocketEvent; 11 | 12 | import java.util.TimeZone; 13 | 14 | public class WebsocketEventLayoutFactory implements LayoutFactory { 15 | @Override 16 | public PatternLayoutBase build(LoggerContext context, TimeZone timeZone) { 17 | return new WebsocketEventLayout(context); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/grpc/net/ManagedGrpcServer.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.grpc.net; 2 | 3 | import io.dropwizard.lifecycle.Managed; 4 | import io.grpc.Server; 5 | 6 | import java.io.IOException; 7 | import java.util.concurrent.TimeUnit; 8 | 9 | public class ManagedGrpcServer implements Managed { 10 | private final Server server; 11 | 12 | public ManagedGrpcServer(Server server) { 13 | this.server = server; 14 | } 15 | 16 | @Override 17 | public void start() throws IOException { 18 | server.start(); 19 | } 20 | 21 | @Override 22 | public void stop() { 23 | try { 24 | server.shutdown().awaitTermination(5, TimeUnit.MINUTES); 25 | } catch (final InterruptedException e) { 26 | server.shutdownNow(); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/FaultTolerantRedisClusterFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.configuration; 7 | 8 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 9 | import io.dropwizard.jackson.Discoverable; 10 | import io.lettuce.core.resource.ClientResources; 11 | import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisClusterClient; 12 | 13 | @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = RedisClusterConfiguration.class) 14 | public interface FaultTolerantRedisClusterFactory extends Discoverable { 15 | 16 | FaultTolerantRedisClusterClient build(String name, ClientResources.Builder clientResourcesBuilder); 17 | } 18 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/SpamFilterConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2021 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.configuration; 7 | 8 | import com.fasterxml.jackson.annotation.JsonCreator; 9 | import com.fasterxml.jackson.annotation.JsonProperty; 10 | import jakarta.validation.constraints.NotBlank; 11 | 12 | public class SpamFilterConfiguration { 13 | 14 | @JsonProperty 15 | @NotBlank 16 | private final String environment; 17 | 18 | @JsonCreator 19 | public SpamFilterConfiguration(@JsonProperty("environment") final String environment) { 20 | this.environment = environment; 21 | } 22 | 23 | public String getEnvironment() { 24 | return environment; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/util/logging/RequestLogManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.util.logging; 7 | 8 | import ch.qos.logback.access.common.spi.IAccessEvent; 9 | import ch.qos.logback.core.filter.Filter; 10 | 11 | public class RequestLogManager { 12 | private static final RequestLogEnabledFilter HTTP_REQUEST_LOG_FILTER = new RequestLogEnabledFilter<>(); 13 | 14 | static Filter getHttpRequestLogFilter() { 15 | return HTTP_REQUEST_LOG_FILTER; 16 | } 17 | 18 | public static void setRequestLoggingEnabled(final boolean enabled) { 19 | HTTP_REQUEST_LOG_FILTER.setRequestLoggingEnabled(enabled); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/util/NonNormalizedPhoneNumberException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2021 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.util; 7 | 8 | public class NonNormalizedPhoneNumberException extends Exception { 9 | 10 | private final String originalNumber; 11 | private final String normalizedNumber; 12 | 13 | public NonNormalizedPhoneNumberException(final String originalNumber, final String normalizedNumber) { 14 | this.originalNumber = originalNumber; 15 | this.normalizedNumber = normalizedNumber; 16 | } 17 | 18 | public String getOriginalNumber() { 19 | return originalNumber; 20 | } 21 | 22 | public String getNormalizedNumber() { 23 | return normalizedNumber; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/util/logging/RequestLogEnabledFilter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.util.logging; 7 | 8 | import ch.qos.logback.core.filter.Filter; 9 | import ch.qos.logback.core.spi.FilterReply; 10 | 11 | class RequestLogEnabledFilter extends Filter { 12 | 13 | private volatile boolean requestLoggingEnabled = false; 14 | 15 | @Override 16 | public FilterReply decide(final E event) { 17 | return requestLoggingEnabled ? FilterReply.NEUTRAL : FilterReply.DENY; 18 | } 19 | 20 | public void setRequestLoggingEnabled(final boolean requestLoggingEnabled) { 21 | this.requestLoggingEnabled = requestLoggingEnabled; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/ReportMessageConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2021 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.configuration; 7 | 8 | import com.fasterxml.jackson.annotation.JsonProperty; 9 | import jakarta.validation.constraints.NotNull; 10 | import java.time.Duration; 11 | 12 | public class ReportMessageConfiguration { 13 | 14 | @JsonProperty 15 | @NotNull 16 | private final Duration reportTtl = Duration.ofDays(7); 17 | 18 | @JsonProperty 19 | @NotNull 20 | private final Duration counterTtl = Duration.ofDays(1); 21 | 22 | public Duration getReportTtl() { 23 | return reportTtl; 24 | } 25 | 26 | public Duration getCounterTtl() { 27 | return counterTtl; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/subscriptions/AppleAppStoreDecodedTransaction.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.subscriptions; 2 | 3 | import com.apple.itunes.storekit.model.JWSRenewalInfoDecodedPayload; 4 | import com.apple.itunes.storekit.model.JWSTransactionDecodedPayload; 5 | import com.apple.itunes.storekit.model.LastTransactionsItem; 6 | 7 | /** 8 | * A decoded and validated storekit transaction 9 | * 10 | * @param signedTransaction The transaction 11 | * @param transaction The transaction info with a validated signature 12 | * @param renewalInfo The renewal info with a validated signature 13 | */ 14 | record AppleAppStoreDecodedTransaction( 15 | LastTransactionsItem signedTransaction, 16 | JWSTransactionDecodedPayload transaction, 17 | JWSRenewalInfoDecodedPayload renewalInfo) {} 18 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/auth/Anonymous.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.auth; 7 | 8 | import jakarta.ws.rs.WebApplicationException; 9 | import jakarta.ws.rs.core.Response; 10 | import java.util.Base64; 11 | 12 | public class Anonymous { 13 | 14 | private final byte[] unidentifiedSenderAccessKey; 15 | 16 | public Anonymous(String header) { 17 | try { 18 | this.unidentifiedSenderAccessKey = Base64.getDecoder().decode(header); 19 | } catch (IllegalArgumentException e) { 20 | throw new WebApplicationException(e, Response.Status.UNAUTHORIZED); 21 | } 22 | } 23 | 24 | public byte[] getAccessKey() { 25 | return unidentifiedSenderAccessKey; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/PaymentsServiceConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.configuration; 7 | 8 | import jakarta.validation.Valid; 9 | import jakarta.validation.constraints.NotEmpty; 10 | import jakarta.validation.constraints.NotNull; 11 | import java.util.List; 12 | import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes; 13 | 14 | public record PaymentsServiceConfiguration(@NotNull SecretBytes userAuthenticationTokenSharedSecret, 15 | @NotEmpty List paymentCurrencies, 16 | @NotNull @Valid PaymentsServiceClientsFactory externalClients) { 17 | 18 | 19 | } 20 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/entities/CredentialProfileResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2021 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.entities; 7 | 8 | import com.fasterxml.jackson.annotation.JsonUnwrapped; 9 | 10 | public abstract class CredentialProfileResponse { 11 | 12 | @JsonUnwrapped 13 | private VersionedProfileResponse versionedProfileResponse; 14 | 15 | protected CredentialProfileResponse() { 16 | } 17 | 18 | protected CredentialProfileResponse(final VersionedProfileResponse versionedProfileResponse) { 19 | this.versionedProfileResponse = versionedProfileResponse; 20 | } 21 | 22 | public VersionedProfileResponse getVersionedProfileResponse() { 23 | return versionedProfileResponse; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/storage/RemoteConfigsManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.storage; 7 | 8 | import com.google.common.base.Suppliers; 9 | import java.util.List; 10 | import java.util.concurrent.TimeUnit; 11 | import java.util.function.Supplier; 12 | 13 | public class RemoteConfigsManager { 14 | 15 | private final Supplier> remoteConfigSupplier; 16 | 17 | public RemoteConfigsManager(RemoteConfigs remoteConfigs) { 18 | remoteConfigSupplier = 19 | Suppliers.memoizeWithExpiration(remoteConfigs::getAll, 10, TimeUnit.SECONDS); 20 | } 21 | 22 | public List getAll() { 23 | return remoteConfigSupplier.get(); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/util/AbstractPublicKeySerializer.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.util; 2 | 3 | import com.fasterxml.jackson.core.JsonGenerator; 4 | import com.fasterxml.jackson.databind.JsonSerializer; 5 | import com.fasterxml.jackson.databind.SerializerProvider; 6 | import java.io.IOException; 7 | import java.util.Base64; 8 | 9 | abstract class AbstractPublicKeySerializer extends JsonSerializer { 10 | 11 | @Override 12 | public void serialize(final K publicKey, 13 | final JsonGenerator jsonGenerator, 14 | final SerializerProvider serializerProvider) throws IOException { 15 | 16 | jsonGenerator.writeString(Base64.getEncoder().encodeToString(serializePublicKey(publicKey))); 17 | } 18 | 19 | protected abstract byte[] serializePublicKey(final K publicKey); 20 | } 21 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/util/HostnameUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2021 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.util; 7 | 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import java.net.InetAddress; 11 | import java.net.UnknownHostException; 12 | import java.util.Locale; 13 | 14 | public class HostnameUtil { 15 | 16 | private static final Logger log = LoggerFactory.getLogger(HostnameUtil.class); 17 | 18 | public static String getLocalHostname() { 19 | try { 20 | return InetAddress.getLocalHost().getHostName().toLowerCase(Locale.US); 21 | } catch (final UnknownHostException e) { 22 | log.warn("Failed to get hostname", e); 23 | return "unknown"; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/util/Optionals.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.util; 2 | 3 | import java.util.Optional; 4 | import java.util.function.BiFunction; 5 | 6 | public class Optionals { 7 | 8 | private Optionals() {} 9 | 10 | /** 11 | * Apply a function to two optional arguments, returning empty if either argument is empty 12 | * 13 | * @param optionalT Optional of type T 14 | * @param optionalU Optional of type U 15 | * @param fun Function of T and U that returns R 16 | * @return The function applied to the values of optionalT and optionalU, or empty 17 | */ 18 | public static Optional zipWith(Optional optionalT, Optional optionalU, BiFunction fun) { 19 | return optionalT.flatMap(t -> optionalU.map(u -> fun.apply(t, u))); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /service/src/main/proto/org/signal/chat/payments.proto: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | syntax = "proto3"; 7 | 8 | option java_multiple_files = true; 9 | 10 | package org.signal.chat.payments; 11 | 12 | /** 13 | * Provides methods for working with payments. 14 | */ 15 | service Payments { 16 | /** 17 | */ 18 | rpc GetCurrencyConversions(GetCurrencyConversionsRequest) returns (GetCurrencyConversionsResponse) {} 19 | } 20 | 21 | message GetCurrencyConversionsRequest { 22 | } 23 | 24 | message GetCurrencyConversionsResponse { 25 | 26 | message CurrencyConversionEntity { 27 | 28 | string base = 1; 29 | 30 | map conversions = 2; 31 | } 32 | 33 | uint64 timestamp = 1; 34 | 35 | repeated CurrencyConversionEntity currencies = 2; 36 | } 37 | -------------------------------------------------------------------------------- /integration-tests/src/main/java/org/signal/integration/config/Config.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.signal.integration.config; 7 | 8 | import jakarta.validation.Valid; 9 | import jakarta.validation.constraints.NotBlank; 10 | import jakarta.validation.constraints.NotNull; 11 | import org.whispersystems.textsecuregcm.configuration.DynamoDbClientFactory; 12 | 13 | public record Config(@NotBlank String domain, 14 | @NotBlank String rootCert, 15 | @NotNull @Valid DynamoDbClientFactory dynamoDbClient, 16 | @NotNull @Valid DynamoDbTables dynamoDbTables, 17 | @NotBlank String prescribedRegistrationNumber, 18 | @NotBlank String prescribedRegistrationCode) { 19 | } 20 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/entities/CurrencyConversionEntity.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.entities; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | import java.math.BigDecimal; 6 | import java.util.Map; 7 | 8 | public class CurrencyConversionEntity { 9 | 10 | @JsonProperty 11 | private String base; 12 | 13 | @JsonProperty 14 | private Map conversions; 15 | 16 | public CurrencyConversionEntity(String base, Map conversions) { 17 | this.base = base; 18 | this.conversions = conversions; 19 | } 20 | 21 | public CurrencyConversionEntity() {} 22 | 23 | public String getBase() { 24 | return base; 25 | } 26 | 27 | public Map getConversions() { 28 | return conversions; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/entities/RemoteAttachment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.entities; 7 | 8 | import io.swagger.v3.oas.annotations.media.Schema; 9 | import jakarta.validation.constraints.NotBlank; 10 | import jakarta.validation.constraints.NotNull; 11 | import jakarta.validation.constraints.Size; 12 | import org.whispersystems.textsecuregcm.util.ValidBase64URLString; 13 | 14 | public record RemoteAttachment( 15 | @Schema(description = "The attachment cdn") 16 | @NotNull 17 | Integer cdn, 18 | 19 | @NotBlank 20 | @ValidBase64URLString 21 | @Size(max = 64) 22 | @Schema(description = "The attachment key", maxLength = 64) 23 | String key) implements TransferArchiveResult {} 24 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/util/InstantAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.util; 7 | 8 | import com.fasterxml.jackson.core.JsonGenerator; 9 | import com.fasterxml.jackson.databind.JsonSerializer; 10 | import com.fasterxml.jackson.databind.SerializerProvider; 11 | import java.io.IOException; 12 | import java.time.Instant; 13 | 14 | public class InstantAdapter { 15 | 16 | public static class EpochSecondSerializer extends JsonSerializer { 17 | 18 | @Override 19 | public void serialize(final Instant value, final JsonGenerator gen, final SerializerProvider serializers) 20 | throws IOException { 21 | 22 | gen.writeNumber(value.getEpochSecond()); 23 | } 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /service/src/main/resources/org/signal/bankmandate/BankMandate.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2023 Signal Messenger, LLC 3 | # SPDX-License-Identifier: AGPL-3.0-only 4 | # 5 | 6 | SEPA_MANDATE = By providing your payment information and confirming this payment, you authorise (A) Signal Technology Foundation and Stripe, our payment service provider, to send instructions to your bank to debit your account and (B) your bank to debit your account in accordance with those instructions. As part of your rights, you are entitled to a refund from your bank under the terms and conditions of your agreement with your bank. A refund must be claimed within 8 weeks starting from the date on which your account was debited. Your rights are explained in a statement that you can obtain from your bank. You agree to receive notifications for future debits up to 2 days before they occur. 7 | 8 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/entities/CurrencyConversionEntityList.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.entities; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | import java.util.List; 6 | 7 | public class CurrencyConversionEntityList { 8 | 9 | @JsonProperty 10 | private List currencies; 11 | 12 | @JsonProperty 13 | private long timestamp; 14 | 15 | public CurrencyConversionEntityList(List currencies, long timestamp) { 16 | this.currencies = currencies; 17 | this.timestamp = timestamp; 18 | } 19 | 20 | public CurrencyConversionEntityList() {} 21 | 22 | public List getCurrencies() { 23 | return currencies; 24 | } 25 | 26 | public long getTimestamp() { 27 | return timestamp; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/MessageCacheConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.configuration; 7 | 8 | import com.fasterxml.jackson.annotation.JsonProperty; 9 | import jakarta.validation.Valid; 10 | import jakarta.validation.constraints.NotNull; 11 | 12 | public class MessageCacheConfiguration { 13 | 14 | @JsonProperty 15 | @NotNull 16 | @Valid 17 | private FaultTolerantRedisClusterFactory cluster; 18 | 19 | @JsonProperty 20 | private int persistDelayMinutes = 10; 21 | 22 | public FaultTolerantRedisClusterFactory getRedisClusterConfiguration() { 23 | return cluster; 24 | } 25 | 26 | public int getPersistDelayMinutes() { 27 | return persistDelayMinutes; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/spam/RegistrationRecoveryChecker.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.spam; 2 | 3 | import jakarta.ws.rs.container.ContainerRequestContext; 4 | 5 | public interface RegistrationRecoveryChecker { 6 | 7 | /** 8 | * Determine if a registration recovery attempt should be allowed or not 9 | * 10 | * @param requestContext The container request context for a registration recovery attempt 11 | * @param e164 The E164 formatted phone number of the requester 12 | * @return true if the registration recovery attempt is allowed, false otherwise. 13 | */ 14 | boolean checkRegistrationRecoveryAttempt(final ContainerRequestContext requestContext, final String e164); 15 | 16 | static RegistrationRecoveryChecker noop() { 17 | return (ignoredCtx, ignoredE164) -> true; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/subscriptions/SubscriptionProcessorException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.textsecuregcm.subscriptions; 6 | 7 | public class SubscriptionProcessorException extends SubscriptionException { 8 | 9 | private final PaymentProvider processor; 10 | private final ChargeFailure chargeFailure; 11 | 12 | public SubscriptionProcessorException(final PaymentProvider processor, final ChargeFailure chargeFailure) { 13 | super(null, null); 14 | this.processor = processor; 15 | this.chargeFailure = chargeFailure; 16 | } 17 | 18 | public PaymentProvider getProcessor() { 19 | return processor; 20 | } 21 | 22 | public ChargeFailure getChargeFailure() { 23 | return chargeFailure; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/limits/RateLimiterDescriptor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.limits; 7 | 8 | /** 9 | * Represents an information that defines a rate limiter. 10 | */ 11 | public interface RateLimiterDescriptor { 12 | /** 13 | * Implementing classes will likely be Enums, so name is chosen not to clash with {@link Enum#name()}. 14 | * @return id of this rate limiter to be used in `yml` config files and as a part of the bucket key. 15 | */ 16 | String id(); 17 | 18 | /** 19 | * @return an instance of {@link RateLimiterConfig} to be used by default, 20 | * i.e. if there is no override in the application dynamic configuration. 21 | */ 22 | RateLimiterConfig defaultConfig(); 23 | } 24 | -------------------------------------------------------------------------------- /service/src/test/java/org/whispersystems/textsecuregcm/tests/util/TestRecipient.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.tests.util; 7 | 8 | import org.whispersystems.textsecuregcm.identity.ServiceIdentifier; 9 | 10 | public record TestRecipient(ServiceIdentifier uuid, 11 | byte[] deviceIds, 12 | int[] registrationIds, 13 | byte[] perRecipientKeyMaterial) { 14 | 15 | public TestRecipient(ServiceIdentifier uuid, 16 | byte deviceId, 17 | int registrationId, 18 | byte[] perRecipientKeyMaterial) { 19 | 20 | this(uuid, new byte[]{deviceId}, new int[]{registrationId}, perRecipientKeyMaterial); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/entities/DeviceName.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.entities; 7 | 8 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 9 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 10 | import org.whispersystems.textsecuregcm.util.ByteArrayAdapter; 11 | import jakarta.validation.constraints.NotEmpty; 12 | import jakarta.validation.constraints.Size; 13 | 14 | public record DeviceName(@JsonSerialize(using = ByteArrayAdapter.Serializing.class) 15 | @JsonDeserialize(using = ByteArrayAdapter.Deserializing.class) 16 | @NotEmpty 17 | @Size(max = 225) 18 | byte[] deviceName) { 19 | } 20 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/SubscriptionPriceConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.configuration; 7 | 8 | import jakarta.validation.Valid; 9 | import jakarta.validation.constraints.DecimalMin; 10 | import jakarta.validation.constraints.NotBlank; 11 | import jakarta.validation.constraints.NotEmpty; 12 | import jakarta.validation.constraints.NotNull; 13 | import java.math.BigDecimal; 14 | import java.util.Map; 15 | import org.whispersystems.textsecuregcm.subscriptions.PaymentProvider; 16 | 17 | public record SubscriptionPriceConfiguration(@Valid @NotEmpty Map processorIds, 18 | @NotNull @DecimalMin("0.01") BigDecimal amount) { 19 | 20 | } 21 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/dynamic/DynamicMetricsConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.configuration.dynamic; 7 | 8 | /** 9 | * @param enableLettuceRemoteTag whether the `remote` tag should be added. Note: although this is dynamic, meters are 10 | * cached after creation, so changes will only affect servers launched after the change. 11 | * @param enableAwsSdkMetrics whether to record AWS SDK metrics. Note: although this is dynamic, meters are cached after 12 | * creation, so changes will only affect servers launched after the change. 13 | */ 14 | public record DynamicMetricsConfiguration(boolean enableLettuceRemoteTag, boolean enableAwsSdkMetrics) { 15 | } 16 | -------------------------------------------------------------------------------- /service/src/main/resources/META-INF/validation/constraints-custom.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | org.whispersystems.textsecuregcm.configuration.secrets.SecretStringList$ValidatorNotEmpty 11 | org.whispersystems.textsecuregcm.configuration.secrets.SecretBytesList$ValidatorNotEmpty 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/util/HttpServletRequestUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.util; 7 | 8 | import jakarta.servlet.http.HttpServletRequest; 9 | 10 | public class HttpServletRequestUtil { 11 | 12 | /** 13 | * Returns the remote address of the request, removing bracket ("[…]") host notation from IPv6 addresses present in 14 | * some implementations, notably {@link org.eclipse.jetty.server.HttpChannel}. 15 | */ 16 | public static String getRemoteAddress(final HttpServletRequest request) { 17 | final String remoteAddr = request.getRemoteAddr(); 18 | 19 | if (remoteAddr.startsWith("[")) { 20 | return remoteAddr.substring(1, remoteAddr.length() - 1); 21 | } 22 | 23 | return remoteAddr; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/PaymentsServiceClientsFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.configuration; 7 | 8 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 9 | import io.dropwizard.jackson.Discoverable; 10 | import org.whispersystems.textsecuregcm.currency.CoinGeckoClient; 11 | import org.whispersystems.textsecuregcm.currency.FixerClient; 12 | import java.net.http.HttpClient; 13 | 14 | @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = PaymentsServiceClientsConfiguration.class) 15 | public interface PaymentsServiceClientsFactory extends Discoverable { 16 | 17 | FixerClient buildFixerClient(final HttpClient httpClient); 18 | 19 | CoinGeckoClient buildCoinGeckoClient(HttpClient httpClient); 20 | } 21 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/S3ObjectMonitorFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.configuration; 7 | 8 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 9 | import io.dropwizard.jackson.Discoverable; 10 | import org.whispersystems.textsecuregcm.s3.S3ObjectMonitor; 11 | import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; 12 | import java.util.concurrent.ScheduledExecutorService; 13 | 14 | @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = MonitoredS3ObjectConfiguration.class) 15 | public interface S3ObjectMonitorFactory extends Discoverable { 16 | 17 | S3ObjectMonitor build(AwsCredentialsProvider awsCredentialsProvider, 18 | ScheduledExecutorService refreshExecutorService); 19 | } 20 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/subscriptions/SubscriptionException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.textsecuregcm.subscriptions; 6 | 7 | import java.util.Optional; 8 | import javax.annotation.Nullable; 9 | 10 | public class SubscriptionException extends Exception { 11 | 12 | private @Nullable String errorDetail; 13 | 14 | public SubscriptionException(Exception cause) { 15 | this(cause, null); 16 | } 17 | 18 | SubscriptionException(Exception cause, String errorDetail) { 19 | super(cause); 20 | this.errorDetail = errorDetail; 21 | } 22 | 23 | /** 24 | * @return An error message suitable to include in a client response 25 | */ 26 | public Optional errorDetail() { 27 | return Optional.ofNullable(errorDetail); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/DirectoryV2Configuration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.textsecuregcm.configuration; 6 | 7 | import com.fasterxml.jackson.annotation.JsonCreator; 8 | import com.fasterxml.jackson.annotation.JsonProperty; 9 | import jakarta.validation.Valid; 10 | 11 | public class DirectoryV2Configuration { 12 | 13 | private final DirectoryV2ClientConfiguration clientConfiguration; 14 | 15 | @JsonCreator 16 | public DirectoryV2Configuration(@JsonProperty("client") DirectoryV2ClientConfiguration clientConfiguration) { 17 | this.clientConfiguration = clientConfiguration; 18 | } 19 | 20 | @Valid 21 | public DirectoryV2ClientConfiguration getDirectoryV2ClientConfiguration() { 22 | return clientConfiguration; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/websocket/NoContextTakeoverPerMessageDeflateExtension.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.websocket; 2 | 3 | import org.eclipse.jetty.websocket.core.ExtensionConfig; 4 | import org.eclipse.jetty.websocket.core.WebSocketComponents; 5 | import org.eclipse.jetty.websocket.core.internal.PerMessageDeflateExtension; 6 | 7 | /// A variant of the Jetty {@link PerMessageDeflateExtension} that always negotiates the [server_no_context_takeover 8 | /// extension parameter](https://datatracker.ietf.org/doc/html/rfc7692#section-7.1.1.1) 9 | public final class NoContextTakeoverPerMessageDeflateExtension extends PerMessageDeflateExtension { 10 | 11 | @Override 12 | public void init(ExtensionConfig config, WebSocketComponents components) { 13 | config.setParameter("server_no_context_takeover"); 14 | super.init(config, components); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | local.properties 3 | .idea 4 | *.iml 5 | run.sh 6 | *~ 7 | local.yml 8 | config/production.yml 9 | config/federated.yml 10 | config/staging.yml 11 | config/testing.yml 12 | config/deploy.properties 13 | /service/config/production.yml 14 | /service/config/federated.yml 15 | /service/config/staging.yml 16 | /service/config/testing.yml 17 | /service/config/deploy.properties 18 | /service/dependency-reduced-pom.xml 19 | .opsmanage 20 | put.sh 21 | deployer-staging.properties 22 | deployer-production.properties 23 | deployer.log 24 | /service/src/main/resources/org/signal/badges/Badges_*.properties 25 | !/service/src/main/resources/org/signal/badges/Badges_en.properties 26 | /service/src/main/resources/org/signal/subscriptions/Subscriptions_*.properties 27 | !/service/src/main/resources/org/signal/subscriptions/Subscriptions_en.properties 28 | .project 29 | .classpath 30 | .settings 31 | .DS_Store 32 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/entities/RateLimitChallenge.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.entities; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import jakarta.validation.constraints.NotNull; 6 | import java.util.List; 7 | 8 | public class RateLimitChallenge { 9 | 10 | @JsonProperty 11 | @NotNull 12 | private final String token; 13 | 14 | @JsonProperty 15 | @NotNull 16 | private final List options; 17 | 18 | @JsonCreator 19 | public RateLimitChallenge(@JsonProperty("token") final String token, @JsonProperty("options") final List options) { 20 | 21 | this.token = token; 22 | this.options = options; 23 | } 24 | 25 | public String getToken() { 26 | return token; 27 | } 28 | 29 | public List getOptions() { 30 | return options; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /service/src/test/java-templates/org/whispersystems/textsecuregcm/util/TestcontainersImages.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.util; 7 | 8 | public class TestcontainersImages { 9 | 10 | private static final String DYNAMO_DB = "${dynamodb.image}"; 11 | private static final String LOCAL_STACK = "${localstack.image}"; 12 | private static final String REDIS = "${redis.image}"; 13 | private static final String REDIS_CLUSTER = "${redis-cluster.image}"; 14 | 15 | public static String getDynamoDb() { 16 | return DYNAMO_DB; 17 | } 18 | 19 | public static String getLocalStack() { 20 | return LOCAL_STACK; 21 | } 22 | 23 | public static String getRedis() { 24 | return REDIS; 25 | } 26 | 27 | public static String getRedisCluster() { 28 | return REDIS_CLUSTER; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/entities/AccountStaleDevices.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.entities; 7 | 8 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 9 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 10 | import org.whispersystems.textsecuregcm.identity.ServiceIdentifier; 11 | import org.whispersystems.textsecuregcm.util.ServiceIdentifierAdapter; 12 | 13 | public record AccountStaleDevices(@JsonSerialize(using = ServiceIdentifierAdapter.ServiceIdentifierSerializer.class) 14 | @JsonDeserialize(using = ServiceIdentifierAdapter.ServiceIdentifierDeserializer.class) 15 | ServiceIdentifier uuid, 16 | 17 | StaleDevicesResponse devices) { 18 | } 19 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/entities/KeyTransparencyMonitorResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.entities; 7 | 8 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 9 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 10 | import io.swagger.v3.oas.annotations.media.Schema; 11 | import jakarta.validation.constraints.NotNull; 12 | import org.whispersystems.textsecuregcm.util.ByteArrayAdapter; 13 | 14 | public record KeyTransparencyMonitorResponse( 15 | @NotNull 16 | @JsonSerialize(using = ByteArrayAdapter.Serializing.class) 17 | @JsonDeserialize(using = ByteArrayAdapter.Deserializing.class) 18 | @Schema(description = "The serialized `MonitorResponse` encoded in standard un-padded base64") 19 | byte[] serializedResponse 20 | ) {} 21 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/entities/KeyTransparencySearchResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.entities; 7 | 8 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 9 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 10 | import io.swagger.v3.oas.annotations.media.Schema; 11 | import jakarta.validation.constraints.NotNull; 12 | import org.whispersystems.textsecuregcm.util.ByteArrayAdapter; 13 | 14 | public record KeyTransparencySearchResponse( 15 | @NotNull 16 | @JsonSerialize(using = ByteArrayAdapter.Serializing.class) 17 | @JsonDeserialize(using = ByteArrayAdapter.Deserializing.class) 18 | @Schema(description = "The serialized `SearchResponse` encoded in standard un-padded base64.") 19 | byte[] serializedResponse 20 | ) {} 21 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/entities/MismatchedDevicesResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.entities; 7 | 8 | import com.fasterxml.jackson.annotation.JsonProperty; 9 | import io.swagger.v3.oas.annotations.media.Schema; 10 | 11 | import java.util.Set; 12 | 13 | public record MismatchedDevicesResponse(@JsonProperty 14 | @Schema(description = "Devices present on the account but absent in the request") 15 | Set missingDevices, 16 | 17 | @JsonProperty 18 | @Schema(description = "Devices absent on the request but present in the account") 19 | Set extraDevices) { 20 | } 21 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/entities/SetPublicKeyRequest.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.entities; 2 | 3 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 4 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 5 | import io.swagger.v3.oas.annotations.media.Schema; 6 | import org.signal.libsignal.protocol.ecc.ECPublicKey; 7 | import org.whispersystems.textsecuregcm.util.ECPublicKeyAdapter; 8 | 9 | public record SetPublicKeyRequest( 10 | @JsonSerialize(using = ECPublicKeyAdapter.Serializer.class) 11 | @JsonDeserialize(using = ECPublicKeyAdapter.Deserializer.class) 12 | @Schema(type="string", description=""" 13 | The public key, serialized in libsignal's elliptic-curve public key format and then encoded as a standard (i.e. 14 | not URL-safe), padded, base64-encoded string. 15 | """) 16 | ECPublicKey publicKey) { 17 | } 18 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/entities/IncomingWebsocketMessage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.entities; 7 | 8 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 9 | import com.fasterxml.jackson.annotation.JsonProperty; 10 | 11 | @JsonIgnoreProperties(ignoreUnknown = true) 12 | public class IncomingWebsocketMessage { 13 | 14 | public static final int TYPE_ACKNOWLEDGE_MESSAGE = 1; 15 | public static final int TYPE_PING_MESSAGE = 2; 16 | public static final int TYPE_PONG_MESSAGE = 3; 17 | 18 | @JsonProperty 19 | protected int type; 20 | 21 | public IncomingWebsocketMessage() {} 22 | 23 | public IncomingWebsocketMessage(int type) { 24 | this.type = type; 25 | } 26 | 27 | public int getType() { 28 | return type; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/grpc/AvatarChangeUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.grpc; 7 | 8 | import io.grpc.Status; 9 | import org.whispersystems.textsecuregcm.entities.AvatarChange; 10 | 11 | public class AvatarChangeUtil { 12 | public static AvatarChange fromGrpcAvatarChange(final org.signal.chat.profile.SetProfileRequest.AvatarChange avatarChangeType) { 13 | return switch (avatarChangeType) { 14 | case AVATAR_CHANGE_UNCHANGED -> AvatarChange.AVATAR_CHANGE_UNCHANGED; 15 | case AVATAR_CHANGE_CLEAR -> AvatarChange.AVATAR_CHANGE_CLEAR; 16 | case AVATAR_CHANGE_UPDATE -> AvatarChange.AVATAR_CHANGE_UPDATE; 17 | case UNRECOGNIZED -> throw Status.INVALID_ARGUMENT.withDescription("Invalid avatar change value").asRuntimeException(); 18 | }; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /api-doc/src/main/resources/openapi/openapi-configuration.yaml: -------------------------------------------------------------------------------- 1 | resourcePackages: 2 | - org.whispersystems.textsecuregcm 3 | prettyPrint: true 4 | cacheTTL: 0 5 | readerClass: org.signal.openapi.OpenApiReader 6 | openAPI: 7 | info: 8 | title: Signal Server API 9 | license: 10 | name: AGPL-3.0-only 11 | url: https://www.gnu.org/licenses/agpl-3.0.txt 12 | servers: 13 | - url: https://chat.signal.org 14 | description: Production service 15 | - url: https://chat.staging.signal.org 16 | description: Staging service 17 | components: 18 | securitySchemes: 19 | authenticatedAccount: 20 | type: http 21 | scheme: basic 22 | description: | 23 | Account authentication is based on Basic authentication schema, 24 | where `username` has a format of `[.]`. If `device_id` is not specified, 25 | user's `main` device is assumed. 26 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/backup/MediaEncryptionParameters.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.backup; 2 | 3 | import javax.crypto.spec.SecretKeySpec; 4 | 5 | public record MediaEncryptionParameters( 6 | SecretKeySpec aesEncryptionKey, 7 | SecretKeySpec hmacSHA256Key) { 8 | 9 | public MediaEncryptionParameters(byte[] encryptionKey, byte[] macKey) { 10 | this( 11 | new SecretKeySpec(encryptionKey, "AES"), 12 | new SecretKeySpec(macKey, "HmacSHA256")); 13 | } 14 | 15 | public int outputSize(final int inputSize) { 16 | // AES-256 has 16-byte block size, and always adds a block if the plaintext is a multiple of the block size 17 | final int numBlocks = (inputSize + 16) / 16; 18 | // 16-byte IV will be generated and prepended to the ciphertext 19 | // IV + AES-256 encrypted data + HmacSHA256 20 | return 16 + (numBlocks * 16) + 32; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/KeyTransparencyServiceConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.configuration; 7 | 8 | import jakarta.validation.constraints.NotBlank; 9 | import jakarta.validation.constraints.NotNull; 10 | import jakarta.validation.constraints.Positive; 11 | import org.whispersystems.textsecuregcm.configuration.secrets.SecretString; 12 | 13 | public record KeyTransparencyServiceConfiguration(@NotBlank String host, 14 | @Positive int port, 15 | @NotBlank String tlsCertificate, 16 | @NotBlank String clientCertificate, 17 | @NotNull SecretString clientPrivateKey) {} 18 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/entities/ReserveUsernameHashResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.entities; 7 | 8 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 9 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 10 | import org.whispersystems.textsecuregcm.controllers.AccountController; 11 | import org.whispersystems.textsecuregcm.util.ByteArrayBase64UrlAdapter; 12 | import org.whispersystems.textsecuregcm.util.ExactlySize; 13 | import java.util.UUID; 14 | 15 | public record ReserveUsernameHashResponse( 16 | @JsonSerialize(using = ByteArrayBase64UrlAdapter.Serializing.class) 17 | @JsonDeserialize(using = ByteArrayBase64UrlAdapter.Deserializing.class) 18 | @ExactlySize(AccountController.USERNAME_HASH_LENGTH) 19 | byte[] usernameHash 20 | ) {} 21 | -------------------------------------------------------------------------------- /service/src/main/resources/org/whispersystems/textsecuregcm/storage/devicecheck/apple_device_check.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICITCCAaegAwIBAgIQC/O+DvHN0uD7jG5yH2IXmDAKBggqhkjOPQQDAzBSMSYw 3 | JAYDVQQDDB1BcHBsZSBBcHAgQXR0ZXN0YXRpb24gUm9vdCBDQTETMBEGA1UECgwK 4 | QXBwbGUgSW5jLjETMBEGA1UECAwKQ2FsaWZvcm5pYTAeFw0yMDAzMTgxODMyNTNa 5 | Fw00NTAzMTUwMDAwMDBaMFIxJjAkBgNVBAMMHUFwcGxlIEFwcCBBdHRlc3RhdGlv 6 | biBSb290IENBMRMwEQYDVQQKDApBcHBsZSBJbmMuMRMwEQYDVQQIDApDYWxpZm9y 7 | bmlhMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAERTHhmLW07ATaFQIEVwTtT4dyctdh 8 | NbJhFs/Ii2FdCgAHGbpphY3+d8qjuDngIN3WVhQUBHAoMeQ/cLiP1sOUtgjqK9au 9 | Yen1mMEvRq9Sk3Jm5X8U62H+xTD3FE9TgS41o0IwQDAPBgNVHRMBAf8EBTADAQH/ 10 | MB0GA1UdDgQWBBSskRBTM72+aEH/pwyp5frq5eWKoTAOBgNVHQ8BAf8EBAMCAQYw 11 | CgYIKoZIzj0EAwMDaAAwZQIwQgFGnByvsiVbpTKwSga0kP0e8EeDS4+sQmTvb7vn 12 | 53O5+FRXgeLhpJ06ysC5PrOyAjEAp5U4xDgEgllF7En3VcE3iexZZtKeYnpqtijV 13 | oyFraWVIyd/dganmrduC1bmTBGwD 14 | -----END CERTIFICATE----- 15 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/subscriptions/SubscriptionChargeFailurePaymentRequiredException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.textsecuregcm.subscriptions; 6 | 7 | public class SubscriptionChargeFailurePaymentRequiredException extends SubscriptionPaymentRequiredException { 8 | 9 | private final PaymentProvider processor; 10 | private final ChargeFailure chargeFailure; 11 | 12 | public SubscriptionChargeFailurePaymentRequiredException(final PaymentProvider processor, 13 | final ChargeFailure chargeFailure) { 14 | super(); 15 | this.processor = processor; 16 | this.chargeFailure = chargeFailure; 17 | } 18 | 19 | public PaymentProvider getProcessor() { 20 | return processor; 21 | } 22 | 23 | public ChargeFailure getChargeFailure() { 24 | return chargeFailure; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/entities/KeyTransparencyDistinguishedKeyResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.entities; 7 | 8 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 9 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 10 | import io.swagger.v3.oas.annotations.media.Schema; 11 | import jakarta.validation.constraints.NotNull; 12 | import org.whispersystems.textsecuregcm.util.ByteArrayAdapter; 13 | 14 | public record KeyTransparencyDistinguishedKeyResponse( 15 | @NotNull 16 | @JsonSerialize(using = ByteArrayAdapter.Serializing.class) 17 | @JsonDeserialize(using = ByteArrayAdapter.Deserializing.class) 18 | @Schema(description = "The serialized `DistinguishedResponse` encoded in standard un-padded base64") 19 | byte[] serializedResponse 20 | ) {} 21 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/entities/AccountMismatchedDevices.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.entities; 7 | 8 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 9 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 10 | import org.whispersystems.textsecuregcm.identity.ServiceIdentifier; 11 | import org.whispersystems.textsecuregcm.util.ServiceIdentifierAdapter; 12 | 13 | public record AccountMismatchedDevices(@JsonSerialize(using = ServiceIdentifierAdapter.ServiceIdentifierSerializer.class) 14 | @JsonDeserialize(using = ServiceIdentifierAdapter.ServiceIdentifierDeserializer.class) 15 | ServiceIdentifier uuid, 16 | 17 | MismatchedDevicesResponse devices) { 18 | } 19 | --------------------------------------------------------------------------------