Add SecureValueRecoveryBController
This commit is contained in:
parent
7d41c1219b
commit
ae2d98750c
|
@ -16,6 +16,9 @@ directoryV2.client.userIdTokenSharedSecret: bbcdefghijklmnopqrstuvwxyz0123456789
|
||||||
svr2.userAuthenticationTokenSharedSecret: abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with SVR2 to generate auth tokens for Signal users
|
svr2.userAuthenticationTokenSharedSecret: abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with SVR2 to generate auth tokens for Signal users
|
||||||
svr2.userIdTokenSharedSecret: bbcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with SVR2 to generate auth identity tokens for Signal users
|
svr2.userIdTokenSharedSecret: bbcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with SVR2 to generate auth identity tokens for Signal users
|
||||||
|
|
||||||
|
svrb.userAuthenticationTokenSharedSecret: abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with SVRB to generate auth tokens for Signal users
|
||||||
|
svrb.userIdTokenSharedSecret: bbcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with SVRB to generate auth identity tokens for Signal users
|
||||||
|
|
||||||
tus.userAuthenticationTokenSharedSecret: abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG=
|
tus.userAuthenticationTokenSharedSecret: abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG=
|
||||||
|
|
||||||
gcpAttachments.rsaSigningKey: |
|
gcpAttachments.rsaSigningKey: |
|
||||||
|
|
|
@ -225,6 +225,34 @@ svr2:
|
||||||
AAAAAAAAAAAAAAAAAAAA
|
AAAAAAAAAAAAAAAAAAAA
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
|
svrb:
|
||||||
|
uri: svrb.example.com
|
||||||
|
userAuthenticationTokenSharedSecret: secret://svrb.userAuthenticationTokenSharedSecret
|
||||||
|
userIdTokenSharedSecret: secret://svrb.userIdTokenSharedSecret
|
||||||
|
svrCaCertificates:
|
||||||
|
- |
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||||
|
AAAAAAAAAAAAAAAAAAAA
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
messageCache: # Redis server configuration for message store cache
|
messageCache: # Redis server configuration for message store cache
|
||||||
persistDelayMinutes: 1
|
persistDelayMinutes: 1
|
||||||
cluster:
|
cluster:
|
||||||
|
|
|
@ -52,7 +52,7 @@ import org.whispersystems.textsecuregcm.configuration.RemoteConfigConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.ReportMessageConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.ReportMessageConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.S3ObjectMonitorFactory;
|
import org.whispersystems.textsecuregcm.configuration.S3ObjectMonitorFactory;
|
||||||
import org.whispersystems.textsecuregcm.configuration.SecureStorageServiceConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.SecureStorageServiceConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.SecureValueRecovery2Configuration;
|
import org.whispersystems.textsecuregcm.configuration.SecureValueRecoveryConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.ShortCodeExpanderConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.ShortCodeExpanderConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.SpamFilterConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.SpamFilterConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.StripeConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.StripeConfiguration;
|
||||||
|
@ -156,7 +156,12 @@ public class WhisperServerConfiguration extends Configuration {
|
||||||
@NotNull
|
@NotNull
|
||||||
@Valid
|
@Valid
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private SecureValueRecovery2Configuration svr2;
|
private SecureValueRecoveryConfiguration svr2;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Valid
|
||||||
|
@JsonProperty
|
||||||
|
private SecureValueRecoveryConfiguration svrb;
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Valid
|
@Valid
|
||||||
|
@ -389,10 +394,14 @@ public class WhisperServerConfiguration extends Configuration {
|
||||||
return pubsub;
|
return pubsub;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SecureValueRecovery2Configuration getSvr2Configuration() {
|
public SecureValueRecoveryConfiguration getSvr2Configuration() {
|
||||||
return svr2;
|
return svr2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SecureValueRecoveryConfiguration getSvrbConfiguration() {
|
||||||
|
return svrb;
|
||||||
|
}
|
||||||
|
|
||||||
public DirectoryV2Configuration getDirectoryV2Configuration() {
|
public DirectoryV2Configuration getDirectoryV2Configuration() {
|
||||||
return directoryV2;
|
return directoryV2;
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,6 +124,7 @@ import org.whispersystems.textsecuregcm.controllers.RegistrationController;
|
||||||
import org.whispersystems.textsecuregcm.controllers.RemoteConfigController;
|
import org.whispersystems.textsecuregcm.controllers.RemoteConfigController;
|
||||||
import org.whispersystems.textsecuregcm.controllers.SecureStorageController;
|
import org.whispersystems.textsecuregcm.controllers.SecureStorageController;
|
||||||
import org.whispersystems.textsecuregcm.controllers.SecureValueRecovery2Controller;
|
import org.whispersystems.textsecuregcm.controllers.SecureValueRecovery2Controller;
|
||||||
|
import org.whispersystems.textsecuregcm.controllers.SecureValueRecoveryBController;
|
||||||
import org.whispersystems.textsecuregcm.controllers.StickerController;
|
import org.whispersystems.textsecuregcm.controllers.StickerController;
|
||||||
import org.whispersystems.textsecuregcm.controllers.SubscriptionController;
|
import org.whispersystems.textsecuregcm.controllers.SubscriptionController;
|
||||||
import org.whispersystems.textsecuregcm.controllers.VerificationController;
|
import org.whispersystems.textsecuregcm.controllers.VerificationController;
|
||||||
|
@ -608,6 +609,8 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
config.getPaymentsServiceConfiguration());
|
config.getPaymentsServiceConfiguration());
|
||||||
ExternalServiceCredentialsGenerator svr2CredentialsGenerator = SecureValueRecovery2Controller.credentialsGenerator(
|
ExternalServiceCredentialsGenerator svr2CredentialsGenerator = SecureValueRecovery2Controller.credentialsGenerator(
|
||||||
config.getSvr2Configuration());
|
config.getSvr2Configuration());
|
||||||
|
ExternalServiceCredentialsGenerator svrbCredentialsGenerator = SecureValueRecoveryBController.credentialsGenerator(
|
||||||
|
config.getSvrbConfiguration());
|
||||||
|
|
||||||
RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager =
|
RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager =
|
||||||
new RegistrationRecoveryPasswordsManager(registrationRecoveryPasswords);
|
new RegistrationRecoveryPasswordsManager(registrationRecoveryPasswords);
|
||||||
|
@ -1121,6 +1124,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
new RemoteConfigController(remoteConfigsManager, config.getRemoteConfigConfiguration().globalConfig(), clock),
|
new RemoteConfigController(remoteConfigsManager, config.getRemoteConfigConfiguration().globalConfig(), clock),
|
||||||
new SecureStorageController(storageCredentialsGenerator),
|
new SecureStorageController(storageCredentialsGenerator),
|
||||||
new SecureValueRecovery2Controller(svr2CredentialsGenerator, accountsManager),
|
new SecureValueRecovery2Controller(svr2CredentialsGenerator, accountsManager),
|
||||||
|
new SecureValueRecoveryBController(svrbCredentialsGenerator),
|
||||||
new StickerController(rateLimiters, config.getCdnConfiguration().credentials().accessKeyId().value(),
|
new StickerController(rateLimiters, config.getCdnConfiguration().credentials().accessKeyId().value(),
|
||||||
config.getCdnConfiguration().credentials().secretAccessKey().value(), config.getCdnConfiguration().region(),
|
config.getCdnConfiguration().credentials().secretAccessKey().value(), config.getCdnConfiguration().region(),
|
||||||
config.getCdnConfiguration().bucket()),
|
config.getCdnConfiguration().bucket()),
|
||||||
|
|
|
@ -12,7 +12,7 @@ import java.util.List;
|
||||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||||
import org.whispersystems.textsecuregcm.util.ExactlySize;
|
import org.whispersystems.textsecuregcm.util.ExactlySize;
|
||||||
|
|
||||||
public record SecureValueRecovery2Configuration(
|
public record SecureValueRecoveryConfiguration(
|
||||||
@NotBlank String uri,
|
@NotBlank String uri,
|
||||||
@ExactlySize(32) SecretBytes userAuthenticationTokenSharedSecret,
|
@ExactlySize(32) SecretBytes userAuthenticationTokenSharedSecret,
|
||||||
@ExactlySize(32) SecretBytes userIdTokenSharedSecret,
|
@ExactlySize(32) SecretBytes userIdTokenSharedSecret,
|
||||||
|
@ -20,7 +20,7 @@ public record SecureValueRecovery2Configuration(
|
||||||
@NotNull @Valid CircuitBreakerConfiguration circuitBreaker,
|
@NotNull @Valid CircuitBreakerConfiguration circuitBreaker,
|
||||||
@NotNull @Valid RetryConfiguration retry) {
|
@NotNull @Valid RetryConfiguration retry) {
|
||||||
|
|
||||||
public SecureValueRecovery2Configuration {
|
public SecureValueRecoveryConfiguration {
|
||||||
if (circuitBreaker == null) {
|
if (circuitBreaker == null) {
|
||||||
circuitBreaker = new CircuitBreakerConfiguration();
|
circuitBreaker = new CircuitBreakerConfiguration();
|
||||||
}
|
}
|
|
@ -28,7 +28,7 @@ import org.whispersystems.textsecuregcm.auth.AuthenticatedDevice;
|
||||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
||||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsSelector;
|
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsSelector;
|
||||||
import org.whispersystems.textsecuregcm.configuration.SecureValueRecovery2Configuration;
|
import org.whispersystems.textsecuregcm.configuration.SecureValueRecoveryConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.entities.AuthCheckRequest;
|
import org.whispersystems.textsecuregcm.entities.AuthCheckRequest;
|
||||||
import org.whispersystems.textsecuregcm.entities.AuthCheckResponseV2;
|
import org.whispersystems.textsecuregcm.entities.AuthCheckResponseV2;
|
||||||
import org.whispersystems.textsecuregcm.limits.RateLimitedByIp;
|
import org.whispersystems.textsecuregcm.limits.RateLimitedByIp;
|
||||||
|
@ -43,12 +43,12 @@ public class SecureValueRecovery2Controller {
|
||||||
|
|
||||||
private static final long MAX_AGE_SECONDS = TimeUnit.DAYS.toSeconds(30);
|
private static final long MAX_AGE_SECONDS = TimeUnit.DAYS.toSeconds(30);
|
||||||
|
|
||||||
public static ExternalServiceCredentialsGenerator credentialsGenerator(final SecureValueRecovery2Configuration cfg) {
|
public static ExternalServiceCredentialsGenerator credentialsGenerator(final SecureValueRecoveryConfiguration cfg) {
|
||||||
return credentialsGenerator(cfg, Clock.systemUTC());
|
return credentialsGenerator(cfg, Clock.systemUTC());
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public static ExternalServiceCredentialsGenerator credentialsGenerator(final SecureValueRecovery2Configuration cfg, final Clock clock) {
|
public static ExternalServiceCredentialsGenerator credentialsGenerator(final SecureValueRecoveryConfiguration cfg, final Clock clock) {
|
||||||
return ExternalServiceCredentialsGenerator
|
return ExternalServiceCredentialsGenerator
|
||||||
.builder(cfg.userAuthenticationTokenSharedSecret())
|
.builder(cfg.userAuthenticationTokenSharedSecret())
|
||||||
.withUserDerivationKey(cfg.userIdTokenSharedSecret().value())
|
.withUserDerivationKey(cfg.userIdTokenSharedSecret().value())
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 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.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import jakarta.ws.rs.Produces;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import java.time.Clock;
|
||||||
|
import org.whispersystems.textsecuregcm.auth.AuthenticatedDevice;
|
||||||
|
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
||||||
|
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||||
|
import org.whispersystems.textsecuregcm.configuration.SecureValueRecoveryConfiguration;
|
||||||
|
|
||||||
|
@Path("/v1/svrb")
|
||||||
|
@Tag(name = "Secure Value Recovery B")
|
||||||
|
public class SecureValueRecoveryBController {
|
||||||
|
|
||||||
|
public static ExternalServiceCredentialsGenerator credentialsGenerator(final SecureValueRecoveryConfiguration cfg) {
|
||||||
|
return credentialsGenerator(cfg, Clock.systemUTC());
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public static ExternalServiceCredentialsGenerator credentialsGenerator(final SecureValueRecoveryConfiguration cfg,
|
||||||
|
final Clock clock) {
|
||||||
|
return ExternalServiceCredentialsGenerator
|
||||||
|
.builder(cfg.userAuthenticationTokenSharedSecret())
|
||||||
|
.withUserDerivationKey(cfg.userIdTokenSharedSecret().value())
|
||||||
|
.prependUsername(false)
|
||||||
|
.withDerivedUsernameTruncateLength(16)
|
||||||
|
.withClock(clock)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private final ExternalServiceCredentialsGenerator svrbCredentialGenerator;
|
||||||
|
|
||||||
|
public SecureValueRecoveryBController(final ExternalServiceCredentialsGenerator svrbCredentialGenerator) {
|
||||||
|
this.svrbCredentialGenerator = svrbCredentialGenerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/auth")
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Operation(
|
||||||
|
summary = "Generate credentials for SVRB",
|
||||||
|
description = """
|
||||||
|
Generate SVRB service credentials. Generated credentials have an expiration time of 1 day (subject to change)
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
@ApiResponse(responseCode = "200", description = "`JSON` with generated credentials.", useReturnTypeSchema = true)
|
||||||
|
@ApiResponse(responseCode = "401", description = "Account authentication check failed.")
|
||||||
|
public ExternalServiceCredentials getAuth(@Auth final AuthenticatedDevice auth) {
|
||||||
|
return svrbCredentialGenerator.generateFor(auth.accountIdentifier().toString());
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,7 +18,7 @@ import org.whispersystems.textsecuregcm.WhisperServerConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||||
import org.whispersystems.textsecuregcm.configuration.DirectoryV2ClientConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.DirectoryV2ClientConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.PaymentsServiceConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.PaymentsServiceConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.SecureValueRecovery2Configuration;
|
import org.whispersystems.textsecuregcm.configuration.SecureValueRecoveryConfiguration;
|
||||||
|
|
||||||
enum ExternalServiceDefinitions {
|
enum ExternalServiceDefinitions {
|
||||||
DIRECTORY(ExternalServiceType.EXTERNAL_SERVICE_TYPE_DIRECTORY, (chatConfig, clock) -> {
|
DIRECTORY(ExternalServiceType.EXTERNAL_SERVICE_TYPE_DIRECTORY, (chatConfig, clock) -> {
|
||||||
|
@ -38,7 +38,17 @@ enum ExternalServiceDefinitions {
|
||||||
.build();
|
.build();
|
||||||
}),
|
}),
|
||||||
SVR(ExternalServiceType.EXTERNAL_SERVICE_TYPE_SVR, (chatConfig, clock) -> {
|
SVR(ExternalServiceType.EXTERNAL_SERVICE_TYPE_SVR, (chatConfig, clock) -> {
|
||||||
final SecureValueRecovery2Configuration cfg = chatConfig.getSvr2Configuration();
|
final SecureValueRecoveryConfiguration cfg = chatConfig.getSvr2Configuration();
|
||||||
|
return ExternalServiceCredentialsGenerator
|
||||||
|
.builder(cfg.userAuthenticationTokenSharedSecret())
|
||||||
|
.withUserDerivationKey(cfg.userIdTokenSharedSecret().value())
|
||||||
|
.prependUsername(false)
|
||||||
|
.withDerivedUsernameTruncateLength(16)
|
||||||
|
.withClock(clock)
|
||||||
|
.build();
|
||||||
|
}),
|
||||||
|
SVRB(ExternalServiceType.EXTERNAL_SERVICE_TYPE_SVRB, (chatConfig, clock) -> {
|
||||||
|
final SecureValueRecoveryConfiguration cfg = chatConfig.getSvrbConfiguration();
|
||||||
return ExternalServiceCredentialsGenerator
|
return ExternalServiceCredentialsGenerator
|
||||||
.builder(cfg.userAuthenticationTokenSharedSecret())
|
.builder(cfg.userAuthenticationTokenSharedSecret())
|
||||||
.withUserDerivationKey(cfg.userIdTokenSharedSecret().value())
|
.withUserDerivationKey(cfg.userIdTokenSharedSecret().value())
|
||||||
|
|
|
@ -21,7 +21,7 @@ import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
||||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||||
import org.whispersystems.textsecuregcm.configuration.SecureValueRecovery2Configuration;
|
import org.whispersystems.textsecuregcm.configuration.SecureValueRecoveryConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.http.FaultTolerantHttpClient;
|
import org.whispersystems.textsecuregcm.http.FaultTolerantHttpClient;
|
||||||
import org.whispersystems.textsecuregcm.util.HttpUtils;
|
import org.whispersystems.textsecuregcm.util.HttpUtils;
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ public class SecureValueRecovery2Client {
|
||||||
|
|
||||||
public SecureValueRecovery2Client(final ExternalServiceCredentialsGenerator secureValueRecoveryCredentialsGenerator,
|
public SecureValueRecovery2Client(final ExternalServiceCredentialsGenerator secureValueRecoveryCredentialsGenerator,
|
||||||
final Executor executor, final ScheduledExecutorService retryExecutor,
|
final Executor executor, final ScheduledExecutorService retryExecutor,
|
||||||
final SecureValueRecovery2Configuration configuration)
|
final SecureValueRecoveryConfiguration configuration)
|
||||||
throws CertificateException {
|
throws CertificateException {
|
||||||
this.secureValueRecoveryCredentialsGenerator = secureValueRecoveryCredentialsGenerator;
|
this.secureValueRecoveryCredentialsGenerator = secureValueRecoveryCredentialsGenerator;
|
||||||
this.deleteUri = URI.create(configuration.uri()).resolve(DELETE_PATH);
|
this.deleteUri = URI.create(configuration.uri()).resolve(DELETE_PATH);
|
||||||
|
|
|
@ -53,6 +53,7 @@ enum ExternalServiceType {
|
||||||
EXTERNAL_SERVICE_TYPE_PAYMENTS = 2;
|
EXTERNAL_SERVICE_TYPE_PAYMENTS = 2;
|
||||||
EXTERNAL_SERVICE_TYPE_STORAGE = 3;
|
EXTERNAL_SERVICE_TYPE_STORAGE = 3;
|
||||||
EXTERNAL_SERVICE_TYPE_SVR = 4;
|
EXTERNAL_SERVICE_TYPE_SVR = 4;
|
||||||
|
EXTERNAL_SERVICE_TYPE_SVRB = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
message GetExternalServiceCredentialsRequest {
|
message GetExternalServiceCredentialsRequest {
|
||||||
|
|
|
@ -23,7 +23,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
||||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||||
import org.whispersystems.textsecuregcm.configuration.SecureValueRecovery2Configuration;
|
import org.whispersystems.textsecuregcm.configuration.SecureValueRecoveryConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.entities.AuthCheckRequest;
|
import org.whispersystems.textsecuregcm.entities.AuthCheckRequest;
|
||||||
import org.whispersystems.textsecuregcm.entities.AuthCheckResponseV2;
|
import org.whispersystems.textsecuregcm.entities.AuthCheckResponseV2;
|
||||||
import org.whispersystems.textsecuregcm.storage.Account;
|
import org.whispersystems.textsecuregcm.storage.Account;
|
||||||
|
@ -42,7 +42,7 @@ import java.util.stream.Collectors;
|
||||||
@ExtendWith(DropwizardExtensionsSupport.class)
|
@ExtendWith(DropwizardExtensionsSupport.class)
|
||||||
public class SecureValueRecovery2ControllerTest {
|
public class SecureValueRecovery2ControllerTest {
|
||||||
|
|
||||||
private static final SecureValueRecovery2Configuration CFG = new SecureValueRecovery2Configuration(
|
private static final SecureValueRecoveryConfiguration CFG = new SecureValueRecoveryConfiguration(
|
||||||
"",
|
"",
|
||||||
randomSecretBytes(32),
|
randomSecretBytes(32),
|
||||||
randomSecretBytes(32),
|
randomSecretBytes(32),
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.whispersystems.textsecuregcm.controllers;
|
||||||
|
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.whispersystems.textsecuregcm.util.MockUtils.randomSecretBytes;
|
||||||
|
|
||||||
|
import io.dropwizard.auth.AuthValueFactoryProvider;
|
||||||
|
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
|
||||||
|
import io.dropwizard.testing.junit5.ResourceExtension;
|
||||||
|
import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.whispersystems.textsecuregcm.auth.AuthenticatedDevice;
|
||||||
|
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
||||||
|
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||||
|
import org.whispersystems.textsecuregcm.configuration.SecureValueRecoveryConfiguration;
|
||||||
|
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
|
||||||
|
import org.whispersystems.textsecuregcm.util.MutableClock;
|
||||||
|
import org.whispersystems.textsecuregcm.util.SystemMapper;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.HexFormat;
|
||||||
|
|
||||||
|
@ExtendWith(DropwizardExtensionsSupport.class)
|
||||||
|
public class SecureValueRecoveryBControllerTest {
|
||||||
|
|
||||||
|
private static final SecureValueRecoveryConfiguration CFG = new SecureValueRecoveryConfiguration(
|
||||||
|
"",
|
||||||
|
randomSecretBytes(32),
|
||||||
|
randomSecretBytes(32),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
private static final MutableClock CLOCK = new MutableClock();
|
||||||
|
|
||||||
|
private static final ExternalServiceCredentialsGenerator CREDENTIAL_GENERATOR =
|
||||||
|
SecureValueRecoveryBController.credentialsGenerator(CFG, CLOCK);
|
||||||
|
|
||||||
|
private static final SecureValueRecoveryBController CONTROLLER =
|
||||||
|
new SecureValueRecoveryBController(CREDENTIAL_GENERATOR);
|
||||||
|
|
||||||
|
private static final ResourceExtension RESOURCES = ResourceExtension.builder()
|
||||||
|
.addProvider(AuthHelper.getAuthFilter())
|
||||||
|
.addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedDevice.class))
|
||||||
|
.setMapper(SystemMapper.jsonMapper())
|
||||||
|
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
|
||||||
|
.addResource(CONTROLLER)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetCredentials() {
|
||||||
|
CLOCK.setTimeInstant(Instant.ofEpochSecond(123));
|
||||||
|
final ExternalServiceCredentials creds = RESOURCES.getJerseyTest()
|
||||||
|
.target("/v1/svrb/auth")
|
||||||
|
.request()
|
||||||
|
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
|
||||||
|
.get(ExternalServiceCredentials.class);
|
||||||
|
|
||||||
|
assertThat(HexFormat.of().parseHex(creds.username())).hasSize(16);
|
||||||
|
System.out.println(creds.password());
|
||||||
|
final String[] split = creds.password().split(":", 2);
|
||||||
|
assertThat(Long.parseLong(split[0])).isEqualTo(123);
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,7 +32,7 @@ import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
||||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||||
import org.whispersystems.textsecuregcm.configuration.SecureValueRecovery2Configuration;
|
import org.whispersystems.textsecuregcm.configuration.SecureValueRecoveryConfiguration;
|
||||||
|
|
||||||
class SecureValueRecovery2ClientTest {
|
class SecureValueRecovery2ClientTest {
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ class SecureValueRecovery2ClientTest {
|
||||||
httpExecutor = Executors.newSingleThreadExecutor();
|
httpExecutor = Executors.newSingleThreadExecutor();
|
||||||
retryExecutor = Executors.newSingleThreadScheduledExecutor();
|
retryExecutor = Executors.newSingleThreadScheduledExecutor();
|
||||||
|
|
||||||
final SecureValueRecovery2Configuration config = new SecureValueRecovery2Configuration(
|
final SecureValueRecoveryConfiguration config = new SecureValueRecoveryConfiguration(
|
||||||
"http://localhost:" + wireMock.getPort(),
|
"http://localhost:" + wireMock.getPort(),
|
||||||
randomSecretBytes(32),
|
randomSecretBytes(32),
|
||||||
randomSecretBytes(32),
|
randomSecretBytes(32),
|
||||||
|
|
|
@ -53,6 +53,9 @@ directoryV2.client.userIdTokenSharedSecret: bbcdefghijklmnopqrstuvwxyz0123456789
|
||||||
svr2.userAuthenticationTokenSharedSecret: abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with SVR2 to generate auth tokens for Signal users
|
svr2.userAuthenticationTokenSharedSecret: abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with SVR2 to generate auth tokens for Signal users
|
||||||
svr2.userIdTokenSharedSecret: bbcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with SVR2 to generate auth identity tokens for Signal users
|
svr2.userIdTokenSharedSecret: bbcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with SVR2 to generate auth identity tokens for Signal users
|
||||||
|
|
||||||
|
svrb.userAuthenticationTokenSharedSecret: abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with SVRB to generate auth tokens for Signal users
|
||||||
|
svrb.userIdTokenSharedSecret: bbcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with SVRB to generate auth identity tokens for Signal users
|
||||||
|
|
||||||
tus.userAuthenticationTokenSharedSecret: abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG=
|
tus.userAuthenticationTokenSharedSecret: abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG=
|
||||||
|
|
||||||
# The below private key was key generated exclusively for testing purposes. Do not use it in any other context.
|
# The below private key was key generated exclusively for testing purposes. Do not use it in any other context.
|
||||||
|
|
|
@ -223,6 +223,35 @@ svr2:
|
||||||
9Kxq0DY7RCEpdHMCKcOL
|
9Kxq0DY7RCEpdHMCKcOL
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
|
svrb:
|
||||||
|
uri: svrb.example.com
|
||||||
|
userAuthenticationTokenSharedSecret: secret://svrb.userAuthenticationTokenSharedSecret
|
||||||
|
userIdTokenSharedSecret: secret://svrb.userIdTokenSharedSecret
|
||||||
|
svrCaCertificates:
|
||||||
|
# this is a randomly generated test certificate
|
||||||
|
- |
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDazCCAlOgAwIBAgIUW5lcNWkuynRVc8Rq5pO6mHQBuZAwDQYJKoZIhvcNAQEL
|
||||||
|
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
|
||||||
|
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNDAzMjUwMzE4MTNaFw0yOTAz
|
||||||
|
MjQwMzE4MTNaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
|
||||||
|
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB
|
||||||
|
AQUAA4IBDwAwggEKAoIBAQCfH4Um+fv2r4KudhD37/UXp8duRLTmp4XvpBTpDHpD
|
||||||
|
2HF8p2yThVKlJnMkP/9Ey1Rb0vhxO7DCltLdW8IYcxJuHoyMvyhGUEtxxkOZbrk8
|
||||||
|
ciUR9jTZ37x7vXRGj/RxcdlS6iD0MeF0D/LAkImt4T/kiKwDbENrVEnYWJmipCKP
|
||||||
|
ribxWky7HqxDCoYMQr0zatxB3A9mx5stH+H3kbw3CZcm+ugF9ZIKDEVHb0lf28gq
|
||||||
|
llmD120q/vs9YV3rzVL7sBGDqf6olkulvHQJKElZg2rdcHWFcngSlU2BjR04oyuH
|
||||||
|
c/SSiLSB3YB0tdFGta5uorXyV1y7RElPeBfOfvEjsG3TAgMBAAGjUzBRMB0GA1Ud
|
||||||
|
DgQWBBQX+xlgSWWbDjv0SrJ+h67xauJ80zAfBgNVHSMEGDAWgBQX+xlgSWWbDjv0
|
||||||
|
SrJ+h67xauJ80zAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAw
|
||||||
|
ZG2MCCjscn6h/QOoJU+IDfa68OqLq0I37gMnLMde4yEhAmm//miePIq4Uz9GRJ+h
|
||||||
|
rAmdEnspKgyQ93PjF7Xpk/JdJA4B1bIrsOl/cSwqx2sFhRt8Kt1DHGlGWXqOaHRP
|
||||||
|
UkZ86MyRL3sXly6WkxEYxZJeQaOzMy2XmQh7grzrlTBuSI+0xf7vsRRDipxr6LVQ
|
||||||
|
6qGWyGODLLc2JD1IXj/1HpRVT2LoGGlKMuyxACQAm4oak1vvJ9mGxgfd9AU+eo58
|
||||||
|
O/esB2Eaf+QqMPELdFSZQfG2jvp+3WQTZK8fDKHyLr076G3UetEMy867F6fzTSZd
|
||||||
|
9Kxq0DY7RCEpdHMCKcOL
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
messageCache: # Redis server configuration for message store cache
|
messageCache: # Redis server configuration for message store cache
|
||||||
persistDelayMinutes: 1
|
persistDelayMinutes: 1
|
||||||
cluster:
|
cluster:
|
||||||
|
|
Loading…
Reference in New Issue