Remove unused SVR3 controller and client

This commit is contained in:
Chris Eager
2024-12-19 18:09:12 -06:00
committed by Chris Eager
parent a3e106fe04
commit 8280106493
29 changed files with 13 additions and 905 deletions

View File

@@ -52,7 +52,6 @@ import org.whispersystems.textsecuregcm.configuration.ReportMessageConfiguration
import org.whispersystems.textsecuregcm.configuration.S3ObjectMonitorFactory;
import org.whispersystems.textsecuregcm.configuration.SecureStorageServiceConfiguration;
import org.whispersystems.textsecuregcm.configuration.SecureValueRecovery2Configuration;
import org.whispersystems.textsecuregcm.configuration.SecureValueRecovery3Configuration;
import org.whispersystems.textsecuregcm.configuration.ShortCodeExpanderConfiguration;
import org.whispersystems.textsecuregcm.configuration.SpamFilterConfiguration;
import org.whispersystems.textsecuregcm.configuration.StripeConfiguration;
@@ -157,10 +156,6 @@ public class WhisperServerConfiguration extends Configuration {
@Valid
@JsonProperty
private SecureValueRecovery2Configuration svr2;
@NotNull
@Valid
@JsonProperty
private SecureValueRecovery3Configuration svr3;
@NotNull
@Valid
@@ -410,9 +405,6 @@ public class WhisperServerConfiguration extends Configuration {
public SecureValueRecovery2Configuration getSvr2Configuration() {
return svr2;
}
public SecureValueRecovery3Configuration getSvr3Configuration() {
return svr3;
}
public DirectoryV2Configuration getDirectoryV2Configuration() {
return directoryV2;

View File

@@ -131,7 +131,6 @@ import org.whispersystems.textsecuregcm.controllers.RegistrationController;
import org.whispersystems.textsecuregcm.controllers.RemoteConfigController;
import org.whispersystems.textsecuregcm.controllers.SecureStorageController;
import org.whispersystems.textsecuregcm.controllers.SecureValueRecovery2Controller;
import org.whispersystems.textsecuregcm.controllers.SecureValueRecovery3Controller;
import org.whispersystems.textsecuregcm.controllers.StickerController;
import org.whispersystems.textsecuregcm.controllers.SubscriptionController;
import org.whispersystems.textsecuregcm.controllers.VerificationController;
@@ -206,7 +205,6 @@ import org.whispersystems.textsecuregcm.s3.PolicySigner;
import org.whispersystems.textsecuregcm.s3.PostPolicyGenerator;
import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient;
import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecovery2Client;
import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecovery3Client;
import org.whispersystems.textsecuregcm.spam.ChallengeConstraintChecker;
import org.whispersystems.textsecuregcm.spam.RegistrationFraudChecker;
import org.whispersystems.textsecuregcm.spam.RegistrationRecoveryChecker;
@@ -481,8 +479,6 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
.maxThreads(32).minThreads(32).workQueue(fcmSenderQueue).build();
ExecutorService secureValueRecovery2ServiceExecutor = environment.lifecycle()
.executorService(name(getClass(), "secureValueRecoveryService2-%d")).maxThreads(1).minThreads(1).build();
ExecutorService secureValueRecovery3ServiceExecutor = environment.lifecycle()
.executorService(name(getClass(), "secureValueRecoveryService3-%d")).maxThreads(1).minThreads(1).build();
ExecutorService storageServiceExecutor = environment.lifecycle()
.executorService(name(getClass(), "storageService-%d")).maxThreads(1).minThreads(1).build();
ExecutorService virtualThreadEventLoggerExecutor = environment.lifecycle()
@@ -590,8 +586,6 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
config.getArtServiceConfiguration());
ExternalServiceCredentialsGenerator svr2CredentialsGenerator = SecureValueRecovery2Controller.credentialsGenerator(
config.getSvr2Configuration());
ExternalServiceCredentialsGenerator svr3CredentialsGenerator = SecureValueRecovery3Controller.credentialsGenerator(
config.getSvr3Configuration());
ExperimentEnrollmentManager experimentEnrollmentManager = new ExperimentEnrollmentManager(
dynamicConfigurationManager);
@@ -610,8 +604,6 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
keyTransparencyCallbackExecutor);
SecureValueRecovery2Client secureValueRecovery2Client = new SecureValueRecovery2Client(svr2CredentialsGenerator,
secureValueRecovery2ServiceExecutor, secureValueRecoveryServiceRetryExecutor, config.getSvr2Configuration());
SecureValueRecovery3Client secureValueRecovery3Client = new SecureValueRecovery3Client(svr3CredentialsGenerator,
secureValueRecovery3ServiceExecutor, secureValueRecoveryServiceRetryExecutor, config.getSvr3Configuration());
SecureStorageClient secureStorageClient = new SecureStorageClient(storageCredentialsGenerator,
storageServiceExecutor, storageServiceRetryExecutor, config.getSecureStorageServiceConfiguration());
DisconnectionRequestManager disconnectionRequestManager = new DisconnectionRequestManager(pubsubClient, disconnectionRequestListenerExecutor);
@@ -632,7 +624,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
new ClientPublicKeysManager(clientPublicKeys, accountLockManager, accountLockExecutor);
AccountsManager accountsManager = new AccountsManager(accounts, phoneNumberIdentifiers, cacheCluster,
pubsubClient, accountLockManager, keysManager, messagesManager, profilesManager,
secureStorageClient, secureValueRecovery2Client, secureValueRecovery3Client, disconnectionRequestManager,
secureStorageClient, secureValueRecovery2Client, disconnectionRequestManager,
registrationRecoveryPasswordsManager, clientPublicKeysManager, accountLockExecutor, messagePollExecutor,
clock, config.getLinkDeviceSecretConfiguration().secret().value(), dynamicConfigurationManager);
RemoteConfigsManager remoteConfigsManager = new RemoteConfigsManager(remoteConfigs);
@@ -667,8 +659,8 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
disconnectionRequestManager.addListener(webSocketConnectionEventManager);
final RegistrationLockVerificationManager registrationLockVerificationManager = new RegistrationLockVerificationManager(
accountsManager, disconnectionRequestManager, svr2CredentialsGenerator, svr3CredentialsGenerator,
registrationRecoveryPasswordsManager, pushNotificationManager, rateLimiters);
accountsManager, disconnectionRequestManager, svr2CredentialsGenerator, registrationRecoveryPasswordsManager,
pushNotificationManager, rateLimiters);
final ReportedMessageMetricsListener reportedMessageMetricsListener = new ReportedMessageMetricsListener(
accountsManager);
@@ -1144,7 +1136,6 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
new RemoteConfigController(remoteConfigsManager, config.getRemoteConfigConfiguration().globalConfig(), clock),
new SecureStorageController(storageCredentialsGenerator),
new SecureValueRecovery2Controller(svr2CredentialsGenerator, accountsManager),
new SecureValueRecovery3Controller(svr3CredentialsGenerator, accountsManager),
new StickerController(rateLimiters, config.getCdnConfiguration().credentials().accessKeyId().value(),
config.getCdnConfiguration().credentials().secretAccessKey().value(), config.getCdnConfiguration().region(),
config.getCdnConfiguration().bucket()),

View File

@@ -22,7 +22,6 @@ import org.apache.commons.lang3.StringUtils;
import org.whispersystems.textsecuregcm.controllers.RateLimitExceededException;
import org.whispersystems.textsecuregcm.entities.PhoneVerificationRequest;
import org.whispersystems.textsecuregcm.entities.RegistrationLockFailure;
import org.whispersystems.textsecuregcm.entities.Svr3Credentials;
import org.whispersystems.textsecuregcm.identity.IdentityType;
import org.whispersystems.textsecuregcm.limits.RateLimiters;
import org.whispersystems.textsecuregcm.metrics.UserAgentTagUtil;
@@ -56,7 +55,6 @@ public class RegistrationLockVerificationManager {
private final AccountsManager accounts;
private final DisconnectionRequestManager disconnectionRequestManager;
private final ExternalServiceCredentialsGenerator svr2CredentialGenerator;
private final ExternalServiceCredentialsGenerator svr3CredentialGenerator;
private final RateLimiters rateLimiters;
private final RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager;
private final PushNotificationManager pushNotificationManager;
@@ -65,14 +63,12 @@ public class RegistrationLockVerificationManager {
final AccountsManager accounts,
final DisconnectionRequestManager disconnectionRequestManager,
final ExternalServiceCredentialsGenerator svr2CredentialGenerator,
final ExternalServiceCredentialsGenerator svr3CredentialGenerator,
final RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager,
final PushNotificationManager pushNotificationManager,
final RateLimiters rateLimiters) {
this.accounts = accounts;
this.disconnectionRequestManager = disconnectionRequestManager;
this.svr2CredentialGenerator = svr2CredentialGenerator;
this.svr3CredentialGenerator = svr3CredentialGenerator;
this.registrationRecoveryPasswordsManager = registrationRecoveryPasswordsManager;
this.pushNotificationManager = pushNotificationManager;
this.rateLimiters = rateLimiters;
@@ -174,7 +170,7 @@ public class RegistrationLockVerificationManager {
.entity(new RegistrationLockFailure(
existingRegistrationLock.getTimeRemaining().toMillis(),
svr2FailureCredentials(existingRegistrationLock, updatedAccount),
svr3FailureCredentials(existingRegistrationLock, updatedAccount)))
null))
.build());
}
@@ -188,11 +184,4 @@ public class RegistrationLockVerificationManager {
return svr2CredentialGenerator.generateForUuid(account.getUuid());
}
private @Nullable Svr3Credentials svr3FailureCredentials(final StoredRegistrationLock existingRegistrationLock, final Account account) {
if (!existingRegistrationLock.needsFailureCredentials()) {
return null;
}
final ExternalServiceCredentials creds = svr3CredentialGenerator.generateForUuid(account.getUuid());
return new Svr3Credentials(creds.username(), creds.password(), account.getSvr3ShareSet());
}
}

View File

@@ -1,34 +0,0 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.configuration;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.util.List;
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
import org.whispersystems.textsecuregcm.util.ExactlySize;
public record SecureValueRecovery3Configuration(
@NotBlank String backend1Uri,
@NotBlank String backend2Uri,
@NotBlank String backend3Uri,
@ExactlySize(32) SecretBytes userAuthenticationTokenSharedSecret,
@ExactlySize(32) SecretBytes userIdTokenSharedSecret,
@NotEmpty List<@NotBlank String> svrCaCertificates,
@NotNull @Valid CircuitBreakerConfiguration circuitBreaker,
@NotNull @Valid RetryConfiguration retry) {
public SecureValueRecovery3Configuration {
if (circuitBreaker == null) {
circuitBreaker = new CircuitBreakerConfiguration();
}
if (retry == null) {
retry = new RetryConfiguration();
}
}
}

View File

@@ -1,161 +0,0 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.controllers;
import com.google.common.annotations.VisibleForTesting;
import io.dropwizard.auth.Auth;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import java.time.Clock;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import org.whispersystems.textsecuregcm.auth.AuthenticatedDevice;
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsSelector;
import org.whispersystems.textsecuregcm.configuration.SecureValueRecovery3Configuration;
import org.whispersystems.textsecuregcm.entities.AuthCheckRequest;
import org.whispersystems.textsecuregcm.entities.AuthCheckResponseV3;
import org.whispersystems.textsecuregcm.entities.SetShareSetRequest;
import org.whispersystems.textsecuregcm.entities.Svr3Credentials;
import org.whispersystems.textsecuregcm.limits.RateLimitedByIp;
import org.whispersystems.textsecuregcm.limits.RateLimiters;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.AccountsManager;
import org.whispersystems.textsecuregcm.util.Optionals;
import org.whispersystems.websocket.auth.Mutable;
import org.whispersystems.websocket.auth.ReadOnly;
@Path("/v3/backup")
@Tag(name = "Secure Value Recovery")
public class SecureValueRecovery3Controller {
private static final long MAX_AGE_SECONDS = TimeUnit.DAYS.toSeconds(30);
public static ExternalServiceCredentialsGenerator credentialsGenerator(final SecureValueRecovery3Configuration cfg) {
return credentialsGenerator(cfg, Clock.systemUTC());
}
@VisibleForTesting
public static ExternalServiceCredentialsGenerator credentialsGenerator(final SecureValueRecovery3Configuration cfg,
final Clock clock) {
return ExternalServiceCredentialsGenerator
.builder(cfg.userAuthenticationTokenSharedSecret())
.withUserDerivationKey(cfg.userIdTokenSharedSecret().value())
.prependUsername(false)
.withDerivedUsernameTruncateLength(16)
.withClock(clock)
.build();
}
private final ExternalServiceCredentialsGenerator backupServiceCredentialGenerator;
private final AccountsManager accountsManager;
public SecureValueRecovery3Controller(final ExternalServiceCredentialsGenerator backupServiceCredentialGenerator,
final AccountsManager accountsManager) {
this.backupServiceCredentialGenerator = backupServiceCredentialGenerator;
this.accountsManager = accountsManager;
}
@GET
@Path("/auth")
@Produces(MediaType.APPLICATION_JSON)
@Operation(
summary = "Generate credentials for SVR3",
description = """
Generate SVR3 service credentials. Generated credentials have an expiration time of 30 days
(however, the TTL is fully controlled by the server side and may change even for already generated credentials).
If a share-set has been previously set via /v3/backups/share-set, it will be included in the response
""")
@ApiResponse(responseCode = "200", description = "`JSON` with generated credentials and share-set", useReturnTypeSchema = true)
@ApiResponse(responseCode = "401", description = "Account authentication check failed.")
public Svr3Credentials getAuth(@ReadOnly @Auth final AuthenticatedDevice auth) {
final ExternalServiceCredentials creds = backupServiceCredentialGenerator.generateFor(
auth.getAccount().getUuid().toString());
return new Svr3Credentials(creds.username(), creds.password(), auth.getAccount().getSvr3ShareSet());
}
@PUT
@Path("/share-set")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Operation(
summary = "Set a share-set for the account",
description = """
Add a share-set to the account that can later be retrieved at v3/backups/auth or during registration. After
storing a value with SVR3, clients must store the returned share-set so the value can be restored later.
""")
@ApiResponse(responseCode = "204", description = "Successfully set share-set")
@ApiResponse(responseCode = "401", description = "Account authentication check failed.")
public void setShareSet(
@Mutable @Auth final AuthenticatedDevice auth,
@NotNull @Valid final SetShareSetRequest request) {
accountsManager.update(auth.getAccount(), account -> account.setSvr3ShareSet(request.shareSet()));
}
@POST
@Path("/auth/check")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@RateLimitedByIp(RateLimiters.For.BACKUP_AUTH_CHECK)
@Operation(
summary = "Check SVR3 credentials",
description = """
Over time, clients may wind up with multiple sets of SVR3 authentication credentials in cloud storage.
To determine which set is most current and should be used to communicate with SVR3 to retrieve a master key
(from which a registration recovery password can be derived), clients should call this endpoint
with a list of stored credentials. The response will identify which (if any) set of credentials are
appropriate for communicating with SVR3.
""")
@ApiResponse(responseCode = "200", description = "`JSON` with the check results.", useReturnTypeSchema = true)
@ApiResponse(responseCode = "422", description = "Provided list of SVR3 credentials could not be parsed")
@ApiResponse(responseCode = "400", description = "`POST` request body is not a valid `JSON`")
public AuthCheckResponseV3 authCheck(@NotNull @Valid final AuthCheckRequest request) {
final List<ExternalServiceCredentialsSelector.CredentialInfo> credentials = ExternalServiceCredentialsSelector.check(
request.tokens(),
backupServiceCredentialGenerator,
MAX_AGE_SECONDS);
final Optional<Account> account = accountsManager.getByE164(request.number());
// the username associated with the provided number
final Optional<String> matchingUsername = account
.map(Account::getUuid)
.map(backupServiceCredentialGenerator::generateForUuid)
.map(ExternalServiceCredentials::username);
return new AuthCheckResponseV3(credentials.stream().collect(Collectors.toMap(
ExternalServiceCredentialsSelector.CredentialInfo::token,
info -> {
if (!info.valid()) {
// This isn't a valid credential (could be for a different SVR service, expired, etc)
return AuthCheckResponseV3.Result.invalid();
}
final String credUsername = info.credentials().username();
return Optionals
// If the account exists, and the account's username matches this credential's username, return a match
.zipWith(account, matchingUsername.filter(credUsername::equals), (a, ignored) ->
AuthCheckResponseV3.Result.match(a.getSvr3ShareSet()))
// Otherwise, return no-match
.orElseGet(AuthCheckResponseV3.Result::noMatch);
}
)));
}
}

View File

@@ -6,6 +6,7 @@
package org.whispersystems.textsecuregcm.entities;
import io.swagger.v3.oas.annotations.media.Schema;
import javax.annotation.Nullable;
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
@Schema(description = """
@@ -16,7 +17,9 @@ public record RegistrationLockFailure(
@Schema(description = "Time remaining in milliseconds before the existing registration lock expires")
long timeRemaining,
@Schema(description = "Credentials that can be used with SVR2")
@Nullable
ExternalServiceCredentials svr2Credentials,
@Schema(description = "Credentials that can be used with SVR3")
Svr3Credentials svr3Credentials) {
@Deprecated
@Nullable
ExternalServiceCredentials svr3Credentials) {
}

View File

@@ -1,22 +0,0 @@
/*
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.entities;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import org.whispersystems.textsecuregcm.util.ByteArrayAdapter;
import org.whispersystems.textsecuregcm.util.ExactlySize;
public record SetShareSetRequest(
@Schema(description = """
A share-set generated by a client after storing a value in SVR3, serialized in un-padded standard base64
""", implementation = String.class)
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
@NotEmpty
@ExactlySize(SHARE_SET_SIZE)
byte[] shareSet) {
public static final int SHARE_SET_SIZE = 169;
}

View File

@@ -1,28 +0,0 @@
/*
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.entities;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import io.swagger.v3.oas.annotations.media.Schema;
import javax.annotation.Nullable;
import org.whispersystems.textsecuregcm.util.ByteArrayAdapter;
@Schema(description = """
A time limited external service credential that can be used to authenticate and restore from SVR3.
""")
public record Svr3Credentials(
@Schema(description = "The credential username")
String username,
@Schema(description = "The credential password")
String password,
@Schema(requiredMode = Schema.RequiredMode.NOT_REQUIRED, description = """
If present, a shareSet previously stored for this account via /v3/backups/shareSet. Required to restore a value
from SVR3. Encoded in standard un-padded base64.
""", implementation = String.class)
@JsonSerialize(using = ByteArrayAdapter.Serializing.class)
@Nullable byte[] shareSet) {}

View File

@@ -1,86 +0,0 @@
/*
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.securevaluerecovery;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.net.HttpHeaders;
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
import org.whispersystems.textsecuregcm.configuration.SecureValueRecovery3Configuration;
import org.whispersystems.textsecuregcm.http.FaultTolerantHttpClient;
import org.whispersystems.textsecuregcm.util.ExceptionUtils;
import org.whispersystems.textsecuregcm.util.HttpUtils;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.security.cert.CertificateException;
import java.time.Duration;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.stream.Stream;
import static org.whispersystems.textsecuregcm.util.HeaderUtils.basicAuthHeader;
/**
* A client for sending requests to Signal's secure value recovery v3 service on behalf of authenticated users.
*/
public class SecureValueRecovery3Client {
private final ExternalServiceCredentialsGenerator secureValueRecoveryCredentialsGenerator;
private final URI backend1Uri;
private final URI backend2Uri;
private final URI backend3Uri;
private final FaultTolerantHttpClient httpClient;
@VisibleForTesting
static final String DELETE_PATH = "/v1/delete";
public SecureValueRecovery3Client(final ExternalServiceCredentialsGenerator secureValueRecoveryCredentialsGenerator,
final Executor executor, final ScheduledExecutorService retryExecutor,
final SecureValueRecovery3Configuration configuration)
throws CertificateException {
this.secureValueRecoveryCredentialsGenerator = secureValueRecoveryCredentialsGenerator;
this.backend1Uri = URI.create(configuration.backend1Uri()).resolve(DELETE_PATH);
this.backend2Uri = URI.create(configuration.backend2Uri()).resolve(DELETE_PATH);
this.backend3Uri = URI.create(configuration.backend3Uri()).resolve(DELETE_PATH);
this.httpClient = FaultTolerantHttpClient.newBuilder()
.withCircuitBreaker(configuration.circuitBreaker())
.withRetry(configuration.retry())
.withRetryExecutor(retryExecutor)
.withVersion(HttpClient.Version.HTTP_1_1)
.withConnectTimeout(Duration.ofSeconds(10))
.withRedirect(HttpClient.Redirect.NEVER)
.withExecutor(executor)
.withName("secure-value-recovery3")
.withSecurityProtocol(FaultTolerantHttpClient.SECURITY_PROTOCOL_TLS_1_2)
.withTrustedServerCertificates(configuration.svrCaCertificates().toArray(String[]::new))
.build();
}
public CompletableFuture<Void> deleteBackups(final UUID accountUuid) {
final ExternalServiceCredentials credentials = secureValueRecoveryCredentialsGenerator.generateForUuid(accountUuid);
final List<CompletableFuture<HttpResponse<String>>> futures = Stream.of(backend1Uri, backend2Uri, backend3Uri)
.map(uri -> HttpRequest.newBuilder()
.uri(uri)
.DELETE()
.header(HttpHeaders.AUTHORIZATION, basicAuthHeader(credentials))
.build())
.map(request -> httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString()))
.toList();
return CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new))
.thenApply(ignored -> futures.stream().map(CompletableFuture::join).toList())
.thenAccept(responses -> responses.forEach(response -> {
if (!HttpUtils.isSuccessfulResponse(response.statusCode())) {
throw new SecureValueRecoveryException(String.format("Failed to delete backup in %s", response.uri()), String.valueOf(response.statusCode()));
}
}));
}
}

View File

@@ -100,19 +100,6 @@ public class Account {
@JsonProperty("inCds")
private boolean discoverableByPhoneNumber = true;
/**
* A share-set the account holder has stored.
*
* A share-set is generated when a client stores a value in SVR3, and should be stored here with the account. When
* they later want to recover the value, they need their share-set and their secret pin. The share-set is not a secret
* and, without the correct pin, is useless information.
*
* SVR3 share-sets are currently 167 bytes.
*/
@JsonProperty("svr3ss")
@Nullable
private byte[] svr3ShareSet;
@JsonProperty("bcr")
@Nullable
private byte[] messagesBackupCredentialRequest;
@@ -485,14 +472,6 @@ public class Account {
this.version = version;
}
public @Nullable byte[] getSvr3ShareSet() {
return svr3ShareSet;
}
public void setSvr3ShareSet(final byte[] svr3ShareSet) {
this.svr3ShareSet = svr3ShareSet;
}
public void setBackupCredentialRequests(final byte[] messagesBackupCredentialRequest,
final byte[] mediaBackupCredentialRequest) {

View File

@@ -315,10 +315,6 @@ public class Accounts {
existingAccount.getBackupCredentialRequest(BackupCredentialType.MESSAGES).orElse(null),
existingAccount.getBackupCredentialRequest(BackupCredentialType.MEDIA).orElse(null));
// Carry over the old SVR3 share-set. This is required for an account to restore information from SVR. The share-
// set is not a secret, if the new account claimer does not have the SVR3 pin, it is useless.
accountToCreate.setSvr3ShareSet(existingAccount.getSvr3ShareSet());
final List<TransactWriteItem> writeItems = new ArrayList<>();
// If we're reclaiming an account that already has a username, we'd like to give the re-registering client

View File

@@ -82,7 +82,6 @@ import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisClient;
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisClusterClient;
import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient;
import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecovery2Client;
import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecovery3Client;
import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecoveryException;
import org.whispersystems.textsecuregcm.util.DestinationDeviceValidator;
import org.whispersystems.textsecuregcm.util.ExceptionUtils;
@@ -126,7 +125,6 @@ public class AccountsManager extends RedisPubSubAdapter<String, String> implemen
private final ProfilesManager profilesManager;
private final SecureStorageClient secureStorageClient;
private final SecureValueRecovery2Client secureValueRecovery2Client;
private final SecureValueRecovery3Client secureValueRecovery3Client;
private final DisconnectionRequestManager disconnectionRequestManager;
private final RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager;
@@ -210,7 +208,6 @@ public class AccountsManager extends RedisPubSubAdapter<String, String> implemen
final ProfilesManager profilesManager,
final SecureStorageClient secureStorageClient,
final SecureValueRecovery2Client secureValueRecovery2Client,
final SecureValueRecovery3Client secureValueRecovery3Client,
final DisconnectionRequestManager disconnectionRequestManager,
final RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager,
final ClientPublicKeysManager clientPublicKeysManager,
@@ -229,7 +226,6 @@ public class AccountsManager extends RedisPubSubAdapter<String, String> implemen
this.profilesManager = profilesManager;
this.secureStorageClient = secureStorageClient;
this.secureValueRecovery2Client = secureValueRecovery2Client;
this.secureValueRecovery3Client = secureValueRecovery3Client;
this.disconnectionRequestManager = disconnectionRequestManager;
this.registrationRecoveryPasswordsManager = requireNonNull(registrationRecoveryPasswordsManager);
this.clientPublicKeysManager = clientPublicKeysManager;
@@ -1264,16 +1260,9 @@ public class AccountsManager extends RedisPubSubAdapter<String, String> implemen
throw new CompletionException(exception);
}));
final CompletableFuture<Void> svr3DeleteBackupFuture = secureValueRecovery3Client.deleteBackups(account.getUuid())
.exceptionally(exception -> {
// We don't care about errors from SVR3 because we're not currently using it
return null;
});
return CompletableFuture.allOf(
secureStorageClient.deleteStoredData(account.getUuid()),
svr2DeleteBackupFuture,
svr3DeleteBackupFuture,
keysManager.deleteSingleUsePreKeys(account.getUuid()),
keysManager.deleteSingleUsePreKeys(account.getPhoneNumberIdentifier()),
messagesManager.clear(account.getUuid()),

View File

@@ -32,7 +32,6 @@ import org.whispersystems.textsecuregcm.backup.Cdn3RemoteStorageManager;
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
import org.whispersystems.textsecuregcm.controllers.SecureStorageController;
import org.whispersystems.textsecuregcm.controllers.SecureValueRecovery2Controller;
import org.whispersystems.textsecuregcm.controllers.SecureValueRecovery3Controller;
import org.whispersystems.textsecuregcm.experiment.PushNotificationExperimentSamples;
import org.whispersystems.textsecuregcm.limits.RateLimiters;
import org.whispersystems.textsecuregcm.metrics.MicrometerAwsSdkMetricPublisher;
@@ -45,7 +44,6 @@ import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisClient;
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisClusterClient;
import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient;
import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecovery2Client;
import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecovery3Client;
import org.whispersystems.textsecuregcm.storage.AccountLockManager;
import org.whispersystems.textsecuregcm.storage.Accounts;
import org.whispersystems.textsecuregcm.storage.AccountsManager;
@@ -161,8 +159,6 @@ record CommandDependencies(
configuration.getSecureStorageServiceConfiguration());
ExternalServiceCredentialsGenerator secureValueRecovery2CredentialsGenerator = SecureValueRecovery2Controller.credentialsGenerator(
configuration.getSvr2Configuration());
ExternalServiceCredentialsGenerator secureValueRecovery3CredentialsGenerator = SecureValueRecovery3Controller.credentialsGenerator(
configuration.getSvr3Configuration());
final ExecutorService awsSdkMetricsExecutor = environment.lifecycle()
.virtualExecutorService(MetricRegistry.name(CommandDependencies.class, "awsSdkMetrics-%d"));
@@ -215,10 +211,6 @@ record CommandDependencies(
secureValueRecovery2CredentialsGenerator, secureValueRecoveryServiceExecutor,
secureValueRecoveryServiceRetryExecutor,
configuration.getSvr2Configuration());
SecureValueRecovery3Client secureValueRecovery3Client = new SecureValueRecovery3Client(
secureValueRecovery3CredentialsGenerator, secureValueRecoveryServiceExecutor,
secureValueRecoveryServiceRetryExecutor,
configuration.getSvr3Configuration());
SecureStorageClient secureStorageClient = new SecureStorageClient(storageCredentialsGenerator,
storageServiceExecutor, storageServiceRetryExecutor, configuration.getSecureStorageServiceConfiguration());
DisconnectionRequestManager disconnectionRequestManager = new DisconnectionRequestManager(pubsubClient, disconnectionRequestListenerExecutor);
@@ -240,7 +232,7 @@ record CommandDependencies(
new RegistrationRecoveryPasswordsManager(registrationRecoveryPasswords);
AccountsManager accountsManager = new AccountsManager(accounts, phoneNumberIdentifiers, cacheCluster,
pubsubClient, accountLockManager, keys, messagesManager, profilesManager,
secureStorageClient, secureValueRecovery2Client, secureValueRecovery3Client, disconnectionRequestManager,
secureStorageClient, secureValueRecovery2Client, disconnectionRequestManager,
registrationRecoveryPasswordsManager, clientPublicKeysManager, accountLockExecutor, messagePollExecutor,
clock, configuration.getLinkDeviceSecretConfiguration().secret().value(), dynamicConfigurationManager);
RateLimiters rateLimiters = RateLimiters.createAndValidate(configuration.getLimitsConfiguration(),