Move turn secret to static configuration

This commit is contained in:
Jon Chambers 2023-06-12 16:25:39 -04:00 committed by Jon Chambers
parent 13456bad3a
commit 9cfd88a23f
9 changed files with 49 additions and 22 deletions

View File

@ -84,3 +84,5 @@ artService.userAuthenticationTokenUserIdSecret: AAAAAAAAAAA= # base64-encoded se
currentReportingKey.secret: AAAAAAAAAAA= currentReportingKey.secret: AAAAAAAAAAA=
currentReportingKey.salt: AAAAAAAAAAA= currentReportingKey.salt: AAAAAAAAAAA=
turn.secret: AAAAAAAAAAA=

View File

@ -419,3 +419,6 @@ registrationService:
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
AAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA
-----END CERTIFICATE----- -----END CERTIFICATE-----
turn:
secret: secret://turn.secret

View File

@ -47,6 +47,7 @@ import org.whispersystems.textsecuregcm.configuration.SecureValueRecovery2Config
import org.whispersystems.textsecuregcm.configuration.SpamFilterConfiguration; import org.whispersystems.textsecuregcm.configuration.SpamFilterConfiguration;
import org.whispersystems.textsecuregcm.configuration.StripeConfiguration; import org.whispersystems.textsecuregcm.configuration.StripeConfiguration;
import org.whispersystems.textsecuregcm.configuration.SubscriptionConfiguration; import org.whispersystems.textsecuregcm.configuration.SubscriptionConfiguration;
import org.whispersystems.textsecuregcm.configuration.TurnSecretConfiguration;
import org.whispersystems.textsecuregcm.configuration.UnidentifiedDeliveryConfiguration; import org.whispersystems.textsecuregcm.configuration.UnidentifiedDeliveryConfiguration;
import org.whispersystems.textsecuregcm.configuration.ZkConfig; import org.whispersystems.textsecuregcm.configuration.ZkConfig;
import org.whispersystems.textsecuregcm.limits.RateLimiterConfig; import org.whispersystems.textsecuregcm.limits.RateLimiterConfig;
@ -264,6 +265,11 @@ public class WhisperServerConfiguration extends Configuration {
@JsonProperty @JsonProperty
private RegistrationServiceConfiguration registrationService; private RegistrationServiceConfiguration registrationService;
@Valid
@NotNull
@JsonProperty
private TurnSecretConfiguration turn;
public AdminEventLoggingConfiguration getAdminEventLoggingConfiguration() { public AdminEventLoggingConfiguration getAdminEventLoggingConfiguration() {
return adminEventLoggingConfiguration; return adminEventLoggingConfiguration;
} }
@ -438,4 +444,8 @@ public class WhisperServerConfiguration extends Configuration {
public RegistrationServiceConfiguration getRegistrationServiceConfiguration() { public RegistrationServiceConfiguration getRegistrationServiceConfiguration() {
return registrationService; return registrationService;
} }
public TurnSecretConfiguration getTurnSecretConfiguration() {
return turn;
}
} }

View File

@ -554,7 +554,8 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
pushNotificationManager, pushNotificationManager,
pushLatencyManager); pushLatencyManager);
final ReceiptSender receiptSender = new ReceiptSender(accountsManager, messageSender, receiptSenderExecutor); final ReceiptSender receiptSender = new ReceiptSender(accountsManager, messageSender, receiptSenderExecutor);
final TurnTokenGenerator turnTokenGenerator = new TurnTokenGenerator(dynamicConfigurationManager); final TurnTokenGenerator turnTokenGenerator = new TurnTokenGenerator(dynamicConfigurationManager,
config.getTurnSecretConfiguration().secret().value());
RecaptchaClient recaptchaClient = new RecaptchaClient( RecaptchaClient recaptchaClient = new RecaptchaClient(
config.getRecaptchaConfiguration().projectPath(), config.getRecaptchaConfiguration().projectPath(),

View File

@ -26,39 +26,44 @@ import java.util.Optional;
public class TurnTokenGenerator { public class TurnTokenGenerator {
private final DynamicConfigurationManager<DynamicConfiguration> dynamicConfiguration; private final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager;
private final byte[] turnSecret;
private static final String ALGORITHM = "HmacSHA1"; private static final String ALGORITHM = "HmacSHA1";
public TurnTokenGenerator(final DynamicConfigurationManager<DynamicConfiguration> config) { public TurnTokenGenerator(final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager,
this.dynamicConfiguration = config; final byte[] turnSecret) {
this.dynamicConfigurationManager = dynamicConfigurationManager;
this.turnSecret = turnSecret;
} }
public TurnToken generate(final String e164) { public TurnToken generate(final String e164) {
try { try {
final byte[] key = dynamicConfiguration.getConfiguration().getTurnConfiguration().getSecret().getBytes();
final List<String> urls = urls(e164); final List<String> urls = urls(e164);
final Mac mac = Mac.getInstance(ALGORITHM); final Mac mac = Mac.getInstance(ALGORITHM);
final long validUntilSeconds = Instant.now().plus(Duration.ofDays(1)).getEpochSecond(); final long validUntilSeconds = Instant.now().plus(Duration.ofDays(1)).getEpochSecond();
final long user = Util.ensureNonNegativeInt(new SecureRandom().nextInt()); final long user = Util.ensureNonNegativeInt(new SecureRandom().nextInt());
final String userTime = validUntilSeconds + ":" + user; final String userTime = validUntilSeconds + ":" + user;
mac.init(new SecretKeySpec(key, ALGORITHM)); mac.init(new SecretKeySpec(turnSecret, ALGORITHM));
final String password = Base64.getEncoder().encodeToString(mac.doFinal(userTime.getBytes())); final String password = Base64.getEncoder().encodeToString(mac.doFinal(userTime.getBytes()));
return new TurnToken(userTime, password, urls); return new TurnToken(userTime, password, urls);
} catch (NoSuchAlgorithmException | InvalidKeyException e) { } catch (final NoSuchAlgorithmException | InvalidKeyException e) {
throw new AssertionError(e); throw new AssertionError(e);
} }
} }
private List<String> urls(final String e164) { private List<String> urls(final String e164) {
final DynamicTurnConfiguration turnConfig = dynamicConfiguration.getConfiguration().getTurnConfiguration(); final DynamicTurnConfiguration turnConfig = dynamicConfigurationManager.getConfiguration().getTurnConfiguration();
// Check if number is enrolled to test out specific turn servers // Check if number is enrolled to test out specific turn servers
final Optional<TurnUriConfiguration> enrolled = turnConfig.getUriConfigs().stream() final Optional<TurnUriConfiguration> enrolled = turnConfig.getUriConfigs().stream()
.filter(config -> config.getEnrolledNumbers().contains(e164)) .filter(config -> config.getEnrolledNumbers().contains(e164))
.findFirst(); .findFirst();
if (enrolled.isPresent()) { if (enrolled.isPresent()) {
return enrolled.get().getUris(); return enrolled.get().getUris();
} }

View File

@ -0,0 +1,11 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.configuration;
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
public record TurnSecretConfiguration(SecretBytes secret) {
}

View File

@ -13,17 +13,10 @@ import org.whispersystems.textsecuregcm.configuration.TurnUriConfiguration;
public class DynamicTurnConfiguration { public class DynamicTurnConfiguration {
@JsonProperty
private String secret;
@JsonProperty @JsonProperty
private List<@Valid TurnUriConfiguration> uriConfigs = Collections.emptyList(); private List<@Valid TurnUriConfiguration> uriConfigs = Collections.emptyList();
public List<TurnUriConfiguration> getUriConfigs() { public List<TurnUriConfiguration> getUriConfigs() {
return uriConfigs; return uriConfigs;
} }
public String getSecret() {
return secret;
}
} }

View File

@ -5,6 +5,7 @@ import org.junit.jupiter.api.Test;
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration; import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager; import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
import java.nio.charset.StandardCharsets;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -21,7 +22,6 @@ public class TurnTokenGeneratorTest {
captcha: captcha:
scoreFloor: 1.0 scoreFloor: 1.0
turn: turn:
secret: bloop
uriConfigs: uriConfigs:
- uris: - uris:
- always1.org - always1.org
@ -39,7 +39,9 @@ public class TurnTokenGeneratorTest {
DynamicConfigurationManager.class); DynamicConfigurationManager.class);
when(mockDynamicConfigManager.getConfiguration()).thenReturn(config); when(mockDynamicConfigManager.getConfiguration()).thenReturn(config);
final TurnTokenGenerator turnTokenGenerator = new TurnTokenGenerator(mockDynamicConfigManager);
final TurnTokenGenerator turnTokenGenerator =
new TurnTokenGenerator(mockDynamicConfigManager, "bloop".getBytes(StandardCharsets.UTF_8));
final long COUNT = 1000; final long COUNT = 1000;
@ -60,7 +62,6 @@ public class TurnTokenGeneratorTest {
captcha: captcha:
scoreFloor: 1.0 scoreFloor: 1.0
turn: turn:
secret: bloop
uriConfigs: uriConfigs:
- uris: - uris:
- always.org - always.org
@ -80,7 +81,8 @@ public class TurnTokenGeneratorTest {
DynamicConfigurationManager.class); DynamicConfigurationManager.class);
when(mockDynamicConfigManager.getConfiguration()).thenReturn(config); when(mockDynamicConfigManager.getConfiguration()).thenReturn(config);
final TurnTokenGenerator turnTokenGenerator = new TurnTokenGenerator(mockDynamicConfigManager); final TurnTokenGenerator turnTokenGenerator =
new TurnTokenGenerator(mockDynamicConfigManager, "bloop".getBytes(StandardCharsets.UTF_8));
final long COUNT = 1000; final long COUNT = 1000;
@ -122,7 +124,9 @@ public class TurnTokenGeneratorTest {
when(mockDynamicConfigManager.getConfiguration()).thenReturn(config); when(mockDynamicConfigManager.getConfiguration()).thenReturn(config);
final TurnTokenGenerator turnTokenGenerator = new TurnTokenGenerator(mockDynamicConfigManager); final TurnTokenGenerator turnTokenGenerator =
new TurnTokenGenerator(mockDynamicConfigManager, "bloop".getBytes(StandardCharsets.UTF_8));
TurnToken token = turnTokenGenerator.generate("+15555555555"); TurnToken token = turnTokenGenerator.generate("+15555555555");
assertThat(token.getUrls().get(0)).isEqualTo("enrolled.org"); assertThat(token.getUrls().get(0)).isEqualTo("enrolled.org");
token = turnTokenGenerator.generate("+15555555556"); token = turnTokenGenerator.generate("+15555555556");

View File

@ -329,7 +329,6 @@ class DynamicConfigurationTest {
{ {
final String config = REQUIRED_CONFIG.concat(""" final String config = REQUIRED_CONFIG.concat("""
turn: turn:
secret: bloop
uriConfigs: uriConfigs:
- uris: - uris:
- turn:test0.org - turn:test0.org
@ -344,7 +343,6 @@ class DynamicConfigurationTest {
.parseConfiguration(config, DynamicConfiguration.class) .parseConfiguration(config, DynamicConfiguration.class)
.orElseThrow() .orElseThrow()
.getTurnConfiguration(); .getTurnConfiguration();
assertThat(turnConfiguration.getSecret()).isEqualTo("bloop");
assertThat(turnConfiguration.getUriConfigs().get(0).getUris()).hasSize(2); assertThat(turnConfiguration.getUriConfigs().get(0).getUris()).hasSize(2);
assertThat(turnConfiguration.getUriConfigs().get(1).getUris()).hasSize(1); assertThat(turnConfiguration.getUriConfigs().get(1).getUris()).hasSize(1);
assertThat(turnConfiguration.getUriConfigs().get(0).getWeight()).isEqualTo(1); assertThat(turnConfiguration.getUriConfigs().get(0).getWeight()).isEqualTo(1);