├── .editorconfig ├── .github ├── FUNDING.yml ├── stale.yml └── workflows │ ├── documentation.yml │ ├── integration-tests.yml │ └── test.yml ├── .gitignore ├── .gitmodules ├── .mvn ├── extensions.xml ├── jgitver.config.xml └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── LICENSE ├── README.md ├── TESTING.md ├── api-doc ├── pom.xml └── src │ └── main │ ├── java │ └── org │ │ └── signal │ │ └── openapi │ │ ├── OpenApiExtension.java │ │ └── OpenApiReader.java │ └── resources │ ├── META-INF │ └── services │ │ └── io.swagger.v3.jaxrs2.ext.OpenAPIExtension │ └── openapi │ └── openapi-configuration.yaml ├── integration-tests ├── .gitignore ├── pom.xml └── src │ ├── main │ └── java │ │ └── org │ │ └── signal │ │ └── integration │ │ ├── Codecs.java │ │ ├── IntegrationTools.java │ │ ├── Operations.java │ │ ├── TestDevice.java │ │ ├── TestUser.java │ │ └── config │ │ ├── Config.java │ │ └── DynamoDbTables.java │ └── test │ └── java │ └── org │ └── signal │ └── integration │ ├── AccountTest.java │ ├── MessagingTest.java │ └── RegistrationTest.java ├── mvnw ├── mvnw.cmd ├── pom.xml ├── service ├── assembly.xml ├── config │ ├── sample-secrets-bundle.yml │ └── sample.yml ├── pom.xml └── src │ ├── main │ ├── graphql │ │ └── braintree │ │ │ ├── ChargePayPalOneTimePayment.graphql │ │ │ ├── CreatePayPalBillingAgreement.graphql │ │ │ ├── CreatePayPalOneTimePayment.graphql │ │ │ ├── TokenizePayPalBillingAgreement.graphql │ │ │ ├── TokenizePayPalOneTimePayment.graphql │ │ │ ├── VaultPaymentMethod.graphql │ │ │ └── schema.json │ ├── java-templates │ │ └── org │ │ │ └── whispersystems │ │ │ └── textsecuregcm │ │ │ └── WhisperServerVersion.java │ ├── java │ │ └── org │ │ │ ├── signal │ │ │ └── i18n │ │ │ │ ├── HeaderControlledResourceBundleLookup.java │ │ │ │ └── ResourceBundleFactory.java │ │ │ └── whispersystems │ │ │ └── textsecuregcm │ │ │ ├── WhisperServerConfiguration.java │ │ │ ├── WhisperServerService.java │ │ │ ├── attachments │ │ │ ├── AttachmentGenerator.java │ │ │ ├── GcsAttachmentGenerator.java │ │ │ ├── TusAttachmentGenerator.java │ │ │ └── TusConfiguration.java │ │ │ ├── auth │ │ │ ├── AccountAndAuthenticatedDeviceHolder.java │ │ │ ├── AccountAuthenticator.java │ │ │ ├── Anonymous.java │ │ │ ├── AuthenticatedBackupUser.java │ │ │ ├── AuthenticatedDevice.java │ │ │ ├── BasicAuthorizationHeader.java │ │ │ ├── CertificateGenerator.java │ │ │ ├── ChangesLinkedDevices.java │ │ │ ├── ChangesPhoneNumber.java │ │ │ ├── CloudflareTurnCredentialsManager.java │ │ │ ├── CombinedUnidentifiedSenderAccessKeys.java │ │ │ ├── ContainerRequestUtil.java │ │ │ ├── DisconnectionRequestListener.java │ │ │ ├── DisconnectionRequestManager.java │ │ │ ├── ExternalServiceCredentials.java │ │ │ ├── ExternalServiceCredentialsGenerator.java │ │ │ ├── ExternalServiceCredentialsSelector.java │ │ │ ├── GroupSendTokenHeader.java │ │ │ ├── IdlePrimaryDeviceAuthenticatedWebSocketUpgradeFilter.java │ │ │ ├── InvalidAuthorizationHeaderException.java │ │ │ ├── LinkedDeviceRefreshRequirementProvider.java │ │ │ ├── OptionalAccess.java │ │ │ ├── PhoneNumberChangeRefreshRequirementProvider.java │ │ │ ├── PhoneVerificationTokenManager.java │ │ │ ├── RegistrationLockVerificationManager.java │ │ │ ├── SaltedTokenHash.java │ │ │ ├── StoredRegistrationLock.java │ │ │ ├── TurnToken.java │ │ │ ├── UnidentifiedAccessChecksum.java │ │ │ ├── UnidentifiedAccessUtil.java │ │ │ ├── WebsocketRefreshApplicationEventListener.java │ │ │ ├── WebsocketRefreshRequestEventListener.java │ │ │ ├── WebsocketRefreshRequirementProvider.java │ │ │ └── grpc │ │ │ │ ├── AbstractAuthenticationInterceptor.java │ │ │ │ ├── AuthenticatedDevice.java │ │ │ │ ├── AuthenticationUtil.java │ │ │ │ ├── ProhibitAuthenticationInterceptor.java │ │ │ │ └── RequireAuthenticationInterceptor.java │ │ │ ├── backup │ │ │ ├── BackupAuthManager.java │ │ │ ├── BackupLevelUtil.java │ │ │ ├── BackupManager.java │ │ │ ├── BackupUploadDescriptor.java │ │ │ ├── BackupsDb.java │ │ │ ├── Cdn3BackupCredentialGenerator.java │ │ │ ├── Cdn3RemoteStorageManager.java │ │ │ ├── CopyParameters.java │ │ │ ├── CopyResult.java │ │ │ ├── ExpiredBackup.java │ │ │ ├── InvalidLengthException.java │ │ │ ├── MediaEncryptionParameters.java │ │ │ ├── PublicKeyConflictException.java │ │ │ ├── RemoteStorageManager.java │ │ │ ├── SourceObjectNotFoundException.java │ │ │ ├── StoredBackupAttributes.java │ │ │ └── UsageInfo.java │ │ │ ├── badges │ │ │ ├── BadgeTranslator.java │ │ │ ├── ConfiguredProfileBadgeConverter.java │ │ │ ├── LevelTranslator.java │ │ │ └── ProfileBadgeConverter.java │ │ │ ├── calls │ │ │ └── routing │ │ │ │ ├── CallDnsRecords.java │ │ │ │ ├── CallDnsRecordsManager.java │ │ │ │ ├── CallRoutingTable.java │ │ │ │ ├── CallRoutingTableManager.java │ │ │ │ ├── CallRoutingTableParser.java │ │ │ │ ├── CidrBlock.java │ │ │ │ └── TurnServerOptions.java │ │ │ ├── captcha │ │ │ ├── Action.java │ │ │ ├── AssessmentResult.java │ │ │ ├── CaptchaChecker.java │ │ │ ├── CaptchaClient.java │ │ │ ├── RegistrationCaptchaManager.java │ │ │ └── ShortCodeExpander.java │ │ │ ├── configuration │ │ │ ├── AccountsTableConfiguration.java │ │ │ ├── ApnConfiguration.java │ │ │ ├── AppleAppStoreConfiguration.java │ │ │ ├── AppleDeviceCheckConfiguration.java │ │ │ ├── AwsCredentialsProviderFactory.java │ │ │ ├── BadgeConfiguration.java │ │ │ ├── BadgesConfiguration.java │ │ │ ├── BraintreeConfiguration.java │ │ │ ├── Cdn3StorageManagerConfiguration.java │ │ │ ├── CdnConfiguration.java │ │ │ ├── CircuitBreakerConfiguration.java │ │ │ ├── ClientCdnConfiguration.java │ │ │ ├── ClientReleaseConfiguration.java │ │ │ ├── CloudflareTurnConfiguration.java │ │ │ ├── DatadogConfiguration.java │ │ │ ├── DefaultAwsCredentialsFactory.java │ │ │ ├── DefaultPubSubPublisherFactory.java │ │ │ ├── DeviceCheckConfiguration.java │ │ │ ├── DirectoryV2ClientConfiguration.java │ │ │ ├── DirectoryV2Configuration.java │ │ │ ├── DogstatsdConfiguration.java │ │ │ ├── DynamoDbClientConfiguration.java │ │ │ ├── DynamoDbClientFactory.java │ │ │ ├── DynamoDbTables.java │ │ │ ├── ExternalRequestFilterConfiguration.java │ │ │ ├── FaultTolerantRedisClientFactory.java │ │ │ ├── FaultTolerantRedisClusterFactory.java │ │ │ ├── FcmConfiguration.java │ │ │ ├── GcpAttachmentsConfiguration.java │ │ │ ├── GenericZkConfig.java │ │ │ ├── GooglePlayBillingConfiguration.java │ │ │ ├── IdlePrimaryDeviceReminderConfiguration.java │ │ │ ├── IssuedReceiptsTableConfiguration.java │ │ │ ├── KeyTransparencyServiceConfiguration.java │ │ │ ├── LinkDeviceSecretConfiguration.java │ │ │ ├── MaxDeviceConfiguration.java │ │ │ ├── MessageByteLimitCardinalityEstimatorConfiguration.java │ │ │ ├── MessageCacheConfiguration.java │ │ │ ├── MonitoredS3ObjectConfiguration.java │ │ │ ├── NoiseWebSocketTunnelConfiguration.java │ │ │ ├── OneTimeDonationConfiguration.java │ │ │ ├── OneTimeDonationCurrencyConfiguration.java │ │ │ ├── PaymentsServiceClientsConfiguration.java │ │ │ ├── PaymentsServiceClientsFactory.java │ │ │ ├── PaymentsServiceConfiguration.java │ │ │ ├── PubSubPublisherFactory.java │ │ │ ├── RedisClusterConfiguration.java │ │ │ ├── RedisConfiguration.java │ │ │ ├── RegistrationServiceClientFactory.java │ │ │ ├── RegistrationServiceConfiguration.java │ │ │ ├── RemoteConfigConfiguration.java │ │ │ ├── ReportMessageConfiguration.java │ │ │ ├── RetryConfiguration.java │ │ │ ├── S3ObjectMonitorFactory.java │ │ │ ├── SecureStorageServiceConfiguration.java │ │ │ ├── SecureValueRecovery2Configuration.java │ │ │ ├── ShortCodeExpanderConfiguration.java │ │ │ ├── SpamFilterConfiguration.java │ │ │ ├── StaticAwsCredentialsFactory.java │ │ │ ├── StripeConfiguration.java │ │ │ ├── SubscriptionConfiguration.java │ │ │ ├── SubscriptionLevelConfiguration.java │ │ │ ├── SubscriptionPriceConfiguration.java │ │ │ ├── TlsKeyStoreConfiguration.java │ │ │ ├── TurnConfiguration.java │ │ │ ├── TurnUriConfiguration.java │ │ │ ├── URLSerializationConverter.java │ │ │ ├── UnidentifiedDeliveryConfiguration.java │ │ │ ├── VirtualThreadConfiguration.java │ │ │ ├── ZkConfig.java │ │ │ ├── dynamic │ │ │ │ ├── DynamicCaptchaConfiguration.java │ │ │ │ ├── DynamicConfiguration.java │ │ │ │ ├── DynamicE164ExperimentEnrollmentConfiguration.java │ │ │ │ ├── DynamicExperimentEnrollmentConfiguration.java │ │ │ │ ├── DynamicMessagePersisterConfiguration.java │ │ │ │ ├── DynamicMetricsConfiguration.java │ │ │ │ ├── DynamicPaymentsConfiguration.java │ │ │ │ ├── DynamicRegistrationConfiguration.java │ │ │ │ ├── DynamicRemoteDeprecationConfiguration.java │ │ │ │ ├── DynamicRestDeprecationConfiguration.java │ │ │ │ └── DynamicVirtualThreadConfiguration.java │ │ │ └── secrets │ │ │ │ ├── BaseSecretValidator.java │ │ │ │ ├── Secret.java │ │ │ │ ├── SecretBytes.java │ │ │ │ ├── SecretBytesList.java │ │ │ │ ├── SecretStore.java │ │ │ │ ├── SecretString.java │ │ │ │ ├── SecretStringList.java │ │ │ │ └── SecretsModule.java │ │ │ ├── controllers │ │ │ ├── AccountController.java │ │ │ ├── AccountControllerV2.java │ │ │ ├── AccountIdentityResponseBuilder.java │ │ │ ├── ArchiveController.java │ │ │ ├── AttachmentControllerV4.java │ │ │ ├── CallLinkController.java │ │ │ ├── CallRoutingControllerV2.java │ │ │ ├── CertificateController.java │ │ │ ├── ChallengeController.java │ │ │ ├── DeviceCheckController.java │ │ │ ├── DeviceController.java │ │ │ ├── DeviceLimitExceededException.java │ │ │ ├── DirectoryV2Controller.java │ │ │ ├── DonationController.java │ │ │ ├── GetCallingRelaysResponse.java │ │ │ ├── KeepAliveController.java │ │ │ ├── KeyTransparencyController.java │ │ │ ├── KeysController.java │ │ │ ├── MessageController.java │ │ │ ├── MismatchedDevices.java │ │ │ ├── MismatchedDevicesException.java │ │ │ ├── MultiRecipientMismatchedDevicesException.java │ │ │ ├── OneTimeDonationController.java │ │ │ ├── PaymentsController.java │ │ │ ├── ProfileController.java │ │ │ ├── ProvisioningController.java │ │ │ ├── RateLimitExceededException.java │ │ │ ├── RegistrationController.java │ │ │ ├── RemoteConfigController.java │ │ │ ├── SecureStorageController.java │ │ │ ├── SecureValueRecovery2Controller.java │ │ │ ├── ServerRejectedException.java │ │ │ ├── StickerController.java │ │ │ ├── SubscriptionController.java │ │ │ ├── VerificationController.java │ │ │ └── VerificationSessionRateLimitExceededException.java │ │ │ ├── currency │ │ │ ├── CoinGeckoClient.java │ │ │ ├── CurrencyConversionManager.java │ │ │ └── FixerClient.java │ │ │ ├── entities │ │ │ ├── AccountAttributes.java │ │ │ ├── AccountCreationResponse.java │ │ │ ├── AccountDataReportResponse.java │ │ │ ├── AccountIdentifierResponse.java │ │ │ ├── AccountIdentityResponse.java │ │ │ ├── AccountMismatchedDevices.java │ │ │ ├── AccountStaleDevices.java │ │ │ ├── AnswerCaptchaChallengeRequest.java │ │ │ ├── AnswerChallengeRequest.java │ │ │ ├── AnswerPushChallengeRequest.java │ │ │ ├── ApnRegistrationId.java │ │ │ ├── AttachmentDescriptorV3.java │ │ │ ├── AuthCheckRequest.java │ │ │ ├── AuthCheckResponseV2.java │ │ │ ├── AuthCheckResponseV3.java │ │ │ ├── AvatarChange.java │ │ │ ├── Badge.java │ │ │ ├── BadgeSvg.java │ │ │ ├── BaseProfileResponse.java │ │ │ ├── BatchIdentityCheckRequest.java │ │ │ ├── BatchIdentityCheckResponse.java │ │ │ ├── ChangeNumberRequest.java │ │ │ ├── CheckKeysRequest.java │ │ │ ├── ConfirmUsernameHashRequest.java │ │ │ ├── CreateCallLinkCredential.java │ │ │ ├── CreateProfileRequest.java │ │ │ ├── CreateVerificationSessionRequest.java │ │ │ ├── CredentialProfileResponse.java │ │ │ ├── CurrencyConversionEntity.java │ │ │ ├── CurrencyConversionEntityList.java │ │ │ ├── DeliveryCertificate.java │ │ │ ├── DeviceActivationRequest.java │ │ │ ├── DeviceInfo.java │ │ │ ├── DeviceInfoList.java │ │ │ ├── DeviceName.java │ │ │ ├── ECPreKey.java │ │ │ ├── ECSignedPreKey.java │ │ │ ├── EncryptedUsername.java │ │ │ ├── Entitlements.java │ │ │ ├── ExpiringProfileKeyCredentialProfileResponse.java │ │ │ ├── ExpiringProfileKeyCredentialResponseAdapter.java │ │ │ ├── GcmRegistrationId.java │ │ │ ├── GetCreateCallLinkCredentialsRequest.java │ │ │ ├── GroupCredentials.java │ │ │ ├── IncomingMessage.java │ │ │ ├── IncomingMessageList.java │ │ │ ├── IncomingWebsocketMessage.java │ │ │ ├── KEMSignedPreKey.java │ │ │ ├── KeyTransparencyDistinguishedKeyResponse.java │ │ │ ├── KeyTransparencyMonitorRequest.java │ │ │ ├── KeyTransparencyMonitorResponse.java │ │ │ ├── KeyTransparencySearchRequest.java │ │ │ ├── KeyTransparencySearchResponse.java │ │ │ ├── LinkDeviceRequest.java │ │ │ ├── LinkDeviceResponse.java │ │ │ ├── MismatchedDevicesResponse.java │ │ │ ├── OutgoingMessageEntity.java │ │ │ ├── OutgoingMessageEntityList.java │ │ │ ├── PhoneNumberDiscoverabilityRequest.java │ │ │ ├── PhoneNumberIdentityKeyDistributionRequest.java │ │ │ ├── PhoneVerificationRequest.java │ │ │ ├── PreKey.java │ │ │ ├── PreKeyCount.java │ │ │ ├── PreKeyResponse.java │ │ │ ├── PreKeyResponseItem.java │ │ │ ├── PreKeySignatureValidator.java │ │ │ ├── ProfileAvatarUploadAttributes.java │ │ │ ├── ProfileKeyCommitmentAdapter.java │ │ │ ├── ProvisioningMessage.java │ │ │ ├── PurchasableBadge.java │ │ │ ├── RateLimitChallenge.java │ │ │ ├── RedeemReceiptRequest.java │ │ │ ├── RegistrationLock.java │ │ │ ├── RegistrationLockFailure.java │ │ │ ├── RegistrationRequest.java │ │ │ ├── RegistrationServiceSession.java │ │ │ ├── RemoteAttachment.java │ │ │ ├── RemoteAttachmentError.java │ │ │ ├── ReserveUsernameHashRequest.java │ │ │ ├── ReserveUsernameHashResponse.java │ │ │ ├── RestoreAccountRequest.java │ │ │ ├── SelfBadge.java │ │ │ ├── SendMessageResponse.java │ │ │ ├── SendMultiRecipientMessageResponse.java │ │ │ ├── SetKeysRequest.java │ │ │ ├── SetPublicKeyRequest.java │ │ │ ├── SignedPreKey.java │ │ │ ├── SpamReport.java │ │ │ ├── StaleDevicesResponse.java │ │ │ ├── StickerPackFormUploadAttributes.java │ │ │ ├── SubmitVerificationCodeRequest.java │ │ │ ├── TransferArchiveResult.java │ │ │ ├── TransferArchiveUploadedRequest.java │ │ │ ├── UpdateVerificationSessionRequest.java │ │ │ ├── UserRemoteConfig.java │ │ │ ├── UserRemoteConfigList.java │ │ │ ├── UsernameHashResponse.java │ │ │ ├── UsernameLinkHandle.java │ │ │ ├── VerificationCodeRequest.java │ │ │ ├── VerificationSessionResponse.java │ │ │ └── VersionedProfileResponse.java │ │ │ ├── experiment │ │ │ ├── DeviceLastSeenState.java │ │ │ ├── Experiment.java │ │ │ ├── ExperimentEnrollmentManager.java │ │ │ ├── IdleDevicePushNotificationExperiment.java │ │ │ ├── PushNotificationExperiment.java │ │ │ ├── PushNotificationExperimentSample.java │ │ │ └── PushNotificationExperimentSamples.java │ │ │ ├── filters │ │ │ ├── ExternalRequestFilter.java │ │ │ ├── RemoteAddressFilter.java │ │ │ ├── RemoteDeprecationFilter.java │ │ │ ├── RequestStatisticsFilter.java │ │ │ ├── RestDeprecationFilter.java │ │ │ └── TimestampResponseFilter.java │ │ │ ├── gcp │ │ │ ├── CanonicalRequest.java │ │ │ ├── CanonicalRequestGenerator.java │ │ │ └── CanonicalRequestSigner.java │ │ │ ├── grpc │ │ │ ├── AccountsAnonymousGrpcService.java │ │ │ ├── AccountsGrpcService.java │ │ │ ├── AvatarChangeUtil.java │ │ │ ├── BackupsAnonymousGrpcService.java │ │ │ ├── BackupsGrpcService.java │ │ │ ├── ChannelNotFoundException.java │ │ │ ├── ChannelShutdownInterceptor.java │ │ │ ├── ConvertibleToGrpcStatus.java │ │ │ ├── DeviceCapabilityUtil.java │ │ │ ├── DeviceIdUtil.java │ │ │ ├── DevicesGrpcService.java │ │ │ ├── ErrorMappingInterceptor.java │ │ │ ├── ExternalServiceCredentialsAnonymousGrpcService.java │ │ │ ├── ExternalServiceCredentialsGrpcService.java │ │ │ ├── ExternalServiceDefinitions.java │ │ │ ├── GroupSendTokenUtil.java │ │ │ ├── IdentityTypeUtil.java │ │ │ ├── KeysAnonymousGrpcService.java │ │ │ ├── KeysGrpcHelper.java │ │ │ ├── KeysGrpcService.java │ │ │ ├── MessagesAnonymousGrpcService.java │ │ │ ├── MessagesGrpcHelper.java │ │ │ ├── MessagesGrpcService.java │ │ │ ├── PaymentsGrpcService.java │ │ │ ├── ProfileAnonymousGrpcService.java │ │ │ ├── ProfileGrpcHelper.java │ │ │ ├── ProfileGrpcService.java │ │ │ ├── RateLimitUtil.java │ │ │ ├── RequestAttributes.java │ │ │ ├── RequestAttributesInterceptor.java │ │ │ ├── RequestAttributesUtil.java │ │ │ ├── ServerInterceptorUtil.java │ │ │ ├── ServiceIdentifierUtil.java │ │ │ ├── StatusConstants.java │ │ │ ├── ValidatingInterceptor.java │ │ │ ├── net │ │ │ │ ├── ErrorHandler.java │ │ │ │ ├── EstablishLocalGrpcConnectionHandler.java │ │ │ │ ├── GrpcClientConnectionManager.java │ │ │ │ ├── HAProxyMessageHandler.java │ │ │ │ ├── HandshakePattern.java │ │ │ │ ├── ManagedDefaultEventLoopGroup.java │ │ │ │ ├── ManagedLocalGrpcServer.java │ │ │ │ ├── ManagedNioEventLoopGroup.java │ │ │ │ ├── NoiseException.java │ │ │ │ ├── NoiseHandler.java │ │ │ │ ├── NoiseHandshakeException.java │ │ │ │ ├── NoiseHandshakeHandler.java │ │ │ │ ├── NoiseHandshakeHelper.java │ │ │ │ ├── NoiseHandshakeInit.java │ │ │ │ ├── NoiseIdentityDeterminedEvent.java │ │ │ │ ├── OutboundCloseErrorMessage.java │ │ │ │ ├── ProxyHandler.java │ │ │ │ ├── ProxyProtocolDetectionHandler.java │ │ │ │ ├── noisedirect │ │ │ │ │ ├── NoiseDirectDataFrameCodec.java │ │ │ │ │ ├── NoiseDirectFrame.java │ │ │ │ │ ├── NoiseDirectFrameCodec.java │ │ │ │ │ ├── NoiseDirectHandshakeSelector.java │ │ │ │ │ ├── NoiseDirectInboundCloseHandler.java │ │ │ │ │ ├── NoiseDirectOutboundErrorHandler.java │ │ │ │ │ └── NoiseDirectTunnelServer.java │ │ │ │ └── websocket │ │ │ │ │ ├── ApplicationWebSocketCloseReason.java │ │ │ │ │ ├── NoiseWebSocketTunnelServer.java │ │ │ │ │ ├── RejectUnsupportedMessagesHandler.java │ │ │ │ │ ├── WebSocketOpeningHandshakeHandler.java │ │ │ │ │ ├── WebSocketOutboundErrorHandler.java │ │ │ │ │ ├── WebsocketHandshakeCompleteHandler.java │ │ │ │ │ └── WebsocketPayloadCodec.java │ │ │ └── validators │ │ │ │ ├── BaseFieldValidator.java │ │ │ │ ├── E164FieldValidator.java │ │ │ │ ├── EnumSpecifiedFieldValidator.java │ │ │ │ ├── ExactlySizeFieldValidator.java │ │ │ │ ├── FieldValidator.java │ │ │ │ ├── NonEmptyFieldValidator.java │ │ │ │ ├── PresentFieldValidator.java │ │ │ │ ├── Range.java │ │ │ │ ├── RangeFieldValidator.java │ │ │ │ ├── SizeFieldValidator.java │ │ │ │ └── ValidatorUtils.java │ │ │ ├── http │ │ │ └── FaultTolerantHttpClient.java │ │ │ ├── identity │ │ │ ├── AciServiceIdentifier.java │ │ │ ├── IdentityType.java │ │ │ ├── PniServiceIdentifier.java │ │ │ └── ServiceIdentifier.java │ │ │ ├── jetty │ │ │ └── JettyHttpConfigurationCustomizer.java │ │ │ ├── keytransparency │ │ │ └── KeyTransparencyServiceClient.java │ │ │ ├── limits │ │ │ ├── BaseRateLimiters.java │ │ │ ├── CardinalityEstimator.java │ │ │ ├── DynamicRateLimiter.java │ │ │ ├── MessageDeliveryLoopMonitor.java │ │ │ ├── NoopMessageDeliveryLoopMonitor.java │ │ │ ├── PushChallengeManager.java │ │ │ ├── RateLimitByIpFilter.java │ │ │ ├── RateLimitChallengeManager.java │ │ │ ├── RateLimitChallengeOption.java │ │ │ ├── RateLimitChallengeOptionManager.java │ │ │ ├── RateLimitedByIp.java │ │ │ ├── RateLimiter.java │ │ │ ├── RateLimiterConfig.java │ │ │ ├── RateLimiterDescriptor.java │ │ │ ├── RateLimiters.java │ │ │ └── RedisMessageDeliveryLoopMonitor.java │ │ │ ├── mappers │ │ │ ├── CompletionExceptionMapper.java │ │ │ ├── DeviceLimitExceededExceptionMapper.java │ │ │ ├── GrpcStatusRuntimeExceptionMapper.java │ │ │ ├── IOExceptionMapper.java │ │ │ ├── ImpossiblePhoneNumberExceptionMapper.java │ │ │ ├── InvalidWebsocketAddressExceptionMapper.java │ │ │ ├── JsonMappingExceptionMapper.java │ │ │ ├── NonNormalizedPhoneNumberExceptionMapper.java │ │ │ ├── NonNormalizedPhoneNumberResponse.java │ │ │ ├── ObsoletePhoneNumberFormatExceptionMapper.java │ │ │ ├── RateLimitExceededExceptionMapper.java │ │ │ ├── RegistrationServiceSenderExceptionMapper.java │ │ │ ├── ServerRejectedExceptionMapper.java │ │ │ └── SubscriptionExceptionMapper.java │ │ │ ├── metrics │ │ │ ├── ApplicationShutdownMonitor.java │ │ │ ├── BackupMetrics.java │ │ │ ├── DevicePlatformUtil.java │ │ │ ├── GarbageCollectionGauges.java │ │ │ ├── LogstashTcpSocketAppenderFactory.java │ │ │ ├── MessageMetrics.java │ │ │ ├── MetricsApplicationEventListener.java │ │ │ ├── MetricsHttpChannelListener.java │ │ │ ├── MetricsRequestEventListener.java │ │ │ ├── MetricsUtil.java │ │ │ ├── MicrometerAwsSdkMetricPublisher.java │ │ │ ├── MicrometerRegistryManager.java │ │ │ ├── NoopAwsSdkMetricPublisher.java │ │ │ ├── OpenWebSocketCounter.java │ │ │ ├── ReportedMessageMetricsListener.java │ │ │ ├── SignalDatadogReporterFactory.java │ │ │ ├── TlsCertificateExpirationUtil.java │ │ │ ├── TrafficSource.java │ │ │ └── UserAgentTagUtil.java │ │ │ ├── providers │ │ │ ├── MultiRecipientMessageProvider.java │ │ │ └── RedisClusterHealthCheck.java │ │ │ ├── push │ │ │ ├── APNSender.java │ │ │ ├── FcmSender.java │ │ │ ├── IdleDeviceNotificationScheduler.java │ │ │ ├── MessageSender.java │ │ │ ├── MessageTooLargeException.java │ │ │ ├── MessageUtil.java │ │ │ ├── NotPushRegisteredException.java │ │ │ ├── ProvisioningManager.java │ │ │ ├── PushNotification.java │ │ │ ├── PushNotificationManager.java │ │ │ ├── PushNotificationScheduler.java │ │ │ ├── PushNotificationSender.java │ │ │ ├── ReceiptSender.java │ │ │ ├── SendPushNotificationResult.java │ │ │ ├── WebSocketConnectionEventListener.java │ │ │ └── WebSocketConnectionEventManager.java │ │ │ ├── redis │ │ │ ├── AbstractFaultTolerantPubSubConnection.java │ │ │ ├── ClusterLuaScript.java │ │ │ ├── ConnectionEventLogger.java │ │ │ ├── FaultTolerantPubSubClusterConnection.java │ │ │ ├── FaultTolerantPubSubConnection.java │ │ │ ├── FaultTolerantRedisClient.java │ │ │ ├── FaultTolerantRedisClusterClient.java │ │ │ ├── LettuceShardCircuitBreaker.java │ │ │ ├── RedisOperation.java │ │ │ └── RedisUriUtil.java │ │ │ ├── registration │ │ │ ├── ClientType.java │ │ │ ├── IdentityTokenCallCredentials.java │ │ │ ├── MessageTransport.java │ │ │ ├── RegistrationFraudException.java │ │ │ ├── RegistrationServiceClient.java │ │ │ ├── RegistrationServiceException.java │ │ │ ├── RegistrationServiceSenderException.java │ │ │ ├── TransportNotAllowedException.java │ │ │ └── VerificationSession.java │ │ │ ├── s3 │ │ │ ├── PolicySigner.java │ │ │ ├── PostPolicyGenerator.java │ │ │ └── S3ObjectMonitor.java │ │ │ ├── scheduler │ │ │ ├── JobScheduler.java │ │ │ └── SchedulingUtil.java │ │ │ ├── securestorage │ │ │ ├── SecureStorageClient.java │ │ │ └── SecureStorageException.java │ │ │ ├── securevaluerecovery │ │ │ ├── SecureValueRecovery2Client.java │ │ │ └── SecureValueRecoveryException.java │ │ │ ├── spam │ │ │ ├── ChallengeConstraintChecker.java │ │ │ ├── ChallengeType.java │ │ │ ├── GrpcResponse.java │ │ │ ├── MessageType.java │ │ │ ├── RateLimitChallengeListener.java │ │ │ ├── RegistrationFraudChecker.java │ │ │ ├── RegistrationRecoveryChecker.java │ │ │ ├── SpamCheckResult.java │ │ │ ├── SpamChecker.java │ │ │ └── SpamFilter.java │ │ │ ├── storage │ │ │ ├── AbstractDynamoDbStore.java │ │ │ ├── Account.java │ │ │ ├── AccountAlreadyExistsException.java │ │ │ ├── AccountBadge.java │ │ │ ├── AccountChangeValidator.java │ │ │ ├── AccountLockManager.java │ │ │ ├── AccountPrincipalSupplier.java │ │ │ ├── AccountUtil.java │ │ │ ├── Accounts.java │ │ │ ├── AccountsManager.java │ │ │ ├── ChangeNumberManager.java │ │ │ ├── ChunkProcessingFailedException.java │ │ │ ├── ClientPublicKeys.java │ │ │ ├── ClientPublicKeysManager.java │ │ │ ├── ClientRelease.java │ │ │ ├── ClientReleaseManager.java │ │ │ ├── ClientReleases.java │ │ │ ├── ContestedOptimisticLockException.java │ │ │ ├── Device.java │ │ │ ├── DeviceCapability.java │ │ │ ├── DeviceIdDeserializer.java │ │ │ ├── DeviceSpec.java │ │ │ ├── DynamicConfigurationManager.java │ │ │ ├── IssuedReceiptsManager.java │ │ │ ├── KeysManager.java │ │ │ ├── LinkDeviceTokenAlreadyUsedException.java │ │ │ ├── MessagePersistenceException.java │ │ │ ├── MessagePersister.java │ │ │ ├── MessagesCache.java │ │ │ ├── MessagesCacheGetItemsScript.java │ │ │ ├── MessagesCacheGetQueuesToPersistScript.java │ │ │ ├── MessagesCacheInsertScript.java │ │ │ ├── MessagesCacheInsertSharedMultiRecipientPayloadAndViewsScript.java │ │ │ ├── MessagesCacheRemoveByGuidScript.java │ │ │ ├── MessagesCacheRemoveQueueScript.java │ │ │ ├── MessagesCacheRemoveRecipientViewFromMrmDataScript.java │ │ │ ├── MessagesCacheUnlockQueueScript.java │ │ │ ├── MessagesDynamoDb.java │ │ │ ├── MessagesManager.java │ │ │ ├── MrmDataMissingException.java │ │ │ ├── OneTimeDonationsManager.java │ │ │ ├── OptimisticLockRetryLimitExceededException.java │ │ │ ├── PaymentTime.java │ │ │ ├── PersistentTimer.java │ │ │ ├── PhoneNumberIdentifiers.java │ │ │ ├── Profiles.java │ │ │ ├── ProfilesManager.java │ │ │ ├── PushChallengeDynamoDb.java │ │ │ ├── RedeemedReceiptsManager.java │ │ │ ├── RefreshingAccountNotFoundException.java │ │ │ ├── RegistrationRecoveryPasswords.java │ │ │ ├── RegistrationRecoveryPasswordsManager.java │ │ │ ├── RemoteConfig.java │ │ │ ├── RemoteConfigs.java │ │ │ ├── RemoteConfigsManager.java │ │ │ ├── RemovedMessage.java │ │ │ ├── RepeatedUseECSignedPreKeyStore.java │ │ │ ├── RepeatedUseKEMSignedPreKeyStore.java │ │ │ ├── RepeatedUseSignedPreKeyStore.java │ │ │ ├── ReportMessageDynamoDb.java │ │ │ ├── ReportMessageManager.java │ │ │ ├── ReportedMessageListener.java │ │ │ ├── SerializedExpireableJsonDynamoStore.java │ │ │ ├── SingleUseECPreKeyStore.java │ │ │ ├── SingleUseKEMPreKeyStore.java │ │ │ ├── SingleUsePreKeyStore.java │ │ │ ├── SubscriberCredentials.java │ │ │ ├── SubscriptionException.java │ │ │ ├── SubscriptionManager.java │ │ │ ├── Subscriptions.java │ │ │ ├── UsernameHashNotAvailableException.java │ │ │ ├── UsernameReservationNotFoundException.java │ │ │ ├── VerificationSessionManager.java │ │ │ ├── VerificationSessions.java │ │ │ ├── VersionedProfile.java │ │ │ └── devicecheck │ │ │ │ ├── AppleDeviceCheckManager.java │ │ │ │ ├── AppleDeviceCheckTrustAnchor.java │ │ │ │ ├── AppleDeviceChecks.java │ │ │ │ ├── ChallengeNotFoundException.java │ │ │ │ ├── DeviceCheckKeyIdNotFoundException.java │ │ │ │ ├── DeviceCheckVerificationFailedException.java │ │ │ │ ├── DuplicatePublicKeyException.java │ │ │ │ ├── RequestReuseException.java │ │ │ │ └── TooManyKeysException.java │ │ │ ├── subscriptions │ │ │ ├── AppleAppStoreManager.java │ │ │ ├── BankMandateTranslator.java │ │ │ ├── BankTransferType.java │ │ │ ├── BraintreeGraphqlClient.java │ │ │ ├── BraintreeManager.java │ │ │ ├── BraintreePlanMetadata.java │ │ │ ├── ChargeFailure.java │ │ │ ├── CustomerAwareSubscriptionPaymentProcessor.java │ │ │ ├── GooglePlayBillingManager.java │ │ │ ├── PaymentDetails.java │ │ │ ├── PaymentMethod.java │ │ │ ├── PaymentProvider.java │ │ │ ├── PaymentStatus.java │ │ │ ├── ProcessorCustomer.java │ │ │ ├── StripeManager.java │ │ │ ├── SubscriptionCurrencyUtil.java │ │ │ ├── SubscriptionInformation.java │ │ │ ├── SubscriptionPaymentProcessor.java │ │ │ ├── SubscriptionPrice.java │ │ │ └── SubscriptionStatus.java │ │ │ ├── util │ │ │ ├── AbstractPublicKeyDeserializer.java │ │ │ ├── AbstractPublicKeySerializer.java │ │ │ ├── AsyncTimerUtil.java │ │ │ ├── AttributeValues.java │ │ │ ├── BackupAuthCredentialAdapter.java │ │ │ ├── BufferingInterceptor.java │ │ │ ├── ByteArrayAdapter.java │ │ │ ├── ByteArrayBase64UrlAdapter.java │ │ │ ├── ByteArrayBase64WithPaddingAdapter.java │ │ │ ├── CertificateUtil.java │ │ │ ├── CircuitBreakerUtil.java │ │ │ ├── ClosableEpoch.java │ │ │ ├── CompletableFutureUtil.java │ │ │ ├── Constants.java │ │ │ ├── Conversions.java │ │ │ ├── DeviceCapabilityAdapter.java │ │ │ ├── DeviceNameByteArrayAdapter.java │ │ │ ├── E164.java │ │ │ ├── ECPublicKeyAdapter.java │ │ │ ├── EnumMapUtil.java │ │ │ ├── ExactlySize.java │ │ │ ├── ExactlySizeValidator.java │ │ │ ├── ExactlySizeValidatorForArraysOfByte.java │ │ │ ├── ExactlySizeValidatorForCollection.java │ │ │ ├── ExactlySizeValidatorForSecretBytes.java │ │ │ ├── ExactlySizeValidatorForString.java │ │ │ ├── ExceptionUtils.java │ │ │ ├── GoogleApiUtil.java │ │ │ ├── HeaderUtils.java │ │ │ ├── HmacUtils.java │ │ │ ├── HostnameUtil.java │ │ │ ├── HttpServletRequestUtil.java │ │ │ ├── HttpUtils.java │ │ │ ├── IdentityKeyAdapter.java │ │ │ ├── ImpossiblePhoneNumberException.java │ │ │ ├── InetAddressRange.java │ │ │ ├── InstantAdapter.java │ │ │ ├── KEMPublicKeyAdapter.java │ │ │ ├── LinkDeviceToken.java │ │ │ ├── ManagedAwsCrt.java │ │ │ ├── NoStackTraceException.java │ │ │ ├── NoStackTraceRuntimeException.java │ │ │ ├── NonNormalizedPhoneNumberException.java │ │ │ ├── ObsoletePhoneNumberFormatException.java │ │ │ ├── Optionals.java │ │ │ ├── Pair.java │ │ │ ├── ProfileHelper.java │ │ │ ├── RedisClusterUtil.java │ │ │ ├── RegistrationIdValidator.java │ │ │ ├── ServiceIdentifierAdapter.java │ │ │ ├── SystemMapper.java │ │ │ ├── UUIDUtil.java │ │ │ ├── UsernameHashZkProofVerifier.java │ │ │ ├── Util.java │ │ │ ├── ValidBase64URLString.java │ │ │ ├── ValidHexString.java │ │ │ ├── VirtualExecutorServiceProvider.java │ │ │ ├── VirtualThreadPinEventMonitor.java │ │ │ ├── WeightedRandomSelect.java │ │ │ ├── logging │ │ │ │ ├── LoggingUnhandledExceptionMapper.java │ │ │ │ ├── RequestLogEnabledFilter.java │ │ │ │ ├── RequestLogEnabledFilterFactory.java │ │ │ │ ├── RequestLogManager.java │ │ │ │ ├── UncaughtExceptionHandler.java │ │ │ │ ├── UnknownKeepaliveOptionFilter.java │ │ │ │ ├── UnknownKeepaliveOptionFilterFactory.java │ │ │ │ └── UriInfoUtil.java │ │ │ └── ua │ │ │ │ ├── ClientPlatform.java │ │ │ │ ├── UnrecognizedUserAgentException.java │ │ │ │ ├── UserAgent.java │ │ │ │ └── UserAgentUtil.java │ │ │ ├── websocket │ │ │ ├── AuthenticatedConnectListener.java │ │ │ ├── InvalidWebsocketAddressException.java │ │ │ ├── ProvisioningConnectListener.java │ │ │ ├── WebSocketAccountAuthenticator.java │ │ │ └── WebSocketConnection.java │ │ │ └── workers │ │ │ ├── AbstractCommandWithDependencies.java │ │ │ ├── AbstractSinglePassCrawlAccountsCommand.java │ │ │ ├── BackupMetricsCommand.java │ │ │ ├── BackupUsageRecalculationCommand.java │ │ │ ├── CertificateCommand.java │ │ │ ├── CheckDynamicConfigurationCommand.java │ │ │ ├── CommandDependencies.java │ │ │ ├── DeleteUserCommand.java │ │ │ ├── DiscardPushNotificationExperimentSamplesCommand.java │ │ │ ├── FinishPushNotificationExperimentCommand.java │ │ │ ├── IdleDeviceNotificationSchedulerFactory.java │ │ │ ├── IdleWakeupEligibilityChecker.java │ │ │ ├── JobSchedulerFactory.java │ │ │ ├── MessagePersisterServiceCommand.java │ │ │ ├── NotifyIdleDevicesCommand.java │ │ │ ├── ProcessScheduledJobsServiceCommand.java │ │ │ ├── PushNotificationExperimentFactory.java │ │ │ ├── RegenerateAccountConstraintDataCommand.java │ │ │ ├── RemoveExpiredAccountsCommand.java │ │ │ ├── RemoveExpiredBackupsCommand.java │ │ │ ├── RemoveExpiredLinkedDevicesCommand.java │ │ │ ├── RemoveExpiredUsernameHoldsCommand.java │ │ │ ├── ScheduledApnPushNotificationSenderServiceCommand.java │ │ │ ├── ServerVersionCommand.java │ │ │ ├── SetRequestLoggingEnabledTask.java │ │ │ ├── SetUserDiscoverabilityCommand.java │ │ │ ├── StartPushNotificationExperimentCommand.java │ │ │ ├── UnlinkDeviceCommand.java │ │ │ └── ZkParamsCommand.java │ ├── proto │ │ ├── DisconnectionRequests.proto │ │ ├── DonationsPubsub.proto │ │ ├── KeyTransparencyService.proto │ │ ├── NoiseDirect.proto │ │ ├── NoiseTunnel.proto │ │ ├── PubSubMessage.proto │ │ ├── RegistrationService.proto │ │ ├── TextSecure.proto │ │ ├── WebSocketConnectionEvent.proto │ │ └── org │ │ │ └── signal │ │ │ └── chat │ │ │ ├── README.md │ │ │ ├── account.proto │ │ │ ├── backups.proto │ │ │ ├── calling.proto │ │ │ ├── common.proto │ │ │ ├── credentials.proto │ │ │ ├── device.proto │ │ │ ├── keys.proto │ │ │ ├── messages.proto │ │ │ ├── payments.proto │ │ │ ├── profile.proto │ │ │ └── require.proto │ └── resources │ │ ├── META-INF │ │ ├── services │ │ │ ├── io.dropwizard.jackson.Discoverable │ │ │ ├── io.dropwizard.logging.common.AppenderFactory │ │ │ ├── io.dropwizard.logging.common.filter.FilterFactory │ │ │ ├── io.dropwizard.metrics.common.ReporterFactory │ │ │ └── org.whispersystems.textsecuregcm.configuration.AwsCredentialsProviderFactory │ │ ├── validation.xml │ │ └── validation │ │ │ └── constraints-custom.xml │ │ ├── banner.txt │ │ ├── lua │ │ ├── account_database_crawler │ │ │ └── unlock.lua │ │ ├── apn │ │ │ └── schedule_background_notification.lua │ │ ├── clear_presence.lua │ │ ├── get_delivery_attempt_count.lua │ │ ├── get_items.lua │ │ ├── get_queues_to_persist.lua │ │ ├── insert_item.lua │ │ ├── insert_shared_multirecipient_message_data.lua │ │ ├── remove_item_by_guid.lua │ │ ├── remove_queue.lua │ │ ├── remove_recipient_view_from_mrm_data.lua │ │ ├── renew_presence.lua │ │ ├── unlock_queue.lua │ │ └── validate_rate_limit.lua │ │ └── org │ │ ├── signal │ │ ├── badges │ │ │ ├── Badges.properties │ │ │ └── Badges_en.properties │ │ └── bankmandate │ │ │ └── BankMandate.properties │ │ └── whispersystems │ │ └── textsecuregcm │ │ ├── push │ │ └── apns-certificates.pem │ │ └── storage │ │ └── devicecheck │ │ └── apple_device_check.pem │ └── test │ ├── java │ └── org │ │ └── whispersystems │ │ └── textsecuregcm │ │ ├── BufferingInterceptorIntegrationTest.java │ │ ├── CheckServiceConfigurations.java │ │ ├── LocalWhisperServerService.java │ │ ├── ProvisioningTimeoutIntegrationTest.java │ │ ├── WebsocketResourceProviderIntegrationTest.java │ │ ├── WebsocketReuseAuthIntegrationTest.java │ │ ├── WhisperServerServiceTest.java │ │ ├── auth │ │ ├── AccountAuthenticatorTest.java │ │ ├── BasicAuthorizationHeaderTest.java │ │ ├── CertificateGeneratorTest.java │ │ ├── CloudflareTurnCredentialsManagerTest.java │ │ ├── DisconnectionRequestManagerTest.java │ │ ├── ExternalServiceCredentialsGeneratorTest.java │ │ ├── ExternalServiceCredentialsSelectorTest.java │ │ ├── IdlePrimaryDeviceAuthenticatedWebSocketUpgradeFilterTest.java │ │ ├── LinkedDeviceRefreshRequirementProviderTest.java │ │ ├── OptionalAccessTest.java │ │ ├── PhoneNumberChangeRefreshRequirementProviderTest.java │ │ ├── RegistrationLockError.java │ │ ├── RegistrationLockVerificationManagerTest.java │ │ ├── SaltedTokenHashTest.java │ │ ├── StoredRegistrationLockTest.java │ │ ├── UnidentifiedAccessChecksumTest.java │ │ ├── UnidentifiedAccessUtilTest.java │ │ └── grpc │ │ │ ├── AbstractAuthenticationInterceptorTest.java │ │ │ ├── MockAuthenticationInterceptor.java │ │ │ ├── ProhibitAuthenticationInterceptorTest.java │ │ │ └── RequireAuthenticationInterceptorTest.java │ │ ├── backup │ │ ├── BackupAuthManagerTest.java │ │ ├── BackupAuthTestUtil.java │ │ ├── BackupManagerTest.java │ │ ├── BackupsDbTest.java │ │ ├── Cdn3BackupCredentialGeneratorTest.java │ │ └── Cdn3RemoteStorageManagerTest.java │ │ ├── badges │ │ └── ConfiguredProfileBadgeConverterTest.java │ │ ├── captcha │ │ ├── CaptchaCheckerTest.java │ │ └── ShortCodeExpanderTest.java │ │ ├── configuration │ │ ├── LocalDynamoDbFactory.java │ │ ├── LocalFaultTolerantRedisClientFactory.java │ │ ├── LocalFaultTolerantRedisClusterFactory.java │ │ ├── NoWaitDogstatsdConfiguration.java │ │ ├── StaticS3ObjectMonitorFactory.java │ │ ├── StubPaymentsServiceClientsFactory.java │ │ ├── StubPubSubPublisherFactory.java │ │ ├── StubRegistrationServiceClientFactory.java │ │ ├── dynamic │ │ │ └── DynamicConfigurationTest.java │ │ └── secrets │ │ │ └── SecretsTest.java │ │ ├── controllers │ │ ├── AccountControllerTest.java │ │ ├── AccountControllerV2Test.java │ │ ├── AccountIdentityResponseBuilderTest.java │ │ ├── ArchiveControllerTest.java │ │ ├── AttachmentControllerV4Test.java │ │ ├── CallLinkControllerTest.java │ │ ├── CallRoutingControllerV2Test.java │ │ ├── CertificateControllerTest.java │ │ ├── ChallengeControllerTest.java │ │ ├── DeviceCheckControllerTest.java │ │ ├── DeviceControllerTest.java │ │ ├── DirectoryControllerV2Test.java │ │ ├── DonationControllerTest.java │ │ ├── KeyTransparencyControllerTest.java │ │ ├── KeysControllerTest.java │ │ ├── MessageControllerTest.java │ │ ├── PaymentsControllerTest.java │ │ ├── ProfileControllerTest.java │ │ ├── ProvisioningControllerTest.java │ │ ├── RegistrationControllerTest.java │ │ ├── RemoteConfigControllerTest.java │ │ ├── SecureStorageControllerTest.java │ │ ├── SecureValueRecovery2ControllerTest.java │ │ ├── SecureValueRecoveryControllerBaseTest.java │ │ ├── StickerControllerTest.java │ │ ├── SubscriptionControllerTest.java │ │ └── VerificationControllerTest.java │ │ ├── currency │ │ ├── CoinGeckoClientTest.java │ │ ├── CurrencyConversionManagerTest.java │ │ └── FixerClientTest.java │ │ ├── entities │ │ ├── AnswerChallengeRequestTest.java │ │ ├── IncomingMessageListTest.java │ │ ├── OutgoingMessageEntityTest.java │ │ └── PreKeyTest.java │ │ ├── experiment │ │ ├── ExperimentEnrollmentManagerTest.java │ │ ├── ExperimentTest.java │ │ ├── IdleDevicePushNotificationExperimentTest.java │ │ └── PushNotificationExperimentSamplesTest.java │ │ ├── filters │ │ ├── ExternalRequestFilterTest.java │ │ ├── RemoteAddressFilterIntegrationTest.java │ │ ├── RemoteAddressFilterTest.java │ │ ├── RemoteDeprecationFilterTest.java │ │ ├── RequestStatisticsFilterTest.java │ │ ├── RestDeprecationFilterTest.java │ │ └── TimestampResponseFilterTest.java │ │ ├── grpc │ │ ├── AccountsAnonymousGrpcServiceTest.java │ │ ├── AccountsGrpcServiceTest.java │ │ ├── BackupsAnonymousGrpcServiceTest.java │ │ ├── BackupsGrpcServiceTest.java │ │ ├── ChannelShutdownInterceptorTest.java │ │ ├── DevicesGrpcServiceTest.java │ │ ├── EchoServiceImpl.java │ │ ├── ExternalServiceCredentialsAnonymousGrpcServiceTest.java │ │ ├── ExternalServiceCredentialsGrpcServiceTest.java │ │ ├── GrpcServerExtension.java │ │ ├── GrpcTestUtils.java │ │ ├── KeysAnonymousGrpcServiceTest.java │ │ ├── KeysGrpcServiceTest.java │ │ ├── MessagesAnonymousGrpcServiceTest.java │ │ ├── MessagesGrpcServiceTest.java │ │ ├── MockRequestAttributesInterceptor.java │ │ ├── PaymentsGrpcServiceTest.java │ │ ├── ProfileAnonymousGrpcServiceTest.java │ │ ├── ProfileGrpcServiceTest.java │ │ ├── RequestAttributesServiceImpl.java │ │ ├── RequestAttributesUtilTest.java │ │ ├── SimpleBaseGrpcTest.java │ │ ├── ValidatingInterceptorTest.java │ │ └── net │ │ │ ├── AbstractLeakDetectionTest.java │ │ │ ├── AbstractNoiseHandlerTest.java │ │ │ ├── AbstractNoiseTunnelServerIntegrationTest.java │ │ │ ├── GrpcClientConnectionManagerTest.java │ │ │ ├── HAProxyMessageHandlerTest.java │ │ │ ├── NoiseAnonymousHandlerTest.java │ │ │ ├── NoiseAuthenticatedHandlerTest.java │ │ │ ├── NoiseHandshakeHelperTest.java │ │ │ ├── ProxyProtocolDetectionHandlerTest.java │ │ │ ├── client │ │ │ ├── ClientErrorHandler.java │ │ │ ├── CloseFrameEvent.java │ │ │ ├── EstablishRemoteConnectionHandler.java │ │ │ ├── FastOpenRequestBufferedEvent.java │ │ │ ├── HAProxyMessageSender.java │ │ │ ├── Http2Buffering.java │ │ │ ├── NoiseClientHandshakeCompleteEvent.java │ │ │ ├── NoiseClientHandshakeHandler.java │ │ │ ├── NoiseClientHandshakeHelper.java │ │ │ ├── NoiseClientTransportHandler.java │ │ │ ├── NoiseTunnelClient.java │ │ │ └── ReadyForNoiseHandshakeEvent.java │ │ │ ├── noisedirect │ │ │ └── DirectNoiseTunnelServerIntegrationTest.java │ │ │ └── websocket │ │ │ ├── RejectUnsupportedMessagesHandlerTest.java │ │ │ ├── TlsWebSocketNoiseTunnelServerIntegrationTest.java │ │ │ ├── WebSocketNoiseTunnelServerIntegrationTest.java │ │ │ ├── WebSocketOpeningHandshakeHandlerTest.java │ │ │ └── WebsocketHandshakeCompleteHandlerTest.java │ │ ├── http │ │ └── FaultTolerantHttpClientTest.java │ │ ├── identity │ │ ├── AciServiceIdentifierTest.java │ │ ├── PniServiceIdentifierTest.java │ │ └── ServiceIdentifierTest.java │ │ ├── limits │ │ ├── CardinalityEstimatorTest.java │ │ ├── DynamicRateLimiterTest.java │ │ ├── MessageDeliveryLoopMonitorTest.java │ │ ├── RateLimitChallengeManagerTest.java │ │ ├── RateLimitChallengeOptionManagerTest.java │ │ ├── RateLimitedByIpTest.java │ │ ├── RateLimiterConfigTest.java │ │ ├── RateLimitersLuaScriptTest.java │ │ └── RateLimitersTest.java │ │ ├── mappers │ │ ├── GrpcStatusRuntimeExceptionMapperTest.java │ │ └── IOExceptionMapperTest.java │ │ ├── metrics │ │ ├── MessageMetricsTest.java │ │ ├── MetricsHttpChannelListenerIntegrationTest.java │ │ ├── MetricsHttpChannelListenerTest.java │ │ ├── MetricsRequestEventListenerTest.java │ │ ├── MetricsUtilTest.java │ │ ├── TlsCertificateExpirationUtilTest.java │ │ └── UserAgentTagUtilTest.java │ │ ├── push │ │ ├── APNSenderTest.java │ │ ├── FcmSenderTest.java │ │ ├── IdleDeviceNotificationSchedulerTest.java │ │ ├── MessageSenderTest.java │ │ ├── ProvisioningManagerTest.java │ │ ├── PushNotificationManagerTest.java │ │ ├── PushNotificationSchedulerTest.java │ │ └── WebSocketConnectionEventManagerTest.java │ │ ├── redis │ │ ├── ClusterLuaScriptTest.java │ │ ├── FaultTolerantPubSubClusterConnectionTest.java │ │ ├── FaultTolerantRedisClientTest.java │ │ ├── FaultTolerantRedisClusterClientTest.java │ │ ├── LettuceShardCircuitBreakerTest.java │ │ ├── RedisClusterExtension.java │ │ └── RedisServerExtension.java │ │ ├── registration │ │ └── IdentityTokenCallCredentialsTest.java │ │ ├── s3 │ │ ├── PolicySignerTest.java │ │ └── S3ObjectMonitorTest.java │ │ ├── scheduler │ │ ├── JobSchedulerTest.java │ │ └── SchedulingUtilTest.java │ │ ├── securestorage │ │ └── SecureStorageClientTest.java │ │ ├── securevaluerecovery │ │ └── SecureValueRecovery2ClientTest.java │ │ ├── storage │ │ ├── AccountChangeValidatorTest.java │ │ ├── AccountCreationDeletionIntegrationTest.java │ │ ├── AccountLockManagerTest.java │ │ ├── AccountTest.java │ │ ├── AccountsManagerChangeNumberIntegrationTest.java │ │ ├── AccountsManagerConcurrentModificationIntegrationTest.java │ │ ├── AccountsManagerDeviceTransferIntegrationTest.java │ │ ├── AccountsManagerTest.java │ │ ├── AccountsManagerUsernameIntegrationTest.java │ │ ├── AccountsTest.java │ │ ├── AddRemoveDeviceIntegrationTest.java │ │ ├── ChangeNumberManagerTest.java │ │ ├── ClientPublicKeysTest.java │ │ ├── ClientReleaseManagerTest.java │ │ ├── ClientReleasesTest.java │ │ ├── DeviceTest.java │ │ ├── DynamicConfigurationManagerTest.java │ │ ├── DynamoDbExtension.java │ │ ├── DynamoDbExtensionSchema.java │ │ ├── IssuedReceiptsManagerTest.java │ │ ├── KeysManagerTest.java │ │ ├── MessagePersisterIntegrationTest.java │ │ ├── MessagePersisterTest.java │ │ ├── MessagesCacheGetItemsScriptTest.java │ │ ├── MessagesCacheInsertScriptTest.java │ │ ├── MessagesCacheInsertSharedMultiRecipientPayloadAndViewsScriptTest.java │ │ ├── MessagesCacheRemoveByGuidScriptTest.java │ │ ├── MessagesCacheRemoveQueueScriptTest.java │ │ ├── MessagesCacheRemoveRecipientViewFromMrmDataScriptTest.java │ │ ├── MessagesCacheTest.java │ │ ├── MessagesDynamoDbTest.java │ │ ├── MessagesManagerTest.java │ │ ├── OnetimeDonationsManagerTest.java │ │ ├── PersistentTimerTest.java │ │ ├── PhoneNumberIdentifiersTest.java │ │ ├── ProfilesManagerTest.java │ │ ├── ProfilesTest.java │ │ ├── PushChallengeDynamoDbTest.java │ │ ├── RedeemedReceiptsManagerTest.java │ │ ├── RegistrationRecoveryTest.java │ │ ├── RemoteConfigsManagerTest.java │ │ ├── RemoteConfigsTest.java │ │ ├── RepeatedUseECSignedPreKeyStoreTest.java │ │ ├── RepeatedUseKEMSignedPreKeyStoreTest.java │ │ ├── RepeatedUseSignedPreKeyStoreTest.java │ │ ├── ReportMessageDynamoDbTest.java │ │ ├── ReportMessageManagerTest.java │ │ ├── SerializedExpireableJsonDynamoStoreTest.java │ │ ├── SingleUseECPreKeyStoreTest.java │ │ ├── SingleUseKEMPreKeyStoreTest.java │ │ ├── SingleUsePreKeyStoreTest.java │ │ ├── SubscriptionsTest.java │ │ ├── VerificationSessionsTest.java │ │ └── devicecheck │ │ │ ├── AppleDeviceCheckManagerTest.java │ │ │ ├── AppleDeviceChecksTest.java │ │ │ └── DeviceCheckTestUtil.java │ │ ├── subscriptions │ │ ├── AppleAppStoreManagerTest.java │ │ ├── BraintreeGraphqlClientTest.java │ │ ├── BraintreeManagerTest.java │ │ ├── GooglePlayBillingManagerTest.java │ │ └── ProcessorCustomerTest.java │ │ ├── tests │ │ └── util │ │ │ ├── AccountsHelper.java │ │ │ ├── AuthHelper.java │ │ │ ├── DevicesHelper.java │ │ │ ├── ExperimentHelper.java │ │ │ ├── FakeDynamicConfigurationManager.java │ │ │ ├── JsonHelpers.java │ │ │ ├── KeysHelper.java │ │ │ ├── MessageHelper.java │ │ │ ├── MockRedisFuture.java │ │ │ ├── MultiRecipientMessageHelper.java │ │ │ ├── ProfileTestHelper.java │ │ │ ├── RedisClusterHelper.java │ │ │ ├── RedisServerHelper.java │ │ │ ├── SynchronousExecutorService.java │ │ │ ├── TestPrincipal.java │ │ │ ├── TestRecipient.java │ │ │ └── TestWebsocketListener.java │ │ ├── util │ │ ├── AttributeValuesTest.java │ │ ├── ClosableEpochTest.java │ │ ├── CompletableFutureTestUtil.java │ │ ├── DeviceCapabilityAdapterTest.java │ │ ├── DeviceNameByteArrayAdapterTest.java │ │ ├── E164Test.java │ │ ├── ECPublicKeyAdapterTest.java │ │ ├── HttpServletRequestUtilIntegrationTest.java │ │ ├── HttpServletRequestUtilTest.java │ │ ├── HttpUtilsTest.java │ │ ├── IdentityKeyAdapterTest.java │ │ ├── InetAddressRangeTest.java │ │ ├── KEMPublicKeyAdapterTest.java │ │ ├── LocaleTest.java │ │ ├── MockUtils.java │ │ ├── MutableClock.java │ │ ├── RedisClusterUtilTest.java │ │ ├── SystemMapperTest.java │ │ ├── TestClock.java │ │ ├── TestRandomUtil.java │ │ ├── TestRemoteAddressFilterProvider.java │ │ ├── UtilTest.java │ │ ├── ValidNumberTest.java │ │ ├── VirtualExecutorServiceProviderTest.java │ │ ├── VirtualThreadPinEventMonitorTest.java │ │ ├── WeightedRandomSelectTest.java │ │ ├── jetty │ │ │ └── TestResource.java │ │ ├── logging │ │ │ ├── LoggingUnhandledExceptionMapperTest.java │ │ │ └── UriInfoUtilTest.java │ │ ├── redis │ │ │ ├── BaseRedisCommandsHandler.java │ │ │ ├── RedisCommandsHandler.java │ │ │ ├── RedisLuaScriptSandbox.java │ │ │ └── SimpleCacheCommandsHandler.java │ │ └── ua │ │ │ └── UserAgentUtilTest.java │ │ ├── websocket │ │ ├── ProvisioningConnectListenerTest.java │ │ ├── WebSocketAccountAuthenticatorTest.java │ │ ├── WebSocketConnectionIntegrationTest.java │ │ └── WebSocketConnectionTest.java │ │ └── workers │ │ ├── FinishPushNotificationExperimentCommandTest.java │ │ ├── IdleWakeupEligibilityCheckerTest.java │ │ ├── NotifyIdleDevicesCommandTest.java │ │ ├── ProcessScheduledJobsServiceCommandTest.java │ │ ├── RegenerateAccountConstraintDataCommandTest.java │ │ ├── RemoveExpiredAccountsCommandTest.java │ │ ├── RemoveExpiredLinkedDevicesCommandTest.java │ │ ├── RemoveExpiredUsernameHoldsCommandTest.java │ │ └── StartPushNotificationExperimentCommandTest.java │ ├── proto │ ├── echo_service.proto │ ├── request_attributes_service.proto │ ├── test_tree_head.proto │ └── validation_test.proto │ └── resources │ ├── META-INF │ └── services │ │ ├── org.whispersystems.textsecuregcm.configuration.DatadogConfiguration │ │ ├── org.whispersystems.textsecuregcm.configuration.DynamicConfigurationManagerFactory │ │ ├── org.whispersystems.textsecuregcm.configuration.DynamoDbClientFactory │ │ ├── org.whispersystems.textsecuregcm.configuration.FaultTolerantRedisClientFactory │ │ ├── org.whispersystems.textsecuregcm.configuration.FaultTolerantRedisClusterFactory │ │ ├── org.whispersystems.textsecuregcm.configuration.PaymentsServiceClientsFactory │ │ ├── org.whispersystems.textsecuregcm.configuration.PubSubPublisherFactory │ │ ├── org.whispersystems.textsecuregcm.configuration.RegistrationServiceClientFactory │ │ └── org.whispersystems.textsecuregcm.configuration.S3ObjectMonitorFactory │ ├── config │ ├── test-secrets-bundle.yml │ └── test.yml │ ├── fixtures │ ├── current_message_duplicate_device.json │ ├── current_message_extra_device.json │ ├── current_message_multi_device.json │ ├── current_message_multi_device_not_urgent.json │ ├── current_message_multi_device_pni.json │ ├── current_message_null_message_in_list.json │ ├── current_message_registration_id.json │ ├── current_message_single_device.json │ ├── current_message_single_device_bad_type.json │ ├── current_message_single_device_not_urgent.json │ ├── current_message_single_device_server_receipt_type.json │ ├── current_message_sync.json │ ├── fixer.res.json │ ├── mismatched_registration_id.json │ ├── missing_device_response.json │ ├── missing_device_response2.json │ └── prekey_v2.json │ ├── logback-test.xml │ └── org │ └── whispersystems │ └── textsecuregcm │ ├── storage │ ├── AccountsManagerTest-testJsonRoundTripSerialization.json │ └── devicecheck │ │ ├── apple-sample-attestation │ │ ├── webauthn4j-sample-assertion │ │ └── webauthn4j-sample-attestation │ └── util │ └── ip2asn-test.tsv └── websocket-resources ├── pom.xml └── src ├── main ├── java │ └── org │ │ └── whispersystems │ │ └── websocket │ │ ├── ReusableAuth.java │ │ ├── WebSocketClient.java │ │ ├── WebSocketResourceProvider.java │ │ ├── WebSocketResourceProviderFactory.java │ │ ├── WebSocketSecurityContext.java │ │ ├── WebsocketHeaders.java │ │ ├── auth │ │ ├── AuthenticatedWebSocketUpgradeFilter.java │ │ ├── AuthenticationException.java │ │ ├── Mutable.java │ │ ├── PrincipalSupplier.java │ │ ├── ReadOnly.java │ │ ├── WebSocketAuthenticator.java │ │ └── WebsocketAuthValueFactoryProvider.java │ │ ├── configuration │ │ └── WebSocketConfiguration.java │ │ ├── logging │ │ ├── AsyncWebsocketEventAppenderFactory.java │ │ ├── WebsocketEvent.java │ │ ├── WebsocketRequestLog.java │ │ ├── WebsocketRequestLoggerFactory.java │ │ └── layout │ │ │ ├── WebsocketEventLayout.java │ │ │ ├── WebsocketEventLayoutFactory.java │ │ │ └── converters │ │ │ ├── ContentLengthConverter.java │ │ │ ├── DateConverter.java │ │ │ ├── EnsureLineSeparation.java │ │ │ ├── LineSeparatorConverter.java │ │ │ ├── NAConverter.java │ │ │ ├── RemoteHostConverter.java │ │ │ ├── RequestHeaderConverter.java │ │ │ ├── RequestUrlConverter.java │ │ │ ├── StatusCodeConverter.java │ │ │ └── WebSocketEventConverter.java │ │ ├── messages │ │ ├── InvalidMessageException.java │ │ ├── WebSocketMessage.java │ │ ├── WebSocketMessageFactory.java │ │ ├── WebSocketRequestMessage.java │ │ ├── WebSocketResponseMessage.java │ │ └── protobuf │ │ │ ├── ProtobufWebSocketMessage.java │ │ │ ├── ProtobufWebSocketMessageFactory.java │ │ │ ├── ProtobufWebSocketRequestMessage.java │ │ │ └── ProtobufWebSocketResponseMessage.java │ │ ├── session │ │ ├── ContextPrincipal.java │ │ ├── WebSocketSession.java │ │ ├── WebSocketSessionContainerRequestValueFactory.java │ │ ├── WebSocketSessionContext.java │ │ └── WebSocketSessionContextValueFactoryProvider.java │ │ └── setup │ │ ├── WebSocketConnectListener.java │ │ └── WebSocketEnvironment.java └── proto │ └── WebSocketProtocol.proto └── test └── java └── org └── whispersystems └── websocket ├── WebSocketResourceProviderFactoryTest.java ├── WebSocketResourceProviderTest.java └── logging └── WebSocketRequestLogTest.java /.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 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/signalapp/Signal-Server/981d929f506c25b18449442557940afc2ea2fca0/.github/stale.yml -------------------------------------------------------------------------------- /.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 | .java-version 20 | .opsmanage 21 | put.sh 22 | deployer-staging.properties 23 | deployer-production.properties 24 | deployer.log 25 | /service/src/main/resources/org/signal/badges/Badges_*.properties 26 | !/service/src/main/resources/org/signal/badges/Badges_en.properties 27 | /service/src/main/resources/org/signal/subscriptions/Subscriptions_*.properties 28 | !/service/src/main/resources/org/signal/subscriptions/Subscriptions_en.properties 29 | .project 30 | .classpath 31 | .settings 32 | .DS_Store -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.mvn/extensions.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | fr.brouillard.oss 6 | jgitver-maven-plugin 7 | 1.9.0 8 | 9 | 10 | -------------------------------------------------------------------------------- /.mvn/jgitver.config.xml: -------------------------------------------------------------------------------- 1 | 4 | true 5 | false 6 | 7 | 8 | (.*) 9 | 10 | IGNORE 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/signalapp/Signal-Server/981d929f506c25b18449442557940afc2ea2fca0/.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /integration-tests/.gitignore: -------------------------------------------------------------------------------- 1 | .libs 2 | src/main/resources/config.yml 3 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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/graphql/braintree/CreatePayPalBillingAgreement.graphql: -------------------------------------------------------------------------------- 1 | mutation CreatePayPalBillingAgreement($input: CreatePayPalBillingAgreementInput!) { 2 | createPayPalBillingAgreement(input: $input) { 3 | approvalUrl, 4 | billingAgreementToken 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /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/graphql/braintree/TokenizePayPalBillingAgreement.graphql: -------------------------------------------------------------------------------- 1 | mutation TokenizePayPalBillingAgreement($input: TokenizePayPalBillingAgreementInput!) { 2 | tokenizePayPalBillingAgreement(input: $input) { 3 | paymentMethod { 4 | id 5 | } 6 | } 7 | } 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 | -------------------------------------------------------------------------------- /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-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/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/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/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/java/org/whispersystems/textsecuregcm/auth/AccountAndAuthenticatedDeviceHolder.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 org.whispersystems.textsecuregcm.storage.Account; 9 | import org.whispersystems.textsecuregcm.storage.Device; 10 | 11 | public interface AccountAndAuthenticatedDeviceHolder { 12 | 13 | Account getAccount(); 14 | 15 | Device getAuthenticatedDevice(); 16 | } 17 | -------------------------------------------------------------------------------- /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/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/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/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 | -------------------------------------------------------------------------------- /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/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/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/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/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/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/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/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/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/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/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/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/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/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/calls/routing/TurnServerOptions.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.calls.routing; 7 | 8 | import java.util.List; 9 | import java.util.Optional; 10 | 11 | public record TurnServerOptions( 12 | String hostname, 13 | Optional> urlsWithIps, 14 | Optional> urlsWithHostname 15 | ) { 16 | } 17 | -------------------------------------------------------------------------------- /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/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/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/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 | 12 | public record CdnConfiguration(@NotNull @Valid StaticAwsCredentialsFactory credentials, 13 | @NotBlank String bucket, 14 | @NotBlank String region) { 15 | } 16 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/configuration/ClientCdnConfiguration.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.configuration; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import jakarta.validation.Valid; 5 | import jakarta.validation.constraints.NotNull; 6 | 7 | /** 8 | * Configuration used to interact with a cdn via HTTP 9 | */ 10 | public class ClientCdnConfiguration { 11 | 12 | @JsonProperty 13 | @NotNull 14 | @Valid 15 | CircuitBreakerConfiguration circuitBreaker = new CircuitBreakerConfiguration(); 16 | 17 | @JsonProperty 18 | @NotNull 19 | @Valid 20 | RetryConfiguration retry = new RetryConfiguration(); 21 | 22 | public CircuitBreakerConfiguration getCircuitBreaker() { 23 | return circuitBreaker; 24 | } 25 | 26 | public RetryConfiguration getRetry() { 27 | return retry; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /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/configuration/DatadogConfiguration.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.micrometer.statsd.StatsdConfig; 11 | import java.time.Duration; 12 | 13 | @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = DogstatsdConfiguration.class) 14 | public interface DatadogConfiguration extends StatsdConfig, Discoverable { 15 | 16 | String getEnvironment(); 17 | 18 | Duration getShutdownWaitDuration(); 19 | } 20 | -------------------------------------------------------------------------------- /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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/configuration/RegistrationServiceClientFactory.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.core.setup.Environment; 10 | import io.dropwizard.jackson.Discoverable; 11 | import org.whispersystems.textsecuregcm.registration.RegistrationServiceClient; 12 | import java.util.concurrent.Executor; 13 | import java.util.concurrent.ScheduledExecutorService; 14 | 15 | @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = RegistrationServiceConfiguration.class) 16 | public interface RegistrationServiceClientFactory extends Discoverable { 17 | 18 | RegistrationServiceClient build(Environment environment, Executor callbackExecutor, 19 | ScheduledExecutorService identityRefreshExecutor); 20 | } 21 | -------------------------------------------------------------------------------- /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/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/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/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/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/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/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/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/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/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(Duration pinEventThreshold) {} 10 | -------------------------------------------------------------------------------- /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/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/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/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/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/configuration/dynamic/DynamicVirtualThreadConfiguration.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 | import java.util.Set; 9 | 10 | public record DynamicVirtualThreadConfiguration(Set allowedPinEvents) {} 11 | -------------------------------------------------------------------------------- /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/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/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/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/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 org.whispersystems.textsecuregcm.auth.TurnToken; 9 | 10 | import java.util.List; 11 | 12 | public record GetCallingRelaysResponse(List relays) { 13 | } 14 | -------------------------------------------------------------------------------- /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/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/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/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 | -------------------------------------------------------------------------------- /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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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 com.google.common.annotations.VisibleForTesting; 10 | import jakarta.validation.constraints.NotEmpty; 11 | import jakarta.validation.constraints.Size; 12 | 13 | public class RegistrationLock { 14 | 15 | @JsonProperty 16 | @Size(min=64, max=64) 17 | @NotEmpty 18 | private String registrationLock; 19 | 20 | public RegistrationLock() {} 21 | 22 | @VisibleForTesting 23 | public RegistrationLock(String registrationLock) { 24 | this.registrationLock = registrationLock; 25 | } 26 | 27 | public String getRegistrationLock() { 28 | return registrationLock; 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/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/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/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/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/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/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/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/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/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/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/DeviceLastSeenState.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.experiment; 2 | 3 | import javax.annotation.Nullable; 4 | 5 | public record DeviceLastSeenState(boolean deviceExists, 6 | long createdAtMillis, 7 | boolean hasPushToken, 8 | long lastSeenMillis, 9 | @Nullable PushTokenType pushTokenType) { 10 | 11 | public static DeviceLastSeenState MISSING_DEVICE_STATE = new DeviceLastSeenState(false, 0, false, 0, null); 12 | 13 | public enum PushTokenType { 14 | APNS, 15 | FCM 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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/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/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 | -------------------------------------------------------------------------------- /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/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/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/grpc/net/HandshakePattern.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.textsecuregcm.grpc.net; 6 | 7 | public enum HandshakePattern { 8 | NK("Noise_NK_25519_ChaChaPoly_BLAKE2b"), 9 | IK("Noise_IK_25519_ChaChaPoly_BLAKE2b"); 10 | 11 | private final String protocol; 12 | 13 | public String protocol() { 14 | return protocol; 15 | } 16 | 17 | 18 | HandshakePattern(String protocol) { 19 | this.protocol = protocol; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/grpc/net/ManagedDefaultEventLoopGroup.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.grpc.net; 2 | 3 | import io.dropwizard.lifecycle.Managed; 4 | import io.netty.channel.DefaultEventLoopGroup; 5 | 6 | /** 7 | * A wrapper for a Netty {@link DefaultEventLoopGroup} that implements Dropwizard's {@link Managed} interface, allowing 8 | * Dropwizard to manage the lifecycle of the event loop group. 9 | */ 10 | public class ManagedDefaultEventLoopGroup extends DefaultEventLoopGroup implements Managed { 11 | 12 | @Override 13 | public void stop() throws InterruptedException { 14 | this.shutdownGracefully().await(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /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/main/java/org/whispersystems/textsecuregcm/grpc/net/NoiseException.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.grpc.net; 2 | 3 | import org.whispersystems.textsecuregcm.util.NoStackTraceException; 4 | 5 | /** 6 | * Indicates that some problem occurred while processing an encrypted noise message (e.g. an unexpected message size/ 7 | * format or a general encryption error). 8 | */ 9 | public class NoiseException extends NoStackTraceException { 10 | public NoiseException(final String message) { 11 | super(message); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/grpc/net/NoiseHandshakeException.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.grpc.net; 2 | 3 | import org.whispersystems.textsecuregcm.util.NoStackTraceException; 4 | 5 | /** 6 | * Indicates that some problem occurred while completing a Noise handshake (e.g. an unexpected message size/format or 7 | * a general encryption error). 8 | */ 9 | public class NoiseHandshakeException extends NoStackTraceException { 10 | 11 | public NoiseHandshakeException(final String message) { 12 | super(message); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/grpc/net/ProxyHandler.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.grpc.net; 2 | 3 | import io.netty.channel.Channel; 4 | import io.netty.channel.ChannelFutureListener; 5 | import io.netty.channel.ChannelHandlerContext; 6 | import io.netty.channel.ChannelInboundHandlerAdapter; 7 | 8 | /** 9 | * A proxy handler writes all data read from one channel to another peer channel. 10 | */ 11 | public class ProxyHandler extends ChannelInboundHandlerAdapter { 12 | 13 | private final Channel peerChannel; 14 | 15 | public ProxyHandler(final Channel peerChannel) { 16 | this.peerChannel = peerChannel; 17 | } 18 | 19 | @Override 20 | public void channelRead(final ChannelHandlerContext context, final Object message) { 21 | peerChannel.writeAndFlush(message) 22 | .addListener(ChannelFutureListener.CLOSE_ON_FAILURE); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/grpc/net/websocket/ApplicationWebSocketCloseReason.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.grpc.net.websocket; 2 | 3 | import io.netty.handler.codec.http.websocketx.WebSocketCloseStatus; 4 | 5 | enum ApplicationWebSocketCloseReason { 6 | NOISE_HANDSHAKE_ERROR(4001), 7 | NOISE_ENCRYPTION_ERROR(4002); 8 | 9 | private final int statusCode; 10 | 11 | ApplicationWebSocketCloseReason(final int statusCode) { 12 | this.statusCode = statusCode; 13 | } 14 | 15 | public int getStatusCode() { 16 | return statusCode; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /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/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/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/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/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/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/limits/RateLimiterConfig.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 jakarta.validation.constraints.AssertTrue; 9 | import java.time.Duration; 10 | 11 | public record RateLimiterConfig(int bucketSize, Duration permitRegenerationDuration, boolean failOpen) { 12 | 13 | public double leakRatePerMillis() { 14 | return 1.0 / (permitRegenerationDuration.toNanos() / 1e6); 15 | } 16 | 17 | @AssertTrue 18 | public boolean hasPositiveRegenerationRate() { 19 | try { 20 | return permitRegenerationDuration.toNanos() > 0; 21 | } catch (final ArithmeticException e) { 22 | // The duration was too large to fit in a long, so it's definitely positive 23 | return true; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /service/src/main/java/org/whispersystems/textsecuregcm/mappers/ObsoletePhoneNumberFormatExceptionMapper.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.mappers; 2 | 3 | import io.micrometer.core.instrument.Metrics; 4 | import jakarta.ws.rs.core.Response; 5 | import jakarta.ws.rs.ext.ExceptionMapper; 6 | import org.whispersystems.textsecuregcm.metrics.MetricsUtil; 7 | import org.whispersystems.textsecuregcm.util.ObsoletePhoneNumberFormatException; 8 | 9 | public class ObsoletePhoneNumberFormatExceptionMapper implements ExceptionMapper { 10 | 11 | private static final String COUNTER_NAME = MetricsUtil.name(ObsoletePhoneNumberFormatExceptionMapper.class, "errors"); 12 | 13 | @Override 14 | public Response toResponse(final ObsoletePhoneNumberFormatException exception) { 15 | Metrics.counter(COUNTER_NAME, "regionCode", exception.getRegionCode()).increment(); 16 | return Response.status(499).build(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /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/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/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/providers/RedisClusterHealthCheck.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2020 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.providers; 7 | 8 | import com.codahale.metrics.health.HealthCheck; 9 | import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisClusterClient; 10 | 11 | public class RedisClusterHealthCheck extends HealthCheck { 12 | 13 | private final FaultTolerantRedisClusterClient redisCluster; 14 | 15 | public RedisClusterHealthCheck(final FaultTolerantRedisClusterClient redisCluster) { 16 | this.redisCluster = redisCluster; 17 | } 18 | 19 | @Override 20 | protected Result check() { 21 | redisCluster.withCluster(connection -> connection.sync().upstream().commands().ping()); 22 | return Result.healthy(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /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/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/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/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/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/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 | -------------------------------------------------------------------------------- /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/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/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/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/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 | -------------------------------------------------------------------------------- /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/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/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/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/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/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/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/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/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/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/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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/ECPublicKeyAdapter.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.protocol.InvalidKeyException; 9 | import org.signal.libsignal.protocol.ecc.ECPublicKey; 10 | 11 | public class ECPublicKeyAdapter { 12 | 13 | public static class Serializer extends AbstractPublicKeySerializer { 14 | 15 | @Override 16 | protected byte[] serializePublicKey(final ECPublicKey publicKey) { 17 | return publicKey.serialize(); 18 | } 19 | } 20 | 21 | public static class Deserializer extends AbstractPublicKeyDeserializer { 22 | 23 | @Override 24 | protected ECPublicKey deserializePublicKey(final byte[] publicKeyBytes) throws InvalidKeyException { 25 | return new ECPublicKey(publicKeyBytes); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /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/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/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/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 | -------------------------------------------------------------------------------- /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/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/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/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/java/org/whispersystems/textsecuregcm/util/KEMPublicKeyAdapter.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.protocol.InvalidKeyException; 9 | import org.signal.libsignal.protocol.kem.KEMPublicKey; 10 | 11 | public class KEMPublicKeyAdapter { 12 | 13 | public static class Serializer extends AbstractPublicKeySerializer { 14 | 15 | @Override 16 | protected byte[] serializePublicKey(final KEMPublicKey publicKey) { 17 | return publicKey.serialize(); 18 | } 19 | } 20 | 21 | public static class Deserializer extends AbstractPublicKeyDeserializer { 22 | 23 | @Override 24 | protected KEMPublicKey deserializePublicKey(final byte[] publicKeyBytes) throws InvalidKeyException { 25 | return new KEMPublicKey(publicKeyBytes); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /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/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/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/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(org.signal.libsignal.protocol.util.Pair p) { 11 | this(p.first(), p.second()); 12 | } 13 | 14 | public Pair(Map.Entry e) { 15 | this(e.getKey(), e.getValue()); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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/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/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/logging/UncaughtExceptionHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | package org.whispersystems.textsecuregcm.util.logging; 7 | 8 | import javax.annotation.Nullable; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | public class UncaughtExceptionHandler { 13 | 14 | private static final Logger logger = LoggerFactory.getLogger(UncaughtExceptionHandler.class); 15 | 16 | public static void register() { 17 | @Nullable final Thread.UncaughtExceptionHandler current = Thread.getDefaultUncaughtExceptionHandler(); 18 | 19 | if (current != null) { 20 | logger.warn("Uncaught exception handler already exists: {}", current); 21 | return; 22 | } 23 | 24 | Thread.setDefaultUncaughtExceptionHandler((t, e) -> logger.error("Uncaught exception on thread {}", t, e)); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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/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/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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /service/src/main/resources/META-INF/services/io.dropwizard.jackson.Discoverable: -------------------------------------------------------------------------------- 1 | org.whispersystems.textsecuregcm.configuration.AwsCredentialsProviderFactory 2 | org.whispersystems.textsecuregcm.configuration.DatadogConfiguration 3 | org.whispersystems.textsecuregcm.configuration.DynamoDbClientFactory 4 | org.whispersystems.textsecuregcm.configuration.FaultTolerantRedisClusterFactory 5 | org.whispersystems.textsecuregcm.configuration.FaultTolerantRedisClientFactory 6 | org.whispersystems.textsecuregcm.configuration.PaymentsServiceClientsFactory 7 | org.whispersystems.textsecuregcm.configuration.PubSubPublisherFactory 8 | org.whispersystems.textsecuregcm.configuration.RegistrationServiceClientFactory 9 | org.whispersystems.textsecuregcm.configuration.S3ObjectMonitorFactory 10 | -------------------------------------------------------------------------------- /service/src/main/resources/META-INF/services/io.dropwizard.logging.common.AppenderFactory: -------------------------------------------------------------------------------- 1 | org.whispersystems.textsecuregcm.metrics.LogstashTcpSocketAppenderFactory 2 | -------------------------------------------------------------------------------- /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/main/resources/META-INF/services/io.dropwizard.metrics.common.ReporterFactory: -------------------------------------------------------------------------------- 1 | org.whispersystems.textsecuregcm.metrics.SignalDatadogReporterFactory 2 | -------------------------------------------------------------------------------- /service/src/main/resources/META-INF/services/org.whispersystems.textsecuregcm.configuration.AwsCredentialsProviderFactory: -------------------------------------------------------------------------------- 1 | org.whispersystems.textsecuregcm.configuration.StaticAwsCredentialsFactory 2 | -------------------------------------------------------------------------------- /service/src/main/resources/META-INF/validation.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | META-INF/validation/constraints-custom.xml 8 | 9 | -------------------------------------------------------------------------------- /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/resources/banner.txt: -------------------------------------------------------------------------------- 1 | _____ _ _ _____ 2 | / ___|(_) | |/ ___| 3 | \ `--. _ __ _ _ __ __ _ | |\ `--. ___ _ __ __ __ ___ _ __ 4 | `--. \| | / _` || '_ \ / _` || | `--. \ / _ \| '__|\ \ / // _ \| '__| 5 | /\__/ /| || (_| || | | || (_| || |/\__/ /| __/| | \ V /| __/| | 6 | \____/ |_| \__, ||_| |_| \__,_||_|\____/ \___||_| \_/ \___||_| 7 | __/ | 8 | |___/ 9 | 10 | -------------------------------------------------------------------------------- /service/src/main/resources/lua/account_database_crawler/unlock.lua: -------------------------------------------------------------------------------- 1 | -- keys: lock_key 2 | -- argv: lock_value 3 | 4 | if redis.call("GET", KEYS[1]) == ARGV[1] then 5 | return redis.call("DEL", KEYS[1]) 6 | else 7 | return 0 8 | end 9 | -------------------------------------------------------------------------------- /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/resources/lua/clear_presence.lua: -------------------------------------------------------------------------------- 1 | local presenceKey = KEYS[1] 2 | local presenceUuid = ARGV[1] 3 | 4 | if redis.call("GET", presenceKey) == presenceUuid then 5 | redis.call("DEL", presenceKey) 6 | return true 7 | end 8 | 9 | return false 10 | -------------------------------------------------------------------------------- /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/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/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 | -------------------------------------------------------------------------------- /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/resources/lua/renew_presence.lua: -------------------------------------------------------------------------------- 1 | local presenceKey = KEYS[1] 2 | local presenceUuid = ARGV[1] 3 | local expireSeconds = ARGV[2] 4 | 5 | if redis.call("GET", presenceKey) == presenceUuid then 6 | redis.call("EXPIRE", presenceKey, expireSeconds) 7 | end 8 | -------------------------------------------------------------------------------- /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/resources/org/signal/badges/Badges_en.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/signalapp/Signal-Server/981d929f506c25b18449442557940afc2ea2fca0/service/src/main/resources/org/signal/badges/Badges_en.properties -------------------------------------------------------------------------------- /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/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/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 | -------------------------------------------------------------------------------- /service/src/test/java/org/whispersystems/textsecuregcm/configuration/NoWaitDogstatsdConfiguration.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 java.time.Duration; 10 | 11 | @JsonTypeName("nowait") 12 | public class NoWaitDogstatsdConfiguration extends DogstatsdConfiguration { 13 | 14 | @Override 15 | public Duration getShutdownWaitDuration() { 16 | return Duration.ZERO; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /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/test/java/org/whispersystems/textsecuregcm/grpc/net/AbstractLeakDetectionTest.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.grpc.net; 2 | 3 | import io.netty.util.ResourceLeakDetector; 4 | import org.junit.jupiter.api.AfterAll; 5 | import org.junit.jupiter.api.BeforeAll; 6 | 7 | public abstract class AbstractLeakDetectionTest { 8 | 9 | private static ResourceLeakDetector.Level originalResourceLeakDetectorLevel; 10 | 11 | @BeforeAll 12 | static void setLeakDetectionLevel() { 13 | originalResourceLeakDetectorLevel = ResourceLeakDetector.getLevel(); 14 | ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.PARANOID); 15 | } 16 | 17 | @AfterAll 18 | static void restoreLeakDetectionLevel() { 19 | ResourceLeakDetector.setLevel(originalResourceLeakDetectorLevel); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /service/src/test/java/org/whispersystems/textsecuregcm/grpc/net/client/ClientErrorHandler.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.grpc.net.client; 2 | 3 | import io.netty.channel.ChannelHandlerContext; 4 | import io.netty.channel.ChannelInboundHandlerAdapter; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | public class ClientErrorHandler extends ChannelInboundHandlerAdapter { 9 | private static final Logger log = LoggerFactory.getLogger(ClientErrorHandler.class); 10 | 11 | @Override 12 | public void exceptionCaught(final ChannelHandlerContext context, final Throwable cause) { 13 | log.error("Caught inbound error in client; closing connection", cause); 14 | context.channel().close(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /service/src/test/java/org/whispersystems/textsecuregcm/grpc/net/client/FastOpenRequestBufferedEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.textsecuregcm.grpc.net.client; 6 | 7 | import io.netty.buffer.ByteBuf; 8 | 9 | public record FastOpenRequestBufferedEvent(ByteBuf fastOpenRequest) {} 10 | -------------------------------------------------------------------------------- /service/src/test/java/org/whispersystems/textsecuregcm/grpc/net/client/NoiseClientHandshakeCompleteEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.textsecuregcm.grpc.net.client; 6 | 7 | import org.whispersystems.textsecuregcm.grpc.net.NoiseTunnelProtos; 8 | 9 | import java.util.Optional; 10 | 11 | /** 12 | * A netty user event that indicates that the noise handshake finished successfully. 13 | * 14 | * @param fastResponse A response if the client included a request to send in the initiate handshake message payload and 15 | * the server included a payload in the handshake response. 16 | */ 17 | public record NoiseClientHandshakeCompleteEvent(NoiseTunnelProtos.HandshakeResponse handshakeResponse) {} 18 | -------------------------------------------------------------------------------- /service/src/test/java/org/whispersystems/textsecuregcm/grpc/net/client/ReadyForNoiseHandshakeEvent.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.textsecuregcm.grpc.net.client; 2 | 3 | public record ReadyForNoiseHandshakeEvent() { 4 | } 5 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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/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 org.whispersystems.websocket.ReusableAuth; 9 | import org.whispersystems.websocket.auth.PrincipalSupplier; 10 | 11 | public class TestPrincipal implements Principal { 12 | 13 | private final String name; 14 | 15 | private TestPrincipal(String name) { 16 | this.name = name; 17 | } 18 | 19 | @Override 20 | public String getName() { 21 | return name; 22 | } 23 | 24 | public static ReusableAuth reusableAuth(final String name) { 25 | return ReusableAuth.authenticated(new TestPrincipal(name), PrincipalSupplier.forImmutablePrincipal()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /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/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/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/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 | -------------------------------------------------------------------------------- /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/test/resources/META-INF/services/org.whispersystems.textsecuregcm.configuration.DatadogConfiguration: -------------------------------------------------------------------------------- 1 | org.whispersystems.textsecuregcm.configuration.NoWaitDogstatsdConfiguration 2 | -------------------------------------------------------------------------------- /service/src/test/resources/META-INF/services/org.whispersystems.textsecuregcm.configuration.DynamicConfigurationManagerFactory: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/signalapp/Signal-Server/981d929f506c25b18449442557940afc2ea2fca0/service/src/test/resources/META-INF/services/org.whispersystems.textsecuregcm.configuration.DynamicConfigurationManagerFactory -------------------------------------------------------------------------------- /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.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.PaymentsServiceClientsFactory: -------------------------------------------------------------------------------- 1 | org.whispersystems.textsecuregcm.configuration.StubPaymentsServiceClientsFactory 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.RegistrationServiceClientFactory: -------------------------------------------------------------------------------- 1 | org.whispersystems.textsecuregcm.configuration.StubRegistrationServiceClientFactory 2 | -------------------------------------------------------------------------------- /service/src/test/resources/META-INF/services/org.whispersystems.textsecuregcm.configuration.S3ObjectMonitorFactory: -------------------------------------------------------------------------------- 1 | org.whispersystems.textsecuregcm.configuration.StaticS3ObjectMonitorFactory 2 | -------------------------------------------------------------------------------- /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_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/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/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_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_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/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_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/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/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/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/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/test/resources/fixtures/mismatched_registration_id.json: -------------------------------------------------------------------------------- 1 | { 2 | "staleDevices" : [2] 3 | } -------------------------------------------------------------------------------- /service/src/test/resources/fixtures/missing_device_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "missingDevices" : [2, 3], 3 | "extraDevices" : [] 4 | } 5 | -------------------------------------------------------------------------------- /service/src/test/resources/fixtures/missing_device_response2.json: -------------------------------------------------------------------------------- 1 | { 2 | "missingDevices" : [2], 3 | "extraDevices" : [4] 4 | } 5 | -------------------------------------------------------------------------------- /service/src/test/resources/fixtures/prekey_v2.json: -------------------------------------------------------------------------------- 1 | { 2 | "keyId" : 1234, 3 | "publicKey" : "BQ+NbroQtVKyFaCSfqzSw8Wy72Ff22RSa5ERKTv5DIk2" 4 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /service/src/test/resources/org/whispersystems/textsecuregcm/storage/devicecheck/apple-sample-attestation: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/signalapp/Signal-Server/981d929f506c25b18449442557940afc2ea2fca0/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/981d929f506c25b18449442557940afc2ea2fca0/service/src/test/resources/org/whispersystems/textsecuregcm/storage/devicecheck/webauthn4j-sample-assertion -------------------------------------------------------------------------------- /service/src/test/resources/org/whispersystems/textsecuregcm/storage/devicecheck/webauthn4j-sample-attestation: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/signalapp/Signal-Server/981d929f506c25b18449442557940afc2ea2fca0/service/src/test/resources/org/whispersystems/textsecuregcm/storage/devicecheck/webauthn4j-sample-attestation -------------------------------------------------------------------------------- /service/src/test/resources/org/whispersystems/textsecuregcm/util/ip2asn-test.tsv: -------------------------------------------------------------------------------- 1 | 95865344 95865855 0 None Not routed 2 | 458051584 458227711 7552 VN VIETEL-AS-AP Viettel Group 3 | 843841536 844103679 7922 US COMCAST-7922 - Comcast Cable Communications, LLC 4 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 org.eclipse.jetty.websocket.server.JettyServerUpgradeRequest; 10 | import org.eclipse.jetty.websocket.server.JettyServerUpgradeResponse; 11 | import org.whispersystems.websocket.ReusableAuth; 12 | 13 | public interface AuthenticatedWebSocketUpgradeFilter { 14 | 15 | void handleAuthentication(ReusableAuth authenticated, 16 | JettyServerUpgradeRequest request, 17 | JettyServerUpgradeResponse response); 18 | } 19 | -------------------------------------------------------------------------------- /websocket-resources/src/main/java/org/whispersystems/websocket/auth/AuthenticationException.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 AuthenticationException extends Exception { 8 | 9 | public AuthenticationException(String s) { 10 | super(s); 11 | } 12 | 13 | public AuthenticationException(Exception e) { 14 | super(e); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /websocket-resources/src/main/java/org/whispersystems/websocket/auth/Mutable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.websocket.auth; 6 | 7 | import io.dropwizard.auth.Auth; 8 | 9 | import java.lang.annotation.ElementType; 10 | import java.lang.annotation.Retention; 11 | import java.lang.annotation.RetentionPolicy; 12 | import java.lang.annotation.Target; 13 | 14 | /** 15 | * An @{@link Auth} object annotated with {@link Mutable} indicates that the consumer of the object 16 | * will modify the object or its underlying canonical source. 17 | * 18 | * Note: An {@link Auth} object that does not specify @{@link ReadOnly} will be assumed to be @Mutable 19 | * 20 | * @see org.whispersystems.websocket.ReusableAuth 21 | */ 22 | @Retention(RetentionPolicy.RUNTIME) 23 | @Target({ElementType.FIELD, ElementType.PARAMETER}) 24 | public @interface Mutable { 25 | } 26 | 27 | -------------------------------------------------------------------------------- /websocket-resources/src/main/java/org/whispersystems/websocket/auth/WebSocketAuthenticator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Signal Messenger, LLC 3 | * SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | package org.whispersystems.websocket.auth; 6 | 7 | import java.security.Principal; 8 | import java.util.Optional; 9 | import org.eclipse.jetty.websocket.api.UpgradeRequest; 10 | import org.whispersystems.websocket.ReusableAuth; 11 | 12 | public interface WebSocketAuthenticator { 13 | ReusableAuth authenticate(UpgradeRequest request) throws AuthenticationException; 14 | } 15 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------