diff --git a/app/src/main/java/org/thoughtcrime/securesms/dependencies/AppDependencies.kt b/app/src/main/java/org/thoughtcrime/securesms/dependencies/AppDependencies.kt index 786b944cb9..112a7ebfd0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/dependencies/AppDependencies.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/dependencies/AppDependencies.kt @@ -361,7 +361,7 @@ object AppDependencies { interface Provider { fun providePushServiceSocket(signalServiceConfiguration: SignalServiceConfiguration, groupsV2Operations: GroupsV2Operations): PushServiceSocket fun provideGroupsV2Operations(signalServiceConfiguration: SignalServiceConfiguration): GroupsV2Operations - fun provideSignalServiceAccountManager(authWebSocket: AccountApi, pushServiceSocket: PushServiceSocket, groupsV2Operations: GroupsV2Operations): SignalServiceAccountManager + fun provideSignalServiceAccountManager(authWebSocket: SignalWebSocket.AuthenticatedWebSocket, accountApi: AccountApi, pushServiceSocket: PushServiceSocket, groupsV2Operations: GroupsV2Operations): SignalServiceAccountManager fun provideSignalServiceMessageSender(authWebSocket: SignalWebSocket.AuthenticatedWebSocket, protocolStore: SignalServiceDataStore, pushServiceSocket: PushServiceSocket, messageApi: MessageApi): SignalServiceMessageSender fun provideSignalServiceMessageReceiver(pushServiceSocket: PushServiceSocket): SignalServiceMessageReceiver fun provideSignalServiceNetworkAccess(): SignalServiceNetworkAccess diff --git a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java index 76dcc2f73b..27eeee8cde 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java +++ b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java @@ -146,8 +146,8 @@ public class ApplicationDependencyProvider implements AppDependencies.Provider { } @Override - public @NonNull SignalServiceAccountManager provideSignalServiceAccountManager(@NonNull AccountApi accountApi, @NonNull PushServiceSocket pushServiceSocket, @NonNull GroupsV2Operations groupsV2Operations) { - return new SignalServiceAccountManager(accountApi, pushServiceSocket, groupsV2Operations); + public @NonNull SignalServiceAccountManager provideSignalServiceAccountManager(@NonNull SignalWebSocket.AuthenticatedWebSocket authWebSocket, @NonNull AccountApi accountApi, @NonNull PushServiceSocket pushServiceSocket, @NonNull GroupsV2Operations groupsV2Operations) { + return new SignalServiceAccountManager(authWebSocket, accountApi, pushServiceSocket, groupsV2Operations); } @Override diff --git a/app/src/main/java/org/thoughtcrime/securesms/dependencies/NetworkDependenciesModule.kt b/app/src/main/java/org/thoughtcrime/securesms/dependencies/NetworkDependenciesModule.kt index 8f6606396c..67dd2022a3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/dependencies/NetworkDependenciesModule.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/dependencies/NetworkDependenciesModule.kt @@ -95,7 +95,7 @@ class NetworkDependenciesModule( } val signalServiceAccountManager: SignalServiceAccountManager by lazy { - provider.provideSignalServiceAccountManager(accountApi, pushServiceSocket, groupsV2Operations) + provider.provideSignalServiceAccountManager(authWebSocket, accountApi, pushServiceSocket, groupsV2Operations) } val libsignalNetwork: Network by lazy { diff --git a/app/src/test/java/org/thoughtcrime/securesms/dependencies/MockApplicationDependencyProvider.kt b/app/src/test/java/org/thoughtcrime/securesms/dependencies/MockApplicationDependencyProvider.kt index 31c2a2cde7..3d0e64bbfe 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/dependencies/MockApplicationDependencyProvider.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/dependencies/MockApplicationDependencyProvider.kt @@ -66,7 +66,7 @@ class MockApplicationDependencyProvider : AppDependencies.Provider { return mockk(relaxed = true) } - override fun provideSignalServiceAccountManager(authWebSocket: AccountApi, pushServiceSocket: PushServiceSocket, groupsV2Operations: GroupsV2Operations): SignalServiceAccountManager { + override fun provideSignalServiceAccountManager(authWebSocket: SignalWebSocket.AuthenticatedWebSocket, accountApi: AccountApi, pushServiceSocket: PushServiceSocket, groupsV2Operations: GroupsV2Operations): SignalServiceAccountManager { return mockk(relaxed = true) } diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceAccountManager.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceAccountManager.java index b202030f97..c83be817e9 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceAccountManager.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceAccountManager.java @@ -17,12 +17,9 @@ import org.whispersystems.signalservice.api.crypto.SealedSenderAccess; import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations; import org.whispersystems.signalservice.api.groupsv2.GroupsV2Api; import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations; -import org.whispersystems.signalservice.api.messages.calls.TurnServerInfo; -import org.whispersystems.signalservice.api.payments.CurrencyConversions; import org.whispersystems.signalservice.api.profiles.AvatarUploadParams; import org.whispersystems.signalservice.api.profiles.ProfileAndCredential; import org.whispersystems.signalservice.api.profiles.SignalServiceProfileWrite; -import org.whispersystems.signalservice.api.push.ServiceId; import org.whispersystems.signalservice.api.push.ServiceId.ACI; import org.whispersystems.signalservice.api.push.ServiceId.PNI; import org.whispersystems.signalservice.api.push.ServiceIdType; @@ -31,8 +28,8 @@ import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException import org.whispersystems.signalservice.api.registration.RegistrationApi; import org.whispersystems.signalservice.api.svr.SecureValueRecoveryV2; import org.whispersystems.signalservice.api.svr.SecureValueRecoveryV3; +import org.whispersystems.signalservice.api.websocket.SignalWebSocket; import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration; -import org.whispersystems.signalservice.internal.push.AuthCredentials; import org.whispersystems.signalservice.internal.push.OneTimePreKeyCounts; import org.whispersystems.signalservice.internal.push.PaymentAddress; import org.whispersystems.signalservice.internal.push.ProfileAvatarData; @@ -43,7 +40,6 @@ import org.whispersystems.signalservice.internal.push.http.ProfileCipherOutputSt import org.whispersystems.signalservice.internal.util.StaticCredentialsProvider; import java.io.IOException; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -53,6 +49,9 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + /** * The main interface for creating, registering, and * managing a Signal Service account. @@ -63,10 +62,11 @@ public class SignalServiceAccountManager { private static final String TAG = SignalServiceAccountManager.class.getSimpleName(); - private final PushServiceSocket pushServiceSocket; - private final GroupsV2Operations groupsV2Operations; - private final SignalServiceConfiguration configuration; - private final AccountApi accountApi; + private final PushServiceSocket pushServiceSocket; + private final GroupsV2Operations groupsV2Operations; + private final SignalServiceConfiguration configuration; + private final SignalWebSocket.AuthenticatedWebSocket authWebSocket; + private final AccountApi accountApi; /** * Construct a SignalServiceAccountManager. @@ -91,13 +91,18 @@ public class SignalServiceAccountManager { GroupsV2Operations gv2Operations = new GroupsV2Operations(ClientZkOperations.create(configuration), maxGroupSize); return new SignalServiceAccountManager( + null, null, new PushServiceSocket(configuration, credentialProvider, signalAgent, gv2Operations.getProfileOperations(), automaticNetworkRetry), gv2Operations ); } - public SignalServiceAccountManager(AccountApi accountApi, PushServiceSocket pushServiceSocket, GroupsV2Operations groupsV2Operations) { + public SignalServiceAccountManager(@Nullable SignalWebSocket.AuthenticatedWebSocket authWebSocket, + @Nullable AccountApi accountApi, + @Nonnull PushServiceSocket pushServiceSocket, + @Nonnull GroupsV2Operations groupsV2Operations) { + this.authWebSocket = authWebSocket; this.accountApi = accountApi; this.groupsV2Operations = groupsV2Operations; this.pushServiceSocket = pushServiceSocket; @@ -113,11 +118,11 @@ public class SignalServiceAccountManager { } public SecureValueRecoveryV2 getSecureValueRecoveryV2(String mrEnclave) { - return new SecureValueRecoveryV2(configuration, mrEnclave, pushServiceSocket); + return new SecureValueRecoveryV2(configuration, mrEnclave, authWebSocket); } public SecureValueRecoveryV3 getSecureValueRecoveryV3(Network network) { - return new SecureValueRecoveryV3(network, pushServiceSocket); + return new SecureValueRecoveryV3(network, authWebSocket); } public WhoAmIResponse getWhoAmI() throws IOException { @@ -243,7 +248,7 @@ public class SignalServiceAccountManager { } public GroupsV2Api getGroupsV2Api() { - return new GroupsV2Api(pushServiceSocket, groupsV2Operations); + return new GroupsV2Api(authWebSocket, pushServiceSocket, groupsV2Operations); } public RegistrationApi getRegistrationApi() { diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/groupsv2/GroupsV2Api.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/groupsv2/GroupsV2Api.java index 40f7726311..a7d8e2bc15 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/groupsv2/GroupsV2Api.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/groupsv2/GroupsV2Api.java @@ -25,6 +25,7 @@ import org.signal.storageservice.protos.groups.local.DecryptedGroupJoinInfo; import org.whispersystems.signalservice.api.NetworkResult; import org.whispersystems.signalservice.api.push.ServiceId.ACI; import org.whispersystems.signalservice.api.push.ServiceId.PNI; +import org.whispersystems.signalservice.api.websocket.SignalWebSocket; import org.whispersystems.signalservice.internal.push.PushServiceSocket; import org.whispersystems.signalservice.internal.push.exceptions.ForbiddenException; @@ -43,10 +44,12 @@ import okio.ByteString; public class GroupsV2Api { - private final PushServiceSocket socket; - private final GroupsV2Operations groupsOperations; + private final SignalWebSocket.AuthenticatedWebSocket authWebSocket; + private final PushServiceSocket socket; + private final GroupsV2Operations groupsOperations; - public GroupsV2Api(PushServiceSocket socket, GroupsV2Operations groupsOperations) { + public GroupsV2Api(SignalWebSocket.AuthenticatedWebSocket authWebSocket, PushServiceSocket socket, GroupsV2Operations groupsOperations) { + this.authWebSocket = authWebSocket; this.socket = socket; this.groupsOperations = groupsOperations; } @@ -54,10 +57,8 @@ public class GroupsV2Api { /** * Provides 7 days of credentials, which you should cache. */ - public CredentialResponseMaps getCredentials(long todaySeconds) - throws IOException - { - return parseCredentialResponse(socket.retrieveGroupsV2Credentials(todaySeconds)); + public CredentialResponseMaps getCredentials(long todaySeconds) throws IOException { + return parseCredentialResponse(GroupsV2ApiHelper.getCredentials(authWebSocket, todaySeconds)); } /** diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/groupsv2/GroupsV2ApiHelper.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/groupsv2/GroupsV2ApiHelper.kt new file mode 100644 index 0000000000..a5b9647da1 --- /dev/null +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/groupsv2/GroupsV2ApiHelper.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api.groupsv2 + +import org.whispersystems.signalservice.api.NetworkResult +import org.whispersystems.signalservice.api.websocket.SignalWebSocket +import org.whispersystems.signalservice.internal.get +import org.whispersystems.signalservice.internal.websocket.WebSocketRequestMessage +import java.io.IOException +import kotlin.time.Duration.Companion.days + +/** + * Allow [GroupsV2Api] to have a partial kotlin conversion by putting more kotlin friendly calls here. + */ +object GroupsV2ApiHelper { + /** + * Provides 7 days of credentials, which you should cache. + * + * GET /v1/certificate/auth/group?redemptionStartSeconds=[todaySeconds]&redemptionEndSeconds=`todaySecondsPlus7DaysOfSeconds` + * - 200: Success + */ + @JvmStatic + @Throws(IOException::class) + fun getCredentials(authWebSocket: SignalWebSocket.AuthenticatedWebSocket, todaySeconds: Long): CredentialResponse { + val todayPlus7 = todaySeconds + 7.days.inWholeSeconds + val request = WebSocketRequestMessage.get("/v1/certificate/auth/group?redemptionStartSeconds=$todaySeconds&redemptionEndSeconds=$todayPlus7") + return NetworkResult.fromWebSocketRequest(authWebSocket, request, CredentialResponse::class).successOrThrow() + } +} diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/svr/SecureValueRecoveryV2.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/svr/SecureValueRecoveryV2.kt index 2aacf44bf7..95e5962616 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/svr/SecureValueRecoveryV2.kt +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/svr/SecureValueRecoveryV2.kt @@ -11,6 +11,7 @@ import org.signal.svr2.proto.DeleteRequest import org.signal.svr2.proto.ExposeRequest import org.signal.svr2.proto.Request import org.signal.svr2.proto.RestoreRequest +import org.whispersystems.signalservice.api.NetworkResult import org.whispersystems.signalservice.api.crypto.InvalidCiphertextException import org.whispersystems.signalservice.api.kbs.MasterKey import org.whispersystems.signalservice.api.kbs.PinHashUtil @@ -21,11 +22,13 @@ import org.whispersystems.signalservice.api.svr.SecureValueRecovery.InvalidReque import org.whispersystems.signalservice.api.svr.SecureValueRecovery.PinChangeSession import org.whispersystems.signalservice.api.svr.SecureValueRecovery.RestoreResponse import org.whispersystems.signalservice.api.svr.SecureValueRecovery.SvrVersion +import org.whispersystems.signalservice.api.websocket.SignalWebSocket import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration +import org.whispersystems.signalservice.internal.get import org.whispersystems.signalservice.internal.push.AuthCredentials -import org.whispersystems.signalservice.internal.push.PushServiceSocket import org.whispersystems.signalservice.internal.util.Hex import org.whispersystems.signalservice.internal.util.JsonUtil +import org.whispersystems.signalservice.internal.websocket.WebSocketRequestMessage import java.io.IOException import org.signal.svr2.proto.BackupResponse as ProtoBackupResponse import org.signal.svr2.proto.ExposeResponse as ProtoExposeResponse @@ -37,7 +40,7 @@ import org.signal.svr2.proto.RestoreResponse as ProtoRestoreResponse class SecureValueRecoveryV2( private val serviceConfiguration: SignalServiceConfiguration, private val mrEnclave: String, - private val pushServiceSocket: PushServiceSocket + private val authWebSocket: SignalWebSocket.AuthenticatedWebSocket ) : SecureValueRecovery { companion object { @@ -92,7 +95,8 @@ class SecureValueRecoveryV2( @Throws(IOException::class) override fun authorization(): AuthCredentials { - return pushServiceSocket.svr2Authorization + val request = WebSocketRequestMessage.get("/v2/backup/auth") + return NetworkResult.fromWebSocketRequest(authWebSocket, request, AuthCredentials::class).successOrThrow() } override fun toString(): String { diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/svr/SecureValueRecoveryV3.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/svr/SecureValueRecoveryV3.kt index d152a2f015..786159ecd3 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/svr/SecureValueRecoveryV3.kt +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/svr/SecureValueRecoveryV3.kt @@ -10,6 +10,7 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize import com.fasterxml.jackson.databind.annotation.JsonSerialize import org.signal.core.util.logging.Log import org.signal.libsignal.net.Network +import org.whispersystems.signalservice.api.NetworkResult import org.whispersystems.signalservice.api.kbs.MasterKey import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException import org.whispersystems.signalservice.api.svr.SecureValueRecovery.BackupResponse @@ -17,11 +18,13 @@ import org.whispersystems.signalservice.api.svr.SecureValueRecovery.DeleteRespon import org.whispersystems.signalservice.api.svr.SecureValueRecovery.PinChangeSession import org.whispersystems.signalservice.api.svr.SecureValueRecovery.RestoreResponse import org.whispersystems.signalservice.api.svr.SecureValueRecovery.SvrVersion +import org.whispersystems.signalservice.api.websocket.SignalWebSocket +import org.whispersystems.signalservice.internal.get import org.whispersystems.signalservice.internal.push.AuthCredentials import org.whispersystems.signalservice.internal.push.ByteArrayDeserializerBase64 import org.whispersystems.signalservice.internal.push.ByteArraySerializerBase64NoPadding -import org.whispersystems.signalservice.internal.push.PushServiceSocket import org.whispersystems.signalservice.internal.util.JsonUtil +import org.whispersystems.signalservice.internal.websocket.WebSocketRequestMessage import java.io.IOException /** @@ -29,7 +32,7 @@ import java.io.IOException */ class SecureValueRecoveryV3( private val network: Network, - private val pushServiceSocket: PushServiceSocket + private val authWebSocket: SignalWebSocket.AuthenticatedWebSocket ) : SecureValueRecovery { companion object { @@ -61,7 +64,7 @@ class SecureValueRecoveryV3( override fun restoreDataPostRegistration(userPin: String): RestoreResponse { val authorization: Svr3Credentials = try { - pushServiceSocket.svr3Authorization + svr3Authorization().successOrThrow() } catch (e: NonSuccessfulResponseCodeException) { return RestoreResponse.ApplicationError(e) } catch (e: IOException) { @@ -110,7 +113,12 @@ class SecureValueRecoveryV3( @Throws(IOException::class) override fun authorization(): AuthCredentials { - return pushServiceSocket.svr3Authorization.toAuthCredential() + return svr3Authorization().successOrThrow().toAuthCredential() + } + + private fun svr3Authorization(): NetworkResult { + val request = WebSocketRequestMessage.get("/v3/backup/auth") + return NetworkResult.fromWebSocketRequest(authWebSocket, request, Svr3Credentials::class) } override fun toString(): String { diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java index a5c21a7cb4..941fcdbdb1 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java @@ -43,7 +43,6 @@ import org.whispersystems.signalservice.api.account.AccountAttributes; import org.whispersystems.signalservice.api.account.PreKeyCollection; import org.whispersystems.signalservice.api.account.PreKeyUpload; import org.whispersystems.signalservice.api.crypto.SealedSenderAccess; -import org.whispersystems.signalservice.api.groupsv2.CredentialResponse; import org.whispersystems.signalservice.api.groupsv2.GroupsV2AuthorizationString; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment.ProgressListener; import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentRemoteId; @@ -51,7 +50,6 @@ import org.whispersystems.signalservice.api.messages.calls.CallingResponse; import org.whispersystems.signalservice.api.profiles.ProfileAndCredential; import org.whispersystems.signalservice.api.profiles.SignalServiceProfile; import org.whispersystems.signalservice.api.profiles.SignalServiceProfileWrite; -import org.whispersystems.signalservice.api.push.ServiceId; import org.whispersystems.signalservice.api.push.ServiceId.ACI; import org.whispersystems.signalservice.api.push.ServiceIdType; import org.whispersystems.signalservice.api.push.SignalServiceAddress; @@ -218,7 +216,6 @@ public class PushServiceSocket { private static final String STICKER_MANIFEST_PATH = "stickers/%s/manifest.proto"; private static final String STICKER_PATH = "stickers/%s/full/%d"; - private static final String GROUPSV2_CREDENTIAL = "/v1/certificate/auth/group?redemptionStartSeconds=%d&redemptionEndSeconds=%d"; private static final String GROUPSV2_GROUP = "/v2/groups/"; private static final String GROUPSV2_GROUP_PASSWORD = "/v2/groups/?inviteLinkPassword=%s"; private static final String GROUPSV2_GROUP_CHANGES = "/v2/groups/logs/%s?maxSupportedChangeEpoch=%d&includeFirstState=%s&includeLastState=false"; @@ -251,9 +248,6 @@ public class PushServiceSocket { private static final String REGISTRATION_PATH = "/v1/registration"; - private static final String SVR2_AUTH = "/v2/backup/auth"; - private static final String SVR3_AUTH = "/v3/backup/auth"; - private static final String BACKUP_AUTH_CHECK_V2 = "/v2/backup/auth/check"; private static final String BACKUP_AUTH_CHECK_V3 = "/v3/backup/auth/check"; @@ -416,18 +410,6 @@ public class PushServiceSocket { return JsonUtil.fromJson(response, VerifyAccountResponse.class); } - public AuthCredentials getSvr2Authorization() throws IOException { - String body = makeServiceRequest(SVR2_AUTH, "GET", null); - AuthCredentials credentials = JsonUtil.fromJsonResponse(body, AuthCredentials.class); - - return credentials; - } - - public Svr3Credentials getSvr3Authorization() throws IOException { - String body = makeServiceRequest(SVR3_AUTH, "GET", null); - return JsonUtil.fromJsonResponse(body, Svr3Credentials.class); - } - public void setRestoreMethodChosen(@Nonnull String token, @Nonnull RestoreMethodBody request) throws IOException { String body = JsonUtil.toJson(request); makeServiceRequest(String.format(Locale.US, SET_RESTORE_METHOD_PATH, urlEncode(token)), "PUT", body, NO_HEADERS, UNOPINIONATED_HANDLER, SealedSenderAccess.NONE); @@ -2314,20 +2296,6 @@ public class PushServiceSocket { } } - public CredentialResponse retrieveGroupsV2Credentials(long todaySeconds) - throws IOException - { - long todayPlus7 = todaySeconds + TimeUnit.DAYS.toSeconds(7); - String response = makeServiceRequest(String.format(Locale.US, GROUPSV2_CREDENTIAL, todaySeconds, todayPlus7), - "GET", - null, - NO_HEADERS, - NO_HANDLER, - SealedSenderAccess.NONE); - - return JsonUtil.fromJson(response, CredentialResponse.class); - } - private static final ResponseCodeHandler GROUPS_V2_PUT_RESPONSE_HANDLER = (responseCode, body, getHeader) -> { if (getHeader.apply("X-Signal-Timestamp") == null) { throw new NonSuccessfulResponseCodeException(500, "Missing timestamp header");