refactoring of ExternalServiceCredentialGenerator
This commit is contained in:
parent
dd98f7f043
commit
eb499833c6
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2013-2022 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
package org.whispersystems.textsecuregcm;
|
||||
|
@ -80,16 +80,18 @@ import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
|||
import org.whispersystems.textsecuregcm.auth.CertificateGenerator;
|
||||
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAccountAuthenticator;
|
||||
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialGenerator;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||
import org.whispersystems.textsecuregcm.auth.TurnTokenGenerator;
|
||||
import org.whispersystems.textsecuregcm.auth.WebsocketRefreshApplicationEventListener;
|
||||
import org.whispersystems.textsecuregcm.badges.ConfiguredProfileBadgeConverter;
|
||||
import org.whispersystems.textsecuregcm.badges.ResourceBundleLevelTranslator;
|
||||
import org.whispersystems.textsecuregcm.captcha.CaptchaChecker;
|
||||
import org.whispersystems.textsecuregcm.captcha.HCaptchaClient;
|
||||
import org.whispersystems.textsecuregcm.captcha.RecaptchaClient;
|
||||
import org.whispersystems.textsecuregcm.configuration.DirectoryServerConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
||||
import org.whispersystems.textsecuregcm.controllers.AccountController;
|
||||
import org.whispersystems.textsecuregcm.controllers.ArtController;
|
||||
import org.whispersystems.textsecuregcm.controllers.AttachmentControllerV2;
|
||||
import org.whispersystems.textsecuregcm.controllers.AttachmentControllerV3;
|
||||
import org.whispersystems.textsecuregcm.controllers.CertificateController;
|
||||
|
@ -108,15 +110,14 @@ import org.whispersystems.textsecuregcm.controllers.RemoteConfigController;
|
|||
import org.whispersystems.textsecuregcm.controllers.SecureBackupController;
|
||||
import org.whispersystems.textsecuregcm.controllers.SecureStorageController;
|
||||
import org.whispersystems.textsecuregcm.controllers.StickerController;
|
||||
import org.whispersystems.textsecuregcm.controllers.ArtController;
|
||||
import org.whispersystems.textsecuregcm.controllers.SubscriptionController;
|
||||
import org.whispersystems.textsecuregcm.controllers.VoiceVerificationController;
|
||||
import org.whispersystems.textsecuregcm.currency.CoinMarketCapClient;
|
||||
import org.whispersystems.textsecuregcm.currency.CurrencyConversionManager;
|
||||
import org.whispersystems.textsecuregcm.currency.FixerClient;
|
||||
import org.whispersystems.textsecuregcm.experiment.ExperimentEnrollmentManager;
|
||||
import org.whispersystems.textsecuregcm.filters.RequestStatisticsFilter;
|
||||
import org.whispersystems.textsecuregcm.filters.RemoteDeprecationFilter;
|
||||
import org.whispersystems.textsecuregcm.filters.RequestStatisticsFilter;
|
||||
import org.whispersystems.textsecuregcm.filters.TimestampResponseFilter;
|
||||
import org.whispersystems.textsecuregcm.limits.DynamicRateLimiters;
|
||||
import org.whispersystems.textsecuregcm.limits.PushChallengeManager;
|
||||
|
@ -158,7 +159,6 @@ import org.whispersystems.textsecuregcm.push.ProvisioningManager;
|
|||
import org.whispersystems.textsecuregcm.push.PushLatencyManager;
|
||||
import org.whispersystems.textsecuregcm.push.PushNotificationManager;
|
||||
import org.whispersystems.textsecuregcm.push.ReceiptSender;
|
||||
import org.whispersystems.textsecuregcm.captcha.RecaptchaClient;
|
||||
import org.whispersystems.textsecuregcm.redis.ConnectionEventLogger;
|
||||
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
|
||||
import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool;
|
||||
|
@ -450,29 +450,23 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
|||
config.getBraintree().supportedCurrencies(), config.getBraintree().merchantAccounts(),
|
||||
config.getBraintree().graphqlUrl(), config.getBraintree().circuitBreaker(), subscriptionProcessorExecutor);
|
||||
|
||||
ExternalServiceCredentialGenerator directoryCredentialsGenerator = new ExternalServiceCredentialGenerator(
|
||||
config.getDirectoryConfiguration().getDirectoryClientConfiguration().getUserAuthenticationTokenSharedSecret(),
|
||||
config.getDirectoryConfiguration().getDirectoryClientConfiguration().getUserAuthenticationTokenUserIdSecret());
|
||||
ExternalServiceCredentialGenerator directoryV2CredentialsGenerator = new ExternalServiceCredentialGenerator(
|
||||
config.getDirectoryV2Configuration().getDirectoryV2ClientConfiguration().getUserAuthenticationTokenSharedSecret(),
|
||||
config.getDirectoryV2Configuration().getDirectoryV2ClientConfiguration().getUserIdTokenSharedSecret(),
|
||||
true, false);
|
||||
|
||||
ExternalServiceCredentialsGenerator directoryCredentialsGenerator = DirectoryController.credentialsGenerator(
|
||||
config.getDirectoryConfiguration().getDirectoryClientConfiguration());
|
||||
ExternalServiceCredentialsGenerator directoryV2CredentialsGenerator = DirectoryV2Controller.credentialsGenerator(
|
||||
config.getDirectoryV2Configuration().getDirectoryV2ClientConfiguration());
|
||||
ExternalServiceCredentialsGenerator storageCredentialsGenerator = SecureStorageController.credentialsGenerator(
|
||||
config.getSecureStorageServiceConfiguration());
|
||||
ExternalServiceCredentialsGenerator backupCredentialsGenerator = SecureBackupController.credentialsGenerator(
|
||||
config.getSecureBackupServiceConfiguration());
|
||||
ExternalServiceCredentialsGenerator paymentsCredentialsGenerator = PaymentsController.credentialsGenerator(
|
||||
config.getPaymentsServiceConfiguration());
|
||||
ExternalServiceCredentialsGenerator artCredentialsGenerator = ArtController.credentialsGenerator(
|
||||
config.getArtServiceConfiguration());
|
||||
|
||||
dynamicConfigurationManager.start();
|
||||
|
||||
ExperimentEnrollmentManager experimentEnrollmentManager = new ExperimentEnrollmentManager(dynamicConfigurationManager);
|
||||
|
||||
ExternalServiceCredentialGenerator storageCredentialsGenerator = new ExternalServiceCredentialGenerator(
|
||||
config.getSecureStorageServiceConfiguration().getUserAuthenticationTokenSharedSecret(), true);
|
||||
ExternalServiceCredentialGenerator backupCredentialsGenerator = new ExternalServiceCredentialGenerator(
|
||||
config.getSecureBackupServiceConfiguration().getUserAuthenticationTokenSharedSecret(), true);
|
||||
ExternalServiceCredentialGenerator paymentsCredentialsGenerator = new ExternalServiceCredentialGenerator(
|
||||
config.getPaymentsServiceConfiguration().getUserAuthenticationTokenSharedSecret(), true);
|
||||
ExternalServiceCredentialGenerator artCredentialsGenerator = new ExternalServiceCredentialGenerator(
|
||||
config.getArtServiceConfiguration().getUserAuthenticationTokenSharedSecret(),
|
||||
config.getArtServiceConfiguration().getUserAuthenticationTokenUserIdSecret(),
|
||||
true, false, false);
|
||||
|
||||
RegistrationServiceClient registrationServiceClient = new RegistrationServiceClient(config.getRegistrationServiceConfiguration().getHost(), config.getRegistrationServiceConfiguration().getPort(), config.getRegistrationServiceConfiguration().getApiKey(), config.getRegistrationServiceConfiguration().getRegistrationCaCertificate(), registrationCallbackExecutor);
|
||||
SecureBackupClient secureBackupClient = new SecureBackupClient(backupCredentialsGenerator, backupServiceExecutor, config.getSecureBackupServiceConfiguration());
|
||||
SecureStorageClient secureStorageClient = new SecureStorageClient(storageCredentialsGenerator, storageServiceExecutor, config.getSecureStorageServiceConfiguration());
|
||||
|
|
|
@ -1,106 +0,0 @@
|
|||
/*
|
||||
* Copyright 2013-2021 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.auth;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.time.Clock;
|
||||
import java.util.HexFormat;
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import org.whispersystems.textsecuregcm.util.Util;
|
||||
|
||||
public class ExternalServiceCredentialGenerator {
|
||||
|
||||
private final byte[] key;
|
||||
private final byte[] userIdKey;
|
||||
private final boolean usernameDerivation;
|
||||
private final boolean prependUsername;
|
||||
private final boolean truncateKey;
|
||||
private final Clock clock;
|
||||
|
||||
public ExternalServiceCredentialGenerator(byte[] key, byte[] userIdKey) {
|
||||
this(key, userIdKey, true, true, true);
|
||||
}
|
||||
|
||||
public ExternalServiceCredentialGenerator(byte[] key, boolean prependUsername) {
|
||||
this(key, prependUsername, true);
|
||||
}
|
||||
|
||||
public ExternalServiceCredentialGenerator(byte[] key, boolean prependUsername, boolean truncateKey) {
|
||||
this(key, new byte[0], false, prependUsername, truncateKey);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public ExternalServiceCredentialGenerator(byte[] key, byte[] userIdKey, boolean usernameDerivation) {
|
||||
this(key, userIdKey, usernameDerivation, true, true);
|
||||
}
|
||||
|
||||
public ExternalServiceCredentialGenerator(byte[] key, byte[] userIdKey, boolean usernameDerivation,
|
||||
boolean prependUsername) {
|
||||
this(key, userIdKey, usernameDerivation, prependUsername, true, Clock.systemUTC());
|
||||
}
|
||||
|
||||
public ExternalServiceCredentialGenerator(byte[] key, byte[] userIdKey, boolean usernameDerivation,
|
||||
boolean prependUsername, boolean truncateKey) {
|
||||
this(key, userIdKey, usernameDerivation, prependUsername, truncateKey, Clock.systemUTC());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public ExternalServiceCredentialGenerator(byte[] key, byte[] userIdKey, boolean usernameDerivation,
|
||||
boolean prependUsername, Clock clock) {
|
||||
this(key, userIdKey, usernameDerivation, prependUsername, true, clock);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public ExternalServiceCredentialGenerator(byte[] key, byte[] userIdKey, boolean usernameDerivation,
|
||||
boolean prependUsername, boolean truncateKey, Clock clock) {
|
||||
this.key = key;
|
||||
this.userIdKey = userIdKey;
|
||||
this.usernameDerivation = usernameDerivation;
|
||||
this.prependUsername = prependUsername;
|
||||
this.truncateKey = truncateKey;
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
public ExternalServiceCredentials generateFor(String identity) {
|
||||
Mac mac = getMacInstance();
|
||||
String username = getUserId(identity, mac, usernameDerivation);
|
||||
long currentTimeSeconds = clock.millis() / 1000;
|
||||
String prefix = username + ":" + currentTimeSeconds;
|
||||
byte[] prefixMac = getHmac(key, prefix.getBytes(), mac);
|
||||
final HexFormat hex = HexFormat.of();
|
||||
String output = hex.formatHex(truncateKey ? Util.truncate(prefixMac, 10) : prefixMac);
|
||||
String token = (prependUsername ? prefix : currentTimeSeconds) + ":" + output;
|
||||
|
||||
return new ExternalServiceCredentials(username, token);
|
||||
}
|
||||
|
||||
private String getUserId(String number, Mac mac, boolean usernameDerivation) {
|
||||
final HexFormat hex = HexFormat.of();
|
||||
if (usernameDerivation) return hex.formatHex(Util.truncate(getHmac(userIdKey, number.getBytes(), mac), 10));
|
||||
else return number;
|
||||
}
|
||||
|
||||
private Mac getMacInstance() {
|
||||
try {
|
||||
return Mac.getInstance("HmacSHA256");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] getHmac(byte[] key, byte[] input, Mac mac) {
|
||||
try {
|
||||
mac.init(new SecretKeySpec(key, "HmacSHA256"));
|
||||
return mac.doFinal(input);
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.auth;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static org.whispersystems.textsecuregcm.util.HmacUtils.hmac256ToHexString;
|
||||
import static org.whispersystems.textsecuregcm.util.HmacUtils.hmac256TruncatedToHexString;
|
||||
|
||||
import java.time.Clock;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
public class ExternalServiceCredentialsGenerator {
|
||||
|
||||
private static final int TRUNCATE_LENGTH = 10;
|
||||
|
||||
private final byte[] key;
|
||||
|
||||
private final byte[] userDerivationKey;
|
||||
|
||||
private final boolean prependUsername;
|
||||
|
||||
private final boolean truncateSignature;
|
||||
|
||||
private final Clock clock;
|
||||
|
||||
|
||||
public static ExternalServiceCredentialsGenerator.Builder builder(final byte[] key) {
|
||||
return new Builder(key);
|
||||
}
|
||||
|
||||
private ExternalServiceCredentialsGenerator(
|
||||
final byte[] key,
|
||||
final byte[] userDerivationKey,
|
||||
final boolean prependUsername,
|
||||
final boolean truncateSignature,
|
||||
final Clock clock) {
|
||||
this.key = requireNonNull(key);
|
||||
this.userDerivationKey = requireNonNull(userDerivationKey);
|
||||
this.prependUsername = prependUsername;
|
||||
this.truncateSignature = truncateSignature;
|
||||
this.clock = requireNonNull(clock);
|
||||
}
|
||||
|
||||
public ExternalServiceCredentials generateForUuid(final UUID uuid) {
|
||||
return generateFor(uuid.toString());
|
||||
}
|
||||
|
||||
public ExternalServiceCredentials generateFor(final String identity) {
|
||||
final String username = userDerivationKey.length > 0
|
||||
? hmac256TruncatedToHexString(userDerivationKey, identity, TRUNCATE_LENGTH)
|
||||
: identity;
|
||||
|
||||
final long currentTimeSeconds = TimeUnit.MILLISECONDS.toSeconds(clock.millis());
|
||||
|
||||
final String dataToSign = username + ":" + currentTimeSeconds;
|
||||
|
||||
final String signature = truncateSignature
|
||||
? hmac256TruncatedToHexString(key, dataToSign, TRUNCATE_LENGTH)
|
||||
: hmac256ToHexString(key, dataToSign);
|
||||
|
||||
final String token = (prependUsername ? dataToSign : currentTimeSeconds) + ":" + signature;
|
||||
|
||||
return new ExternalServiceCredentials(username, token);
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private final byte[] key;
|
||||
|
||||
private byte[] userDerivationKey = new byte[0];
|
||||
|
||||
private boolean prependUsername = true;
|
||||
|
||||
private boolean truncateSignature = true;
|
||||
|
||||
private Clock clock = Clock.systemUTC();
|
||||
|
||||
|
||||
private Builder(final byte[] key) {
|
||||
this.key = requireNonNull(key);
|
||||
}
|
||||
|
||||
public Builder withUserDerivationKey(final byte[] userDerivationKey) {
|
||||
Validate.isTrue(requireNonNull(userDerivationKey).length > 0, "userDerivationKey must not be empty");
|
||||
this.userDerivationKey = userDerivationKey;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withClock(final Clock clock) {
|
||||
this.clock = requireNonNull(clock);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder prependUsername(final boolean prependUsername) {
|
||||
this.prependUsername = prependUsername;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder truncateSignature(final boolean truncateSignature) {
|
||||
this.truncateSignature = truncateSignature;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ExternalServiceCredentialsGenerator build() {
|
||||
return new ExternalServiceCredentialsGenerator(
|
||||
key, userDerivationKey, prependUsername, truncateSignature, clock);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,33 +1,11 @@
|
|||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* Copyright 2013-2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.whispersystems.textsecuregcm.util.ExactlySize;
|
||||
|
||||
public class DirectoryV2ClientConfiguration {
|
||||
|
||||
private final byte[] userAuthenticationTokenSharedSecret;
|
||||
private final byte[] userIdTokenSharedSecret;
|
||||
|
||||
@JsonCreator
|
||||
public DirectoryV2ClientConfiguration(
|
||||
@JsonProperty("userAuthenticationTokenSharedSecret") final byte[] userAuthenticationTokenSharedSecret,
|
||||
@JsonProperty("userIdTokenSharedSecret") final byte[] userIdTokenSharedSecret) {
|
||||
this.userAuthenticationTokenSharedSecret = userAuthenticationTokenSharedSecret;
|
||||
this.userIdTokenSharedSecret = userIdTokenSharedSecret;
|
||||
}
|
||||
|
||||
@ExactlySize({32})
|
||||
public byte[] getUserAuthenticationTokenSharedSecret() {
|
||||
return userAuthenticationTokenSharedSecret;
|
||||
}
|
||||
|
||||
@ExactlySize({32})
|
||||
public byte[] getUserIdTokenSharedSecret() {
|
||||
return userIdTokenSharedSecret;
|
||||
}
|
||||
public record DirectoryV2ClientConfiguration(@ExactlySize({32}) byte[] userAuthenticationTokenSharedSecret,
|
||||
@ExactlySize({32}) byte[] userIdTokenSharedSecret) {
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
|
@ -7,65 +7,28 @@ package org.whispersystems.textsecuregcm.configuration;
|
|||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import java.util.List;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.apache.commons.codec.DecoderException;
|
||||
import org.apache.commons.codec.binary.Hex;
|
||||
import java.util.List;
|
||||
|
||||
public class SecureStorageServiceConfiguration {
|
||||
public record SecureStorageServiceConfiguration(@NotEmpty String userAuthenticationTokenSharedSecret,
|
||||
@NotBlank String uri,
|
||||
@NotEmpty List<@NotBlank String> storageCaCertificates,
|
||||
@Valid @JsonProperty("circuitBreaker") CircuitBreakerConfiguration circuitBreakerConfig,
|
||||
@Valid @JsonProperty("retry") RetryConfiguration retryConfig) {
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String userAuthenticationTokenSharedSecret;
|
||||
@VisibleForTesting
|
||||
public SecureStorageServiceConfiguration(
|
||||
final @NotEmpty String userAuthenticationTokenSharedSecret,
|
||||
final @NotBlank String uri,
|
||||
final @NotEmpty List<@NotBlank String> storageCaCertificates) {
|
||||
this(userAuthenticationTokenSharedSecret, uri, storageCaCertificates, new CircuitBreakerConfiguration(), new RetryConfiguration());
|
||||
}
|
||||
|
||||
@NotBlank
|
||||
@JsonProperty
|
||||
private String uri;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private List<@NotBlank String> storageCaCertificates;
|
||||
|
||||
@NotNull
|
||||
@Valid
|
||||
@JsonProperty
|
||||
private CircuitBreakerConfiguration circuitBreaker = new CircuitBreakerConfiguration();
|
||||
|
||||
@NotNull
|
||||
@Valid
|
||||
@JsonProperty
|
||||
private RetryConfiguration retry = new RetryConfiguration();
|
||||
|
||||
public byte[] getUserAuthenticationTokenSharedSecret() throws DecoderException {
|
||||
public byte[] decodeUserAuthenticationTokenSharedSecret() throws DecoderException {
|
||||
return Hex.decodeHex(userAuthenticationTokenSharedSecret.toCharArray());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setUri(final String uri) {
|
||||
this.uri = uri;
|
||||
}
|
||||
|
||||
public String getUri() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setStorageCaCertificates(final List<String> certificatePem) {
|
||||
this.storageCaCertificates = certificatePem;
|
||||
}
|
||||
|
||||
public List<String> getStorageCaCertificates() {
|
||||
return storageCaCertificates;
|
||||
}
|
||||
|
||||
public CircuitBreakerConfiguration getCircuitBreakerConfiguration() {
|
||||
return circuitBreaker;
|
||||
}
|
||||
|
||||
public RetryConfiguration getRetryConfiguration() {
|
||||
return retry;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2013-2021 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
package org.whispersystems.textsecuregcm.controllers;
|
||||
|
@ -61,8 +61,8 @@ import org.whispersystems.textsecuregcm.auth.AuthenticationCredentials;
|
|||
import org.whispersystems.textsecuregcm.auth.BasicAuthorizationHeader;
|
||||
import org.whispersystems.textsecuregcm.auth.ChangesDeviceEnabledState;
|
||||
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialGenerator;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||
import org.whispersystems.textsecuregcm.auth.StoredRegistrationLock;
|
||||
import org.whispersystems.textsecuregcm.auth.StoredVerificationCode;
|
||||
import org.whispersystems.textsecuregcm.auth.TurnToken;
|
||||
|
@ -165,7 +165,7 @@ public class AccountController {
|
|||
private final Map<String, Integer> testDevices;
|
||||
private final CaptchaChecker captchaChecker;
|
||||
private final PushNotificationManager pushNotificationManager;
|
||||
private final ExternalServiceCredentialGenerator backupServiceCredentialGenerator;
|
||||
private final ExternalServiceCredentialsGenerator backupServiceCredentialsGenerator;
|
||||
|
||||
private final ChangeNumberManager changeNumberManager;
|
||||
private final Clock clock;
|
||||
|
@ -186,7 +186,7 @@ public class AccountController {
|
|||
CaptchaChecker captchaChecker,
|
||||
PushNotificationManager pushNotificationManager,
|
||||
ChangeNumberManager changeNumberManager,
|
||||
ExternalServiceCredentialGenerator backupServiceCredentialGenerator,
|
||||
ExternalServiceCredentialsGenerator backupServiceCredentialsGenerator,
|
||||
ClientPresenceManager clientPresenceManager,
|
||||
Clock clock
|
||||
) {
|
||||
|
@ -199,7 +199,7 @@ public class AccountController {
|
|||
this.turnTokenGenerator = turnTokenGenerator;
|
||||
this.captchaChecker = captchaChecker;
|
||||
this.pushNotificationManager = pushNotificationManager;
|
||||
this.backupServiceCredentialGenerator = backupServiceCredentialGenerator;
|
||||
this.backupServiceCredentialsGenerator = backupServiceCredentialsGenerator;
|
||||
this.changeNumberManager = changeNumberManager;
|
||||
this.clientPresenceManager = clientPresenceManager;
|
||||
this.clock = clock;
|
||||
|
@ -217,12 +217,12 @@ public class AccountController {
|
|||
CaptchaChecker captchaChecker,
|
||||
PushNotificationManager pushNotificationManager,
|
||||
ChangeNumberManager changeNumberManager,
|
||||
ExternalServiceCredentialGenerator backupServiceCredentialGenerator
|
||||
ExternalServiceCredentialsGenerator backupServiceCredentialsGenerator
|
||||
) {
|
||||
this(pendingAccounts, accounts, rateLimiters,
|
||||
registrationServiceClient, dynamicConfigurationManager, turnTokenGenerator, testDevices, captchaChecker,
|
||||
pushNotificationManager, changeNumberManager,
|
||||
backupServiceCredentialGenerator, null, Clock.systemUTC());
|
||||
backupServiceCredentialsGenerator, null, Clock.systemUTC());
|
||||
}
|
||||
|
||||
@Timed
|
||||
|
@ -832,7 +832,7 @@ public class AccountController {
|
|||
|
||||
final StoredRegistrationLock existingRegistrationLock = existingAccount.getRegistrationLock();
|
||||
final ExternalServiceCredentials existingBackupCredentials =
|
||||
backupServiceCredentialGenerator.generateFor(existingAccount.getUuid().toString());
|
||||
backupServiceCredentialsGenerator.generateForUuid(existingAccount.getUuid());
|
||||
|
||||
if (existingRegistrationLock.requiresClientRegistrationLock()) {
|
||||
if (!Util.isEmpty(clientRegistrationLock)) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2013-2022 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
|
@ -12,19 +12,34 @@ import javax.ws.rs.GET;
|
|||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import org.apache.commons.codec.DecoderException;
|
||||
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialGenerator;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||
import org.whispersystems.textsecuregcm.configuration.ArtServiceConfiguration;
|
||||
import org.whispersystems.textsecuregcm.limits.RateLimiters;
|
||||
|
||||
@Path("/v1/art")
|
||||
public class ArtController {
|
||||
private final ExternalServiceCredentialGenerator artServiceCredentialGenerator;
|
||||
private final ExternalServiceCredentialsGenerator artServiceCredentialsGenerator;
|
||||
private final RateLimiters rateLimiters;
|
||||
|
||||
public static ExternalServiceCredentialsGenerator credentialsGenerator(final ArtServiceConfiguration cfg) {
|
||||
try {
|
||||
return ExternalServiceCredentialsGenerator
|
||||
.builder(cfg.getUserAuthenticationTokenSharedSecret())
|
||||
.withUserDerivationKey(cfg.getUserAuthenticationTokenUserIdSecret())
|
||||
.prependUsername(false)
|
||||
.truncateSignature(false)
|
||||
.build();
|
||||
} catch (DecoderException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public ArtController(RateLimiters rateLimiters,
|
||||
ExternalServiceCredentialGenerator artServiceCredentialGenerator) {
|
||||
this.artServiceCredentialGenerator = artServiceCredentialGenerator;
|
||||
ExternalServiceCredentialsGenerator artServiceCredentialsGenerator) {
|
||||
this.artServiceCredentialsGenerator = artServiceCredentialsGenerator;
|
||||
this.rateLimiters = rateLimiters;
|
||||
}
|
||||
|
||||
|
@ -36,6 +51,6 @@ public class ArtController {
|
|||
throws RateLimitExceededException {
|
||||
final UUID uuid = auth.getAccount().getUuid();
|
||||
rateLimiters.getArtPackLimiter().validate(uuid);
|
||||
return artServiceCredentialGenerator.generateFor(uuid.toString());
|
||||
return artServiceCredentialsGenerator.generateForUuid(uuid);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2013-2021 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
|
@ -32,7 +32,7 @@ public class AttachmentControllerV2 {
|
|||
|
||||
public AttachmentControllerV2(RateLimiters rateLimiters, String accessKey, String accessSecret, String region,
|
||||
String bucket) {
|
||||
this.rateLimiter = rateLimiters.getAttachmentLimiter();
|
||||
this.rateLimiter = rateLimiters.getAttachmentLimiter();
|
||||
this.policyGenerator = new PostPolicyGenerator(region, bucket, accessKey);
|
||||
this.policySigner = new PolicySigner(accessSecret, region);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2013-2021 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
package org.whispersystems.textsecuregcm.controllers;
|
||||
|
@ -13,15 +13,25 @@ import javax.ws.rs.Path;
|
|||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import org.apache.commons.codec.DecoderException;
|
||||
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialGenerator;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||
import org.whispersystems.textsecuregcm.configuration.DirectoryClientConfiguration;
|
||||
|
||||
@Path("/v1/directory")
|
||||
public class DirectoryController {
|
||||
|
||||
private final ExternalServiceCredentialGenerator directoryServiceTokenGenerator;
|
||||
private final ExternalServiceCredentialsGenerator directoryServiceTokenGenerator;
|
||||
|
||||
public DirectoryController(ExternalServiceCredentialGenerator userTokenGenerator) {
|
||||
public static ExternalServiceCredentialsGenerator credentialsGenerator(final DirectoryClientConfiguration cfg)
|
||||
throws DecoderException {
|
||||
return ExternalServiceCredentialsGenerator
|
||||
.builder(cfg.getUserAuthenticationTokenSharedSecret())
|
||||
.withUserDerivationKey(cfg.getUserAuthenticationTokenUserIdSecret())
|
||||
.build();
|
||||
}
|
||||
|
||||
public DirectoryController(ExternalServiceCredentialsGenerator userTokenGenerator) {
|
||||
this.directoryServiceTokenGenerator = userTokenGenerator;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
/*
|
||||
* Copyright 2013-2021 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
package org.whispersystems.textsecuregcm.controllers;
|
||||
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import io.dropwizard.auth.Auth;
|
||||
import java.time.Clock;
|
||||
import java.util.UUID;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
|
@ -13,15 +15,31 @@ import javax.ws.rs.Produces;
|
|||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialGenerator;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||
import org.whispersystems.textsecuregcm.configuration.DirectoryV2ClientConfiguration;
|
||||
|
||||
@Path("/v2/directory")
|
||||
public class DirectoryV2Controller {
|
||||
|
||||
private final ExternalServiceCredentialGenerator directoryServiceTokenGenerator;
|
||||
private final ExternalServiceCredentialsGenerator directoryServiceTokenGenerator;
|
||||
|
||||
public DirectoryV2Controller(ExternalServiceCredentialGenerator userTokenGenerator) {
|
||||
@VisibleForTesting
|
||||
public static ExternalServiceCredentialsGenerator credentialsGenerator(final DirectoryV2ClientConfiguration cfg,
|
||||
final Clock clock) {
|
||||
return ExternalServiceCredentialsGenerator
|
||||
.builder(cfg.userAuthenticationTokenSharedSecret())
|
||||
.withUserDerivationKey(cfg.userIdTokenSharedSecret())
|
||||
.prependUsername(false)
|
||||
.withClock(clock)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static ExternalServiceCredentialsGenerator credentialsGenerator(final DirectoryV2ClientConfiguration cfg) {
|
||||
return credentialsGenerator(cfg, Clock.systemUTC());
|
||||
}
|
||||
|
||||
public DirectoryV2Controller(ExternalServiceCredentialsGenerator userTokenGenerator) {
|
||||
this.directoryServiceTokenGenerator = userTokenGenerator;
|
||||
}
|
||||
|
||||
|
@ -31,7 +49,7 @@ public class DirectoryV2Controller {
|
|||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response getAuthToken(@Auth AuthenticatedAccount auth) {
|
||||
final UUID uuid = auth.getAccount().getUuid();
|
||||
final ExternalServiceCredentials credentials = directoryServiceTokenGenerator.generateFor(uuid.toString());
|
||||
final ExternalServiceCredentials credentials = directoryServiceTokenGenerator.generateForUuid(uuid);
|
||||
return Response.ok().entity(credentials).build();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2013-2021 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
|
@ -11,36 +11,47 @@ import javax.ws.rs.GET;
|
|||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import org.apache.commons.codec.DecoderException;
|
||||
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialGenerator;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||
import org.whispersystems.textsecuregcm.configuration.PaymentsServiceConfiguration;
|
||||
import org.whispersystems.textsecuregcm.currency.CurrencyConversionManager;
|
||||
import org.whispersystems.textsecuregcm.entities.CurrencyConversionEntityList;
|
||||
|
||||
@Path("/v1/payments")
|
||||
public class PaymentsController {
|
||||
|
||||
private final ExternalServiceCredentialGenerator paymentsServiceCredentialGenerator;
|
||||
private final CurrencyConversionManager currencyManager;
|
||||
private final ExternalServiceCredentialsGenerator paymentsServiceCredentialsGenerator;
|
||||
private final CurrencyConversionManager currencyManager;
|
||||
|
||||
public PaymentsController(CurrencyConversionManager currencyManager, ExternalServiceCredentialGenerator paymentsServiceCredentialGenerator) {
|
||||
|
||||
public static ExternalServiceCredentialsGenerator credentialsGenerator(final PaymentsServiceConfiguration cfg)
|
||||
throws DecoderException {
|
||||
return ExternalServiceCredentialsGenerator
|
||||
.builder(cfg.getUserAuthenticationTokenSharedSecret())
|
||||
.prependUsername(true)
|
||||
.build();
|
||||
}
|
||||
|
||||
public PaymentsController(final CurrencyConversionManager currencyManager, final ExternalServiceCredentialsGenerator paymentsServiceCredentialsGenerator) {
|
||||
this.currencyManager = currencyManager;
|
||||
this.paymentsServiceCredentialGenerator = paymentsServiceCredentialGenerator;
|
||||
this.paymentsServiceCredentialsGenerator = paymentsServiceCredentialsGenerator;
|
||||
}
|
||||
|
||||
@Timed
|
||||
@GET
|
||||
@Path("/auth")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public ExternalServiceCredentials getAuth(@Auth AuthenticatedAccount auth) {
|
||||
return paymentsServiceCredentialGenerator.generateFor(auth.getAccount().getUuid().toString());
|
||||
public ExternalServiceCredentials getAuth(final @Auth AuthenticatedAccount auth) {
|
||||
return paymentsServiceCredentialsGenerator.generateForUuid(auth.getAccount().getUuid());
|
||||
}
|
||||
|
||||
@Timed
|
||||
@GET
|
||||
@Path("/conversions")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public CurrencyConversionEntityList getConversions(@Auth AuthenticatedAccount auth) {
|
||||
public CurrencyConversionEntityList getConversions(final @Auth AuthenticatedAccount auth) {
|
||||
return currencyManager.getCurrencyConversions().orElseThrow();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2013-2021 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
|
@ -11,17 +11,27 @@ import javax.ws.rs.GET;
|
|||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import org.apache.commons.codec.DecoderException;
|
||||
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialGenerator;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||
import org.whispersystems.textsecuregcm.configuration.SecureBackupServiceConfiguration;
|
||||
|
||||
@Path("/v1/backup")
|
||||
public class SecureBackupController {
|
||||
|
||||
private final ExternalServiceCredentialGenerator backupServiceCredentialGenerator;
|
||||
private final ExternalServiceCredentialsGenerator backupServiceCredentialsGenerator;
|
||||
|
||||
public SecureBackupController(ExternalServiceCredentialGenerator backupServiceCredentialGenerator) {
|
||||
this.backupServiceCredentialGenerator = backupServiceCredentialGenerator;
|
||||
public static ExternalServiceCredentialsGenerator credentialsGenerator(final SecureBackupServiceConfiguration cfg)
|
||||
throws DecoderException {
|
||||
return ExternalServiceCredentialsGenerator
|
||||
.builder(cfg.getUserAuthenticationTokenSharedSecret())
|
||||
.prependUsername(true)
|
||||
.build();
|
||||
}
|
||||
|
||||
public SecureBackupController(ExternalServiceCredentialsGenerator backupServiceCredentialsGenerator) {
|
||||
this.backupServiceCredentialsGenerator = backupServiceCredentialsGenerator;
|
||||
}
|
||||
|
||||
@Timed
|
||||
|
@ -29,6 +39,6 @@ public class SecureBackupController {
|
|||
@Path("/auth")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public ExternalServiceCredentials getAuth(@Auth AuthenticatedAccount auth) {
|
||||
return backupServiceCredentialGenerator.generateFor(auth.getAccount().getUuid().toString());
|
||||
return backupServiceCredentialsGenerator.generateForUuid(auth.getAccount().getUuid());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2013-2021 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
|
@ -11,17 +11,30 @@ import javax.ws.rs.GET;
|
|||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import org.apache.commons.codec.DecoderException;
|
||||
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialGenerator;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||
import org.whispersystems.textsecuregcm.configuration.SecureStorageServiceConfiguration;
|
||||
|
||||
@Path("/v1/storage")
|
||||
public class SecureStorageController {
|
||||
|
||||
private final ExternalServiceCredentialGenerator storageServiceCredentialGenerator;
|
||||
private final ExternalServiceCredentialsGenerator storageServiceCredentialsGenerator;
|
||||
|
||||
public SecureStorageController(ExternalServiceCredentialGenerator storageServiceCredentialGenerator) {
|
||||
this.storageServiceCredentialGenerator = storageServiceCredentialGenerator;
|
||||
public static ExternalServiceCredentialsGenerator credentialsGenerator(final SecureStorageServiceConfiguration cfg) {
|
||||
try {
|
||||
return ExternalServiceCredentialsGenerator
|
||||
.builder(cfg.decodeUserAuthenticationTokenSharedSecret())
|
||||
.prependUsername(true)
|
||||
.build();
|
||||
} catch (DecoderException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public SecureStorageController(ExternalServiceCredentialsGenerator storageServiceCredentialsGenerator) {
|
||||
this.storageServiceCredentialsGenerator = storageServiceCredentialsGenerator;
|
||||
}
|
||||
|
||||
@Timed
|
||||
|
@ -29,6 +42,6 @@ public class SecureStorageController {
|
|||
@Path("/auth")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public ExternalServiceCredentials getAuth(@Auth AuthenticatedAccount auth) {
|
||||
return storageServiceCredentialGenerator.generateFor(auth.getAccount().getUuid().toString());
|
||||
return storageServiceCredentialsGenerator.generateForUuid(auth.getAccount().getUuid());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,41 +1,43 @@
|
|||
/*
|
||||
* Copyright 2021 Signal Messenger, LLC
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.securebackup;
|
||||
|
||||
import static org.whispersystems.textsecuregcm.util.HeaderUtils.basicAuthHeader;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.net.HttpHeaders;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.time.Duration;
|
||||
import java.util.Base64;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialGenerator;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||
import org.whispersystems.textsecuregcm.configuration.SecureBackupServiceConfiguration;
|
||||
import org.whispersystems.textsecuregcm.http.FaultTolerantHttpClient;
|
||||
import org.whispersystems.textsecuregcm.util.HttpUtils;
|
||||
|
||||
/**
|
||||
* A client for sending requests to Signal's secure value recovery service on behalf of authenticated users.
|
||||
*/
|
||||
public class SecureBackupClient {
|
||||
|
||||
private final ExternalServiceCredentialGenerator secureBackupCredentialGenerator;
|
||||
private final ExternalServiceCredentialsGenerator secureBackupCredentialsGenerator;
|
||||
private final URI deleteUri;
|
||||
private final FaultTolerantHttpClient httpClient;
|
||||
|
||||
@VisibleForTesting
|
||||
static final String DELETE_PATH = "/v1/backup";
|
||||
|
||||
public SecureBackupClient(final ExternalServiceCredentialGenerator secureBackupCredentialGenerator, final Executor executor, final SecureBackupServiceConfiguration configuration) throws CertificateException {
|
||||
this.secureBackupCredentialGenerator = secureBackupCredentialGenerator;
|
||||
public SecureBackupClient(final ExternalServiceCredentialsGenerator secureBackupCredentialsGenerator, final Executor executor, final SecureBackupServiceConfiguration configuration) throws CertificateException {
|
||||
this.secureBackupCredentialsGenerator = secureBackupCredentialsGenerator;
|
||||
this.deleteUri = URI.create(configuration.getUri()).resolve(DELETE_PATH);
|
||||
this.httpClient = FaultTolerantHttpClient.newBuilder()
|
||||
.withCircuitBreaker(configuration.getCircuitBreakerConfiguration())
|
||||
|
@ -51,17 +53,16 @@ public class SecureBackupClient {
|
|||
}
|
||||
|
||||
public CompletableFuture<Void> deleteBackups(final UUID accountUuid) {
|
||||
final ExternalServiceCredentials credentials = secureBackupCredentialGenerator.generateFor(accountUuid.toString());
|
||||
final ExternalServiceCredentials credentials = secureBackupCredentialsGenerator.generateForUuid(accountUuid);
|
||||
|
||||
final HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(deleteUri)
|
||||
.DELETE()
|
||||
.header("Authorization", "Basic " + Base64.getEncoder().encodeToString(
|
||||
(credentials.username() + ":" + credentials.password()).getBytes(StandardCharsets.UTF_8)))
|
||||
.header(HttpHeaders.AUTHORIZATION, basicAuthHeader(credentials))
|
||||
.build();
|
||||
|
||||
return httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString()).thenApply(response -> {
|
||||
if (response.statusCode() >= 200 && response.statusCode() < 300) {
|
||||
if (HttpUtils.isSuccessfulResponse(response.statusCode())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,68 +1,68 @@
|
|||
/*
|
||||
* Copyright 2020 Signal Messenger, LLC
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.securestorage;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialGenerator;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
||||
import org.whispersystems.textsecuregcm.configuration.SecureStorageServiceConfiguration;
|
||||
import org.whispersystems.textsecuregcm.http.FaultTolerantHttpClient;
|
||||
import static org.whispersystems.textsecuregcm.util.HeaderUtils.basicAuthHeader;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.net.HttpHeaders;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.time.Duration;
|
||||
import java.util.Base64;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||
import org.whispersystems.textsecuregcm.configuration.SecureStorageServiceConfiguration;
|
||||
import org.whispersystems.textsecuregcm.http.FaultTolerantHttpClient;
|
||||
import org.whispersystems.textsecuregcm.util.HttpUtils;
|
||||
|
||||
/**
|
||||
* A client for sending requests to Signal's secure storage service on behalf of authenticated users.
|
||||
*/
|
||||
public class SecureStorageClient {
|
||||
|
||||
private final ExternalServiceCredentialGenerator storageServiceCredentialGenerator;
|
||||
private final ExternalServiceCredentialsGenerator storageServiceCredentialsGenerator;
|
||||
private final URI deleteUri;
|
||||
private final FaultTolerantHttpClient httpClient;
|
||||
|
||||
@VisibleForTesting
|
||||
static final String DELETE_PATH = "/v1/storage";
|
||||
|
||||
public SecureStorageClient(final ExternalServiceCredentialGenerator storageServiceCredentialGenerator, final Executor executor, final SecureStorageServiceConfiguration configuration) throws CertificateException {
|
||||
this.storageServiceCredentialGenerator = storageServiceCredentialGenerator;
|
||||
this.deleteUri = URI.create(configuration.getUri()).resolve(DELETE_PATH);
|
||||
public SecureStorageClient(final ExternalServiceCredentialsGenerator storageServiceCredentialsGenerator, final Executor executor, final SecureStorageServiceConfiguration configuration) throws CertificateException {
|
||||
this.storageServiceCredentialsGenerator = storageServiceCredentialsGenerator;
|
||||
this.deleteUri = URI.create(configuration.uri()).resolve(DELETE_PATH);
|
||||
this.httpClient = FaultTolerantHttpClient.newBuilder()
|
||||
.withCircuitBreaker(configuration.getCircuitBreakerConfiguration())
|
||||
.withRetry(configuration.getRetryConfiguration())
|
||||
.withCircuitBreaker(configuration.circuitBreakerConfig())
|
||||
.withRetry(configuration.retryConfig())
|
||||
.withVersion(HttpClient.Version.HTTP_1_1)
|
||||
.withConnectTimeout(Duration.ofSeconds(10))
|
||||
.withRedirect(HttpClient.Redirect.NEVER)
|
||||
.withExecutor(executor)
|
||||
.withName("secure-storage")
|
||||
.withSecurityProtocol(FaultTolerantHttpClient.SECURITY_PROTOCOL_TLS_1_3)
|
||||
.withTrustedServerCertificates(configuration.getStorageCaCertificates().toArray(new String[0]))
|
||||
.withTrustedServerCertificates(configuration.storageCaCertificates().toArray(new String[0]))
|
||||
.build();
|
||||
}
|
||||
|
||||
public CompletableFuture<Void> deleteStoredData(final UUID accountUuid) {
|
||||
final ExternalServiceCredentials credentials = storageServiceCredentialGenerator.generateFor(accountUuid.toString());
|
||||
final ExternalServiceCredentials credentials = storageServiceCredentialsGenerator.generateForUuid(accountUuid);
|
||||
|
||||
final HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(deleteUri)
|
||||
.DELETE()
|
||||
.header("Authorization", "Basic " + Base64.getEncoder().encodeToString(
|
||||
(credentials.username() + ":" + credentials.password()).getBytes(StandardCharsets.UTF_8)))
|
||||
.header(HttpHeaders.AUTHORIZATION, basicAuthHeader(credentials))
|
||||
.build();
|
||||
|
||||
return httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString()).thenApply(response -> {
|
||||
if (response.statusCode() >= 200 && response.statusCode() < 300) {
|
||||
if (HttpUtils.isSuccessfulResponse(response.statusCode())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,19 @@
|
|||
/*
|
||||
* Copyright 2022 Signal Messenger, LLC
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.util;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
||||
|
||||
public final class HeaderUtils {
|
||||
|
||||
|
@ -22,6 +27,16 @@ public final class HeaderUtils {
|
|||
// utility class
|
||||
}
|
||||
|
||||
public static String basicAuthHeader(final ExternalServiceCredentials credentials) {
|
||||
return basicAuthHeader(credentials.username(), credentials.password());
|
||||
}
|
||||
|
||||
public static String basicAuthHeader(final String username, final String password) {
|
||||
requireNonNull(username);
|
||||
requireNonNull(password);
|
||||
return "Basic " + Base64.getEncoder().encodeToString((username + ":" + password).getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static String getTimestampHeader() {
|
||||
return TIMESTAMP_HEADER + ":" + System.currentTimeMillis();
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.util;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.HexFormat;
|
||||
|
||||
public final class HmacUtils {
|
||||
|
||||
private static final HexFormat HEX = HexFormat.of();
|
||||
|
||||
private static final String HMAC_SHA_256 = "HmacSHA256";
|
||||
|
||||
private static final ThreadLocal<Mac> THREAD_LOCAL_HMAC_SHA_256 = ThreadLocal.withInitial(() -> {
|
||||
try {
|
||||
return Mac.getInstance(HMAC_SHA_256);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
|
||||
public static byte[] hmac256(final byte[] key, final byte[] input) {
|
||||
try {
|
||||
final Mac mac = THREAD_LOCAL_HMAC_SHA_256.get();
|
||||
mac.init(new SecretKeySpec(key, HMAC_SHA_256));
|
||||
return mac.doFinal(input);
|
||||
} catch (final InvalidKeyException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] hmac256(final byte[] key, final String input) {
|
||||
return hmac256(key, input.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
public static String hmac256ToHexString(final byte[] key, final byte[] input) {
|
||||
return HEX.formatHex(hmac256(key, input));
|
||||
}
|
||||
|
||||
public static String hmac256ToHexString(final byte[] key, final String input) {
|
||||
return hmac256ToHexString(key, input.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
public static byte[] hmac256Truncated(final byte[] key, final byte[] input, final int length) {
|
||||
return Util.truncate(hmac256(key, input), length);
|
||||
}
|
||||
|
||||
public static byte[] hmac256Truncated(final byte[] key, final String input, final int length) {
|
||||
return hmac256Truncated(key, input.getBytes(StandardCharsets.UTF_8), length);
|
||||
}
|
||||
|
||||
public static String hmac256TruncatedToHexString(final byte[] key, final byte[] input, final int length) {
|
||||
return HEX.formatHex(Util.truncate(hmac256(key, input), length));
|
||||
}
|
||||
|
||||
public static String hmac256TruncatedToHexString(final byte[] key, final String input, final int length) {
|
||||
return hmac256TruncatedToHexString(key, input.getBytes(StandardCharsets.UTF_8), length);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.util;
|
||||
|
||||
public final class HttpUtils {
|
||||
|
||||
private HttpUtils() {
|
||||
// utility class
|
||||
}
|
||||
|
||||
public static boolean isSuccessfulResponse(final int statusCode) {
|
||||
return statusCode >= 200 && statusCode < 300;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2013-2021 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
|
@ -23,8 +23,10 @@ import java.util.concurrent.Executors;
|
|||
import net.sourceforge.argparse4j.inf.Namespace;
|
||||
import net.sourceforge.argparse4j.inf.Subparser;
|
||||
import org.whispersystems.textsecuregcm.WhisperServerConfiguration;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialGenerator;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
||||
import org.whispersystems.textsecuregcm.controllers.SecureBackupController;
|
||||
import org.whispersystems.textsecuregcm.controllers.SecureStorageController;
|
||||
import org.whispersystems.textsecuregcm.experiment.ExperimentEnrollmentManager;
|
||||
import org.whispersystems.textsecuregcm.push.ClientPresenceManager;
|
||||
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
|
||||
|
@ -103,11 +105,11 @@ public class AssignUsernameCommand extends EnvironmentCommand<WhisperServerConfi
|
|||
ExecutorService storageServiceExecutor = environment.lifecycle()
|
||||
.executorService(name(getClass(), "storageService-%d")).maxThreads(8).minThreads(1).build();
|
||||
|
||||
ExternalServiceCredentialGenerator backupCredentialsGenerator = new ExternalServiceCredentialGenerator(
|
||||
configuration.getSecureBackupServiceConfiguration().getUserAuthenticationTokenSharedSecret(), true);
|
||||
ExternalServiceCredentialGenerator storageCredentialsGenerator = new ExternalServiceCredentialGenerator(
|
||||
configuration.getSecureStorageServiceConfiguration().getUserAuthenticationTokenSharedSecret(), true);
|
||||
|
||||
ExternalServiceCredentialsGenerator backupCredentialsGenerator = SecureBackupController.credentialsGenerator(
|
||||
configuration.getSecureBackupServiceConfiguration());
|
||||
ExternalServiceCredentialsGenerator storageCredentialsGenerator = SecureStorageController.credentialsGenerator(
|
||||
configuration.getSecureStorageServiceConfiguration());
|
||||
|
||||
DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager = new DynamicConfigurationManager<>(
|
||||
configuration.getAppConfig().getApplication(), configuration.getAppConfig().getEnvironment(),
|
||||
configuration.getAppConfig().getConfigurationName(), DynamicConfiguration.class);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2013-2021 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
|
@ -25,8 +25,10 @@ import net.sourceforge.argparse4j.inf.Subparser;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.whispersystems.textsecuregcm.WhisperServerConfiguration;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialGenerator;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
||||
import org.whispersystems.textsecuregcm.controllers.SecureBackupController;
|
||||
import org.whispersystems.textsecuregcm.controllers.SecureStorageController;
|
||||
import org.whispersystems.textsecuregcm.experiment.ExperimentEnrollmentManager;
|
||||
import org.whispersystems.textsecuregcm.push.ClientPresenceManager;
|
||||
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
|
||||
|
@ -105,11 +107,11 @@ public class DeleteUserCommand extends EnvironmentCommand<WhisperServerConfigura
|
|||
ExecutorService storageServiceExecutor = environment.lifecycle()
|
||||
.executorService(name(getClass(), "storageService-%d")).maxThreads(8).minThreads(1).build();
|
||||
|
||||
ExternalServiceCredentialGenerator backupCredentialsGenerator = new ExternalServiceCredentialGenerator(
|
||||
configuration.getSecureBackupServiceConfiguration().getUserAuthenticationTokenSharedSecret(), true);
|
||||
ExternalServiceCredentialGenerator storageCredentialsGenerator = new ExternalServiceCredentialGenerator(
|
||||
configuration.getSecureStorageServiceConfiguration().getUserAuthenticationTokenSharedSecret(), true);
|
||||
|
||||
ExternalServiceCredentialsGenerator backupCredentialsGenerator = SecureBackupController.credentialsGenerator(
|
||||
configuration.getSecureBackupServiceConfiguration());
|
||||
ExternalServiceCredentialsGenerator storageCredentialsGenerator = SecureStorageController.credentialsGenerator(
|
||||
configuration.getSecureStorageServiceConfiguration());
|
||||
|
||||
DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager = new DynamicConfigurationManager<>(
|
||||
configuration.getAppConfig().getApplication(), configuration.getAppConfig().getEnvironment(),
|
||||
configuration.getAppConfig().getConfigurationName(), DynamicConfiguration.class);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2013-2021 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
|
@ -24,8 +24,10 @@ import java.util.concurrent.Executors;
|
|||
import net.sourceforge.argparse4j.inf.Namespace;
|
||||
import net.sourceforge.argparse4j.inf.Subparser;
|
||||
import org.whispersystems.textsecuregcm.WhisperServerConfiguration;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialGenerator;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
||||
import org.whispersystems.textsecuregcm.controllers.SecureBackupController;
|
||||
import org.whispersystems.textsecuregcm.controllers.SecureStorageController;
|
||||
import org.whispersystems.textsecuregcm.experiment.ExperimentEnrollmentManager;
|
||||
import org.whispersystems.textsecuregcm.push.ClientPresenceManager;
|
||||
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
|
||||
|
@ -108,11 +110,11 @@ public class SetUserDiscoverabilityCommand extends EnvironmentCommand<WhisperSer
|
|||
ExecutorService storageServiceExecutor = environment.lifecycle()
|
||||
.executorService(name(getClass(), "storageService-%d")).maxThreads(8).minThreads(1).build();
|
||||
|
||||
ExternalServiceCredentialGenerator backupCredentialsGenerator = new ExternalServiceCredentialGenerator(
|
||||
configuration.getSecureBackupServiceConfiguration().getUserAuthenticationTokenSharedSecret(), true);
|
||||
ExternalServiceCredentialGenerator storageCredentialsGenerator = new ExternalServiceCredentialGenerator(
|
||||
configuration.getSecureStorageServiceConfiguration().getUserAuthenticationTokenSharedSecret(), true);
|
||||
|
||||
ExternalServiceCredentialsGenerator backupCredentialsGenerator = SecureBackupController.credentialsGenerator(
|
||||
configuration.getSecureBackupServiceConfiguration());
|
||||
ExternalServiceCredentialsGenerator storageCredentialsGenerator = SecureStorageController.credentialsGenerator(
|
||||
configuration.getSecureStorageServiceConfiguration());
|
||||
|
||||
DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager = new DynamicConfigurationManager<>(
|
||||
configuration.getAppConfig().getApplication(), configuration.getAppConfig().getEnvironment(),
|
||||
configuration.getAppConfig().getConfigurationName(), DynamicConfiguration.class);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2013-2021 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
|
@ -65,12 +65,13 @@ import org.mockito.stubbing.Answer;
|
|||
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
||||
import org.whispersystems.textsecuregcm.auth.AuthenticationCredentials;
|
||||
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialGenerator;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||
import org.whispersystems.textsecuregcm.auth.StoredRegistrationLock;
|
||||
import org.whispersystems.textsecuregcm.auth.StoredVerificationCode;
|
||||
import org.whispersystems.textsecuregcm.auth.TurnTokenGenerator;
|
||||
import org.whispersystems.textsecuregcm.captcha.AssessmentResult;
|
||||
import org.whispersystems.textsecuregcm.captcha.CaptchaChecker;
|
||||
import org.whispersystems.textsecuregcm.configuration.SecureStorageServiceConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicCaptchaConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
||||
import org.whispersystems.textsecuregcm.entities.AccountAttributes;
|
||||
|
@ -111,6 +112,7 @@ import org.whispersystems.textsecuregcm.storage.UsernameReservationNotFoundExcep
|
|||
import org.whispersystems.textsecuregcm.tests.util.AccountsHelper;
|
||||
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
|
||||
import org.whispersystems.textsecuregcm.util.Hex;
|
||||
import org.whispersystems.textsecuregcm.util.MockHelper;
|
||||
import org.whispersystems.textsecuregcm.util.SystemMapper;
|
||||
import org.whispersystems.textsecuregcm.util.TestClock;
|
||||
|
||||
|
@ -169,7 +171,13 @@ class AccountControllerTest {
|
|||
private static DynamicConfigurationManager dynamicConfigurationManager = mock(DynamicConfigurationManager.class);
|
||||
|
||||
private byte[] registration_lock_key = new byte[32];
|
||||
private static ExternalServiceCredentialGenerator storageCredentialGenerator = new ExternalServiceCredentialGenerator(new byte[32], new byte[32], false);
|
||||
|
||||
private static final SecureStorageServiceConfiguration STORAGE_CFG = MockHelper.buildMock(
|
||||
SecureStorageServiceConfiguration.class,
|
||||
cfg -> when(cfg.decodeUserAuthenticationTokenSharedSecret()).thenReturn(new byte[32]));
|
||||
|
||||
private static final ExternalServiceCredentialsGenerator STORAGE_CREDENTIAL_GENERATOR = SecureStorageController
|
||||
.credentialsGenerator(STORAGE_CFG);
|
||||
|
||||
private static final ResourceExtension resources = ResourceExtension.builder()
|
||||
.addProvider(AuthHelper.getAuthFilter())
|
||||
|
@ -192,7 +200,7 @@ class AccountControllerTest {
|
|||
captchaChecker,
|
||||
pushNotificationManager,
|
||||
changeNumberManager,
|
||||
storageCredentialGenerator,
|
||||
STORAGE_CREDENTIAL_GENERATOR,
|
||||
clientPresenceManager,
|
||||
testClock))
|
||||
.build();
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2020 Signal Messenger, LLC
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
|
@ -23,18 +23,18 @@ import java.util.concurrent.ExecutorService;
|
|||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialGenerator;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||
import org.whispersystems.textsecuregcm.configuration.SecureBackupServiceConfiguration;
|
||||
|
||||
class SecureBackupClientTest {
|
||||
|
||||
private UUID accountUuid;
|
||||
private ExternalServiceCredentialGenerator credentialGenerator;
|
||||
private ExternalServiceCredentialsGenerator credentialsGenerator;
|
||||
private ExecutorService httpExecutor;
|
||||
|
||||
private SecureBackupClient secureStorageClient;
|
||||
|
@ -47,7 +47,7 @@ class SecureBackupClientTest {
|
|||
@BeforeEach
|
||||
void setUp() throws CertificateException {
|
||||
accountUuid = UUID.randomUUID();
|
||||
credentialGenerator = mock(ExternalServiceCredentialGenerator.class);
|
||||
credentialsGenerator = mock(ExternalServiceCredentialsGenerator.class);
|
||||
httpExecutor = Executors.newSingleThreadExecutor();
|
||||
|
||||
final SecureBackupServiceConfiguration config = new SecureBackupServiceConfiguration();
|
||||
|
@ -101,7 +101,7 @@ class SecureBackupClientTest {
|
|||
-----END CERTIFICATE-----
|
||||
"""));
|
||||
|
||||
secureStorageClient = new SecureBackupClient(credentialGenerator, httpExecutor, config);
|
||||
secureStorageClient = new SecureBackupClient(credentialsGenerator, httpExecutor, config);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
|
@ -115,7 +115,7 @@ class SecureBackupClientTest {
|
|||
final String username = RandomStringUtils.randomAlphabetic(16);
|
||||
final String password = RandomStringUtils.randomAlphanumeric(32);
|
||||
|
||||
when(credentialGenerator.generateFor(accountUuid.toString())).thenReturn(new ExternalServiceCredentials(username, password));
|
||||
when(credentialsGenerator.generateForUuid(accountUuid)).thenReturn(new ExternalServiceCredentials(username, password));
|
||||
|
||||
wireMock.stubFor(delete(urlEqualTo(SecureBackupClient.DELETE_PATH))
|
||||
.withBasicAuth(username, password)
|
||||
|
@ -130,7 +130,7 @@ class SecureBackupClientTest {
|
|||
final String username = RandomStringUtils.randomAlphabetic(16);
|
||||
final String password = RandomStringUtils.randomAlphanumeric(32);
|
||||
|
||||
when(credentialGenerator.generateFor(accountUuid.toString())).thenReturn(new ExternalServiceCredentials(username, password));
|
||||
when(credentialsGenerator.generateForUuid(accountUuid)).thenReturn(new ExternalServiceCredentials(username, password));
|
||||
|
||||
wireMock.stubFor(delete(urlEqualTo(SecureBackupClient.DELETE_PATH))
|
||||
.withBasicAuth(username, password)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2020 Signal Messenger, LLC
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
|
@ -27,14 +27,14 @@ import org.junit.jupiter.api.AfterEach;
|
|||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialGenerator;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||
import org.whispersystems.textsecuregcm.configuration.SecureStorageServiceConfiguration;
|
||||
|
||||
class SecureStorageClientTest {
|
||||
|
||||
private UUID accountUuid;
|
||||
private ExternalServiceCredentialGenerator credentialGenerator;
|
||||
private ExternalServiceCredentialsGenerator credentialsGenerator;
|
||||
private ExecutorService httpExecutor;
|
||||
|
||||
private SecureStorageClient secureStorageClient;
|
||||
|
@ -47,61 +47,60 @@ class SecureStorageClientTest {
|
|||
@BeforeEach
|
||||
void setUp() throws CertificateException {
|
||||
accountUuid = UUID.randomUUID();
|
||||
credentialGenerator = mock(ExternalServiceCredentialGenerator.class);
|
||||
credentialsGenerator = mock(ExternalServiceCredentialsGenerator.class);
|
||||
httpExecutor = Executors.newSingleThreadExecutor();
|
||||
|
||||
final SecureStorageServiceConfiguration config = new SecureStorageServiceConfiguration();
|
||||
config.setUri("http://localhost:" + wireMock.getPort());
|
||||
final SecureStorageServiceConfiguration config = new SecureStorageServiceConfiguration(
|
||||
"not_used",
|
||||
"http://localhost:" + wireMock.getPort(),
|
||||
List.of("""
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICZDCCAc2gAwIBAgIBADANBgkqhkiG9w0BAQ0FADBPMQswCQYDVQQGEwJ1czEL
|
||||
MAkGA1UECAwCVVMxHjAcBgNVBAoMFVNpZ25hbCBNZXNzZW5nZXIsIExMQzETMBEG
|
||||
A1UEAwwKc2lnbmFsLm9yZzAeFw0yMDEyMjMyMjQ3NTlaFw0zMDEyMjEyMjQ3NTla
|
||||
ME8xCzAJBgNVBAYTAnVzMQswCQYDVQQIDAJVUzEeMBwGA1UECgwVU2lnbmFsIE1l
|
||||
c3NlbmdlciwgTExDMRMwEQYDVQQDDApzaWduYWwub3JnMIGfMA0GCSqGSIb3DQEB
|
||||
AQUAA4GNADCBiQKBgQCfSLcZNHYqbxSsgWp4JvbPRHjQTrlsrKrgD2q7f/OY6O3Y
|
||||
/X0QNcNSOJpliN8rmzwslfsrXHO3q1diGRw4xHogUJZ/7NQrHiP/zhN0VTDh49pD
|
||||
ZpjXVyUbayLS/6qM5arKxBspzEFBb5v8cF6bPr76SO/rpGXiI0j6yJKX6fRiKwID
|
||||
AQABo1AwTjAdBgNVHQ4EFgQU6Jrs/Fmj0z4dA3wvdq/WqA4P49IwHwYDVR0jBBgw
|
||||
FoAU6Jrs/Fmj0z4dA3wvdq/WqA4P49IwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0B
|
||||
AQ0FAAOBgQB+5d5+NtzLILfrc9QmJdIO1YeDP64JmFwTER0kEUouRsb9UwknVWZa
|
||||
y7MTM4NoBV1k0zb5LAk89SIDPr/maW5AsLtEomzjnEiomjoMBUdNe3YCgQReoLnr
|
||||
R/QaUNbrCjTGYfBsjGbIzmkWPUyTec2ZdRyJ8JiVl386+6CZkxnndQ==
|
||||
-----END CERTIFICATE-----
|
||||
""",
|
||||
"""
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEpDCCAowCCQC43PUTWSADVjANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls
|
||||
b2NhbGhvc3QwHhcNMjIxMDE3MjA0NTM0WhcNMjMxMDE3MjA0NTM0WjAUMRIwEAYD
|
||||
VQQDDAlsb2NhbGhvc3QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDV
|
||||
x1cdEd2ffQTlTXWRiCHGcrlYf4RJnctt9sw/BuHWTLXBu5LhyJSGn5LRszO/NCXK
|
||||
Z/cmGR7pLj366RtiwL+Qo3nhvDCK7T9xZeNIusM6XMcMK9D/DGCYPqtjQz8NXd9V
|
||||
ajBBe6nwTDTa+oqX8Mt89foWNkg5Il/lY62u9Dr18LRZ2W9zzYi3Q9/K0CbIX6pM
|
||||
yVlPIO5rITOR2IsbeyqsO9jufgX5lP4ZKLLBAP1b7usjC4YdvWacjQg/rK5aay1x
|
||||
jC2HCDgo/4N30QVXzSA9nFfSe6AE/xkStK4819JqOkY5JsJCbef1P3hOOdSLEjbp
|
||||
xq3MjOs6G6dOgteaAGs10vx7dHxDWETTIiD7BIZ9zRYgOF5bkCaIUO+JfySE1MHD
|
||||
KBAFLoRuvmRev5Ln5R0MCHpUMSmMNgJqz+RWZV3g/gpYbuWiHgJOwL1393eK50Bg
|
||||
W7SXQ8EjJj2yXZSH+1gPzN0DRoJZiaBoTPnCL2qUgvwFpW1PJsM5FDyUJFUoK5kK
|
||||
HLBBSKAPt6ZlSrUe2nBgJv7EF1GK+fTU08LXgW33OpLceGPa0zTShkukQUMtUtZ8
|
||||
GqhO12ohMzEupIu5Xurthq4VVUrzHUdj1ZZRMhAbfLU36sd03MMyL/xBqTN6dzCa
|
||||
GDGIPGpYjAllZ5xMRt2kZdv+Kr6oo3u2nLUIsqI7KQIDAQABMA0GCSqGSIb3DQEB
|
||||
CwUAA4ICAQCB5s43YF35ssf5YONW5iAaifGpi1o0866xfeOybtohFGvQ7V2W34i9
|
||||
TYBCt8+0hgatMcvZ08f0vqig1i7nrvYcE1hnhL7JNkU8qm0s9ytHZt6j62nB0kd/
|
||||
uqE2hOEQalTf/2TGPV0CCgiqLyd8lEUQvQeA38wktwUeZpVnErlzHeMR2CvV3K8R
|
||||
u4vV6SnBcf+TAt56RKYZkPyvZj5llQPo14Glyoo8qZES7Ky1SHmM0GL+baPRBjRW
|
||||
3KgSt98Wyu4yr9qu21JpnbAnLhBfzfSKjSeCRgFElUE1GIaFGRZ7ypA74dUKeLnb
|
||||
/VUWrszmUhGaEjV9dpI6x6B/kSpQMtIQqBaKRY2ALUeEujS/rURi4iMDwSU+GkSH
|
||||
cyEvZKS97OA/dWeXfLXdo4beDBRG93bI4rQnDg5+VdlBOkQSLueb8x6/VThMoC5d
|
||||
vZiotFQHseljQAdTkNa6tBu6c4XDYPCKB3CfkMYOlCfTS7Acn5G6dxTPKBtLGBnL
|
||||
nQfYyzuwYkN09+2PVzt6auBHr3To7uoclkxX+hxyvPIwIZ0N6b4tQR1FCAkvg29Q
|
||||
WIOjZOKGW690ESKCKOnFjUHVO0HpuWnT81URTuY62FXsYdVc2wE4v0E04mEbqQ0P
|
||||
lY6ZKNA81Lm3YADYtObmK1IUrOPo9BeIaPy0UM08SmN880Vunqa91Q==
|
||||
-----END CERTIFICATE-----
|
||||
"""));
|
||||
|
||||
// This is a randomly-generated, throwaway certificate that's not actually connected to anything
|
||||
config.setStorageCaCertificates(List.of("""
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICZDCCAc2gAwIBAgIBADANBgkqhkiG9w0BAQ0FADBPMQswCQYDVQQGEwJ1czEL
|
||||
MAkGA1UECAwCVVMxHjAcBgNVBAoMFVNpZ25hbCBNZXNzZW5nZXIsIExMQzETMBEG
|
||||
A1UEAwwKc2lnbmFsLm9yZzAeFw0yMDEyMjMyMjQ3NTlaFw0zMDEyMjEyMjQ3NTla
|
||||
ME8xCzAJBgNVBAYTAnVzMQswCQYDVQQIDAJVUzEeMBwGA1UECgwVU2lnbmFsIE1l
|
||||
c3NlbmdlciwgTExDMRMwEQYDVQQDDApzaWduYWwub3JnMIGfMA0GCSqGSIb3DQEB
|
||||
AQUAA4GNADCBiQKBgQCfSLcZNHYqbxSsgWp4JvbPRHjQTrlsrKrgD2q7f/OY6O3Y
|
||||
/X0QNcNSOJpliN8rmzwslfsrXHO3q1diGRw4xHogUJZ/7NQrHiP/zhN0VTDh49pD
|
||||
ZpjXVyUbayLS/6qM5arKxBspzEFBb5v8cF6bPr76SO/rpGXiI0j6yJKX6fRiKwID
|
||||
AQABo1AwTjAdBgNVHQ4EFgQU6Jrs/Fmj0z4dA3wvdq/WqA4P49IwHwYDVR0jBBgw
|
||||
FoAU6Jrs/Fmj0z4dA3wvdq/WqA4P49IwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0B
|
||||
AQ0FAAOBgQB+5d5+NtzLILfrc9QmJdIO1YeDP64JmFwTER0kEUouRsb9UwknVWZa
|
||||
y7MTM4NoBV1k0zb5LAk89SIDPr/maW5AsLtEomzjnEiomjoMBUdNe3YCgQReoLnr
|
||||
R/QaUNbrCjTGYfBsjGbIzmkWPUyTec2ZdRyJ8JiVl386+6CZkxnndQ==
|
||||
-----END CERTIFICATE-----
|
||||
""",
|
||||
"""
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEpDCCAowCCQC43PUTWSADVjANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls
|
||||
b2NhbGhvc3QwHhcNMjIxMDE3MjA0NTM0WhcNMjMxMDE3MjA0NTM0WjAUMRIwEAYD
|
||||
VQQDDAlsb2NhbGhvc3QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDV
|
||||
x1cdEd2ffQTlTXWRiCHGcrlYf4RJnctt9sw/BuHWTLXBu5LhyJSGn5LRszO/NCXK
|
||||
Z/cmGR7pLj366RtiwL+Qo3nhvDCK7T9xZeNIusM6XMcMK9D/DGCYPqtjQz8NXd9V
|
||||
ajBBe6nwTDTa+oqX8Mt89foWNkg5Il/lY62u9Dr18LRZ2W9zzYi3Q9/K0CbIX6pM
|
||||
yVlPIO5rITOR2IsbeyqsO9jufgX5lP4ZKLLBAP1b7usjC4YdvWacjQg/rK5aay1x
|
||||
jC2HCDgo/4N30QVXzSA9nFfSe6AE/xkStK4819JqOkY5JsJCbef1P3hOOdSLEjbp
|
||||
xq3MjOs6G6dOgteaAGs10vx7dHxDWETTIiD7BIZ9zRYgOF5bkCaIUO+JfySE1MHD
|
||||
KBAFLoRuvmRev5Ln5R0MCHpUMSmMNgJqz+RWZV3g/gpYbuWiHgJOwL1393eK50Bg
|
||||
W7SXQ8EjJj2yXZSH+1gPzN0DRoJZiaBoTPnCL2qUgvwFpW1PJsM5FDyUJFUoK5kK
|
||||
HLBBSKAPt6ZlSrUe2nBgJv7EF1GK+fTU08LXgW33OpLceGPa0zTShkukQUMtUtZ8
|
||||
GqhO12ohMzEupIu5Xurthq4VVUrzHUdj1ZZRMhAbfLU36sd03MMyL/xBqTN6dzCa
|
||||
GDGIPGpYjAllZ5xMRt2kZdv+Kr6oo3u2nLUIsqI7KQIDAQABMA0GCSqGSIb3DQEB
|
||||
CwUAA4ICAQCB5s43YF35ssf5YONW5iAaifGpi1o0866xfeOybtohFGvQ7V2W34i9
|
||||
TYBCt8+0hgatMcvZ08f0vqig1i7nrvYcE1hnhL7JNkU8qm0s9ytHZt6j62nB0kd/
|
||||
uqE2hOEQalTf/2TGPV0CCgiqLyd8lEUQvQeA38wktwUeZpVnErlzHeMR2CvV3K8R
|
||||
u4vV6SnBcf+TAt56RKYZkPyvZj5llQPo14Glyoo8qZES7Ky1SHmM0GL+baPRBjRW
|
||||
3KgSt98Wyu4yr9qu21JpnbAnLhBfzfSKjSeCRgFElUE1GIaFGRZ7ypA74dUKeLnb
|
||||
/VUWrszmUhGaEjV9dpI6x6B/kSpQMtIQqBaKRY2ALUeEujS/rURi4iMDwSU+GkSH
|
||||
cyEvZKS97OA/dWeXfLXdo4beDBRG93bI4rQnDg5+VdlBOkQSLueb8x6/VThMoC5d
|
||||
vZiotFQHseljQAdTkNa6tBu6c4XDYPCKB3CfkMYOlCfTS7Acn5G6dxTPKBtLGBnL
|
||||
nQfYyzuwYkN09+2PVzt6auBHr3To7uoclkxX+hxyvPIwIZ0N6b4tQR1FCAkvg29Q
|
||||
WIOjZOKGW690ESKCKOnFjUHVO0HpuWnT81URTuY62FXsYdVc2wE4v0E04mEbqQ0P
|
||||
lY6ZKNA81Lm3YADYtObmK1IUrOPo9BeIaPy0UM08SmN880Vunqa91Q==
|
||||
-----END CERTIFICATE-----
|
||||
"""));
|
||||
|
||||
secureStorageClient = new SecureStorageClient(credentialGenerator, httpExecutor, config);
|
||||
secureStorageClient = new SecureStorageClient(credentialsGenerator, httpExecutor, config);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
|
@ -115,7 +114,7 @@ class SecureStorageClientTest {
|
|||
final String username = RandomStringUtils.randomAlphabetic(16);
|
||||
final String password = RandomStringUtils.randomAlphanumeric(32);
|
||||
|
||||
when(credentialGenerator.generateFor(accountUuid.toString())).thenReturn(new ExternalServiceCredentials(username, password));
|
||||
when(credentialsGenerator.generateForUuid(accountUuid)).thenReturn(new ExternalServiceCredentials(username, password));
|
||||
|
||||
wireMock.stubFor(delete(urlEqualTo(SecureStorageClient.DELETE_PATH))
|
||||
.withBasicAuth(username, password)
|
||||
|
@ -130,7 +129,7 @@ class SecureStorageClientTest {
|
|||
final String username = RandomStringUtils.randomAlphabetic(16);
|
||||
final String password = RandomStringUtils.randomAlphanumeric(32);
|
||||
|
||||
when(credentialGenerator.generateFor(accountUuid.toString())).thenReturn(new ExternalServiceCredentials(username, password));
|
||||
when(credentialsGenerator.generateForUuid(accountUuid)).thenReturn(new ExternalServiceCredentials(username, password));
|
||||
|
||||
wireMock.stubFor(delete(urlEqualTo(SecureStorageClient.DELETE_PATH))
|
||||
.withBasicAuth(username, password)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
|
@ -8,15 +8,18 @@ package org.whispersystems.textsecuregcm.tests.auth;
|
|||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialGenerator;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||
|
||||
class ExternalServiceCredentialsGeneratorTest {
|
||||
|
||||
@Test
|
||||
void testGenerateDerivedUsername() {
|
||||
ExternalServiceCredentialGenerator generator = new ExternalServiceCredentialGenerator(new byte[32], new byte[32]);
|
||||
ExternalServiceCredentials credentials = generator.generateFor("+14152222222");
|
||||
final ExternalServiceCredentialsGenerator generator = ExternalServiceCredentialsGenerator
|
||||
.builder(new byte[32])
|
||||
.withUserDerivationKey(new byte[32])
|
||||
.build();
|
||||
final ExternalServiceCredentials credentials = generator.generateFor("+14152222222");
|
||||
|
||||
assertThat(credentials.username()).isNotEqualTo("+14152222222");
|
||||
assertThat(credentials.password().startsWith("+14152222222")).isFalse();
|
||||
|
@ -24,8 +27,10 @@ class ExternalServiceCredentialsGeneratorTest {
|
|||
|
||||
@Test
|
||||
void testGenerateNoDerivedUsername() {
|
||||
ExternalServiceCredentialGenerator generator = new ExternalServiceCredentialGenerator(new byte[32], new byte[32], false);
|
||||
ExternalServiceCredentials credentials = generator.generateFor("+14152222222");
|
||||
final ExternalServiceCredentialsGenerator generator = ExternalServiceCredentialsGenerator
|
||||
.builder(new byte[32])
|
||||
.build();
|
||||
final ExternalServiceCredentials credentials = generator.generateFor("+14152222222");
|
||||
|
||||
assertThat(credentials.username()).isEqualTo("+14152222222");
|
||||
assertThat(credentials.password().startsWith("+14152222222")).isTrue();
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2013-2021 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
|
@ -16,21 +16,29 @@ 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.mockito.Mockito;
|
||||
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
||||
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialGenerator;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||
import org.whispersystems.textsecuregcm.configuration.ArtServiceConfiguration;
|
||||
import org.whispersystems.textsecuregcm.controllers.ArtController;
|
||||
import org.whispersystems.textsecuregcm.limits.RateLimiter;
|
||||
import org.whispersystems.textsecuregcm.limits.RateLimiters;
|
||||
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
|
||||
import org.whispersystems.textsecuregcm.util.MockHelper;
|
||||
import org.whispersystems.textsecuregcm.util.SystemMapper;
|
||||
|
||||
@ExtendWith(DropwizardExtensionsSupport.class)
|
||||
class ArtControllerTest {
|
||||
|
||||
private static final ExternalServiceCredentialGenerator artCredentialGenerator = new ExternalServiceCredentialGenerator(
|
||||
new byte[32], new byte[32], true, false, false);
|
||||
private static final ArtServiceConfiguration ART_SERVICE_CONFIGURATION = MockHelper.buildMock(
|
||||
ArtServiceConfiguration.class,
|
||||
cfg -> {
|
||||
Mockito.when(cfg.getUserAuthenticationTokenSharedSecret()).thenReturn(new byte[32]);
|
||||
Mockito.when(cfg.getUserAuthenticationTokenUserIdSecret()).thenReturn(new byte[32]);
|
||||
});
|
||||
private static final ExternalServiceCredentialsGenerator artCredentialsGenerator = ArtController.credentialsGenerator(ART_SERVICE_CONFIGURATION);
|
||||
private static final RateLimiter rateLimiter = mock(RateLimiter.class);
|
||||
private static final RateLimiters rateLimiters = mock(RateLimiters.class);
|
||||
|
||||
|
@ -40,7 +48,7 @@ class ArtControllerTest {
|
|||
ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)))
|
||||
.setMapper(SystemMapper.getMapper())
|
||||
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
|
||||
.addResource(new ArtController(rateLimiters, artCredentialGenerator))
|
||||
.addResource(new ArtController(rateLimiters, artCredentialsGenerator))
|
||||
.build();
|
||||
|
||||
@Test
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2013-2021 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
|
@ -26,15 +26,15 @@ import org.junit.jupiter.api.Test;
|
|||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
||||
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialGenerator;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||
import org.whispersystems.textsecuregcm.controllers.DirectoryController;
|
||||
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
|
||||
|
||||
@ExtendWith(DropwizardExtensionsSupport.class)
|
||||
class DirectoryControllerTest {
|
||||
|
||||
private static final ExternalServiceCredentialGenerator directoryCredentialsGenerator = mock(ExternalServiceCredentialGenerator.class);
|
||||
private static final ExternalServiceCredentialsGenerator directoryCredentialsGenerator = mock(ExternalServiceCredentialsGenerator.class);
|
||||
private static final ExternalServiceCredentials validCredentials = new ExternalServiceCredentials("username", "password");
|
||||
|
||||
private static final ResourceExtension resources = ResourceExtension.builder()
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2021 Signal Messenger, LLC
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
|
@ -15,8 +15,9 @@ import java.time.ZoneId;
|
|||
import java.util.UUID;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialGenerator;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||
import org.whispersystems.textsecuregcm.configuration.DirectoryV2ClientConfiguration;
|
||||
import org.whispersystems.textsecuregcm.controllers.DirectoryV2Controller;
|
||||
import org.whispersystems.textsecuregcm.storage.Account;
|
||||
import org.whispersystems.textsecuregcm.storage.Device;
|
||||
|
@ -26,11 +27,12 @@ class DirectoryControllerV2Test {
|
|||
|
||||
@Test
|
||||
void testAuthToken() {
|
||||
final ExternalServiceCredentialGenerator credentialGenerator = new ExternalServiceCredentialGenerator(
|
||||
new byte[]{0x1}, new byte[]{0x2}, true, false,
|
||||
Clock.fixed(Instant.ofEpochSecond(1633738643L), ZoneId.of("Etc/UTC")));
|
||||
final ExternalServiceCredentialsGenerator credentialsGenerator = DirectoryV2Controller.credentialsGenerator(
|
||||
new DirectoryV2ClientConfiguration(new byte[]{0x1}, new byte[]{0x2}),
|
||||
Clock.fixed(Instant.ofEpochSecond(1633738643L), ZoneId.of("Etc/UTC"))
|
||||
);
|
||||
|
||||
final DirectoryV2Controller controller = new DirectoryV2Controller(credentialGenerator);
|
||||
final DirectoryV2Controller controller = new DirectoryV2Controller(credentialsGenerator);
|
||||
|
||||
final Account account = mock(Account.class);
|
||||
final UUID uuid = UUID.fromString("11111111-1111-1111-1111-111111111111");
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2013-2021 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
|
@ -25,8 +25,8 @@ import org.junit.jupiter.api.Test;
|
|||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
||||
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialGenerator;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||
import org.whispersystems.textsecuregcm.controllers.PaymentsController;
|
||||
import org.whispersystems.textsecuregcm.currency.CurrencyConversionManager;
|
||||
import org.whispersystems.textsecuregcm.entities.CurrencyConversionEntity;
|
||||
|
@ -36,7 +36,7 @@ import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
|
|||
@ExtendWith(DropwizardExtensionsSupport.class)
|
||||
class PaymentsControllerTest {
|
||||
|
||||
private static final ExternalServiceCredentialGenerator paymentsCredentialGenerator = mock(ExternalServiceCredentialGenerator.class);
|
||||
private static final ExternalServiceCredentialsGenerator paymentsCredentialsGenerator = mock(ExternalServiceCredentialsGenerator.class);
|
||||
private static final CurrencyConversionManager currencyManager = mock(CurrencyConversionManager.class);
|
||||
|
||||
private final ExternalServiceCredentials validCredentials = new ExternalServiceCredentials("username", "password");
|
||||
|
@ -46,13 +46,13 @@ class PaymentsControllerTest {
|
|||
.addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>(
|
||||
ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)))
|
||||
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
|
||||
.addResource(new PaymentsController(currencyManager, paymentsCredentialGenerator))
|
||||
.addResource(new PaymentsController(currencyManager, paymentsCredentialsGenerator))
|
||||
.build();
|
||||
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
when(paymentsCredentialGenerator.generateFor(eq(AuthHelper.VALID_UUID.toString()))).thenReturn(validCredentials);
|
||||
when(paymentsCredentialsGenerator.generateForUuid(eq(AuthHelper.VALID_UUID))).thenReturn(validCredentials);
|
||||
when(currencyManager.getCurrencyConversions()).thenReturn(Optional.of(
|
||||
new CurrencyConversionEntityList(List.of(
|
||||
new CurrencyConversionEntity("FOO", Map.of(
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
/*
|
||||
* Copyright 2013-2021 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.tests.controllers;
|
||||
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
|
||||
|
@ -17,16 +18,23 @@ import org.junit.jupiter.api.Test;
|
|||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
||||
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialGenerator;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||
import org.whispersystems.textsecuregcm.configuration.SecureStorageServiceConfiguration;
|
||||
import org.whispersystems.textsecuregcm.controllers.SecureStorageController;
|
||||
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
|
||||
import org.whispersystems.textsecuregcm.util.MockHelper;
|
||||
import org.whispersystems.textsecuregcm.util.SystemMapper;
|
||||
|
||||
@ExtendWith(DropwizardExtensionsSupport.class)
|
||||
class SecureStorageControllerTest {
|
||||
|
||||
private static final ExternalServiceCredentialGenerator storageCredentialGenerator = new ExternalServiceCredentialGenerator(new byte[32], new byte[32], false);
|
||||
private static final SecureStorageServiceConfiguration STORAGE_CFG = MockHelper.buildMock(
|
||||
SecureStorageServiceConfiguration.class,
|
||||
cfg -> when(cfg.decodeUserAuthenticationTokenSharedSecret()).thenReturn(new byte[32]));
|
||||
|
||||
private static final ExternalServiceCredentialsGenerator STORAGE_CREDENTIAL_GENERATOR = SecureStorageController
|
||||
.credentialsGenerator(STORAGE_CFG);
|
||||
|
||||
private static final ResourceExtension resources = ResourceExtension.builder()
|
||||
.addProvider(AuthHelper.getAuthFilter())
|
||||
|
@ -34,7 +42,7 @@ class SecureStorageControllerTest {
|
|||
ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)))
|
||||
.setMapper(SystemMapper.getMapper())
|
||||
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
|
||||
.addResource(new SecureStorageController(storageCredentialGenerator))
|
||||
.addResource(new SecureStorageController(STORAGE_CREDENTIAL_GENERATOR))
|
||||
.build();
|
||||
|
||||
|
||||
|
@ -60,6 +68,4 @@ class SecureStorageControllerTest {
|
|||
|
||||
assertThat(response.getStatus()).isEqualTo(401);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2013-2021 Signal Messenger, LLC
|
||||
* Copyright 2013-2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
|
@ -7,7 +7,6 @@ package org.whispersystems.textsecuregcm.tests.util;
|
|||
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
|
@ -26,10 +25,10 @@ import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
|||
import org.whispersystems.textsecuregcm.auth.AuthenticationCredentials;
|
||||
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAccountAuthenticator;
|
||||
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
|
||||
import org.whispersystems.textsecuregcm.experiment.ExperimentEnrollmentManager;
|
||||
import org.whispersystems.textsecuregcm.storage.Account;
|
||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||
import org.whispersystems.textsecuregcm.storage.Device;
|
||||
import org.whispersystems.textsecuregcm.util.HeaderUtils;
|
||||
|
||||
public class AuthHelper {
|
||||
// Static seed to ensure reproducible tests.
|
||||
|
@ -201,19 +200,15 @@ public class AuthHelper {
|
|||
}
|
||||
|
||||
public static String getAuthHeader(UUID uuid, long deviceId, String password) {
|
||||
return getAuthHeader(uuid.toString() + "." + deviceId, password);
|
||||
return HeaderUtils.basicAuthHeader(uuid.toString() + "." + deviceId, password);
|
||||
}
|
||||
|
||||
public static String getAuthHeader(UUID uuid, String password) {
|
||||
return getAuthHeader(uuid.toString(), password);
|
||||
return HeaderUtils.basicAuthHeader(uuid.toString(), password);
|
||||
}
|
||||
|
||||
public static String getProvisioningAuthHeader(String number, String password) {
|
||||
return getAuthHeader(number, password);
|
||||
}
|
||||
|
||||
private static String getAuthHeader(String identifier, String password) {
|
||||
return "Basic " + Base64.getEncoder().encodeToString((identifier + ":" + password).getBytes());
|
||||
return HeaderUtils.basicAuthHeader(number, password);
|
||||
}
|
||||
|
||||
public static String getUnidentifiedAccessHeader(byte[] key) {
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.util;
|
||||
|
||||
import org.mockito.Mockito;
|
||||
|
||||
public final class MockHelper {
|
||||
|
||||
private MockHelper() {
|
||||
// utility class
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface MockInitializer<T> {
|
||||
|
||||
void init(T mock) throws Exception;
|
||||
}
|
||||
|
||||
public static <T> T buildMock(final Class<T> clazz, final MockInitializer<T> initializer) throws RuntimeException {
|
||||
final T mock = Mockito.mock(clazz);
|
||||
try {
|
||||
initializer.init(mock);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return mock;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue