mirror of
https://github.com/signalapp/Signal-Server
synced 2026-04-21 19:48:01 +01:00
Moving secret values out of the main configuration file
This commit is contained in:
@@ -5,10 +5,11 @@
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
|
||||
public record AdminEventLoggingConfiguration(
|
||||
@NotEmpty String credentials,
|
||||
@NotBlank String credentials,
|
||||
@NotEmpty String projectId,
|
||||
@NotEmpty String logName) {
|
||||
}
|
||||
|
||||
@@ -1,51 +1,17 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
|
||||
|
||||
public class ApnConfiguration {
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String teamId;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String keyId;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String signingKey;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String bundleId;
|
||||
|
||||
@JsonProperty
|
||||
private boolean sandbox = false;
|
||||
|
||||
public String getTeamId() {
|
||||
return teamId;
|
||||
}
|
||||
|
||||
public String getKeyId() {
|
||||
return keyId;
|
||||
}
|
||||
|
||||
public String getSigningKey() {
|
||||
return signingKey;
|
||||
}
|
||||
|
||||
public String getBundleId() {
|
||||
return bundleId;
|
||||
}
|
||||
|
||||
public boolean isSandboxEnabled() {
|
||||
return sandbox;
|
||||
}
|
||||
public record ApnConfiguration(@NotBlank String teamId,
|
||||
@NotBlank String keyId,
|
||||
@NotNull SecretString signingKey,
|
||||
@NotBlank String bundleId,
|
||||
boolean sandbox) {
|
||||
}
|
||||
|
||||
@@ -5,35 +5,17 @@
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import static org.apache.commons.lang3.ObjectUtils.firstNonNull;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.HexFormat;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||
import org.whispersystems.textsecuregcm.util.ExactlySize;
|
||||
|
||||
public class ArtServiceConfiguration {
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String userAuthenticationTokenSharedSecret;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String userAuthenticationTokenUserIdSecret;
|
||||
|
||||
@JsonProperty
|
||||
@NotNull
|
||||
private Duration tokenExpiration = Duration.ofDays(1);
|
||||
|
||||
public byte[] getUserAuthenticationTokenSharedSecret() {
|
||||
return HexFormat.of().parseHex(userAuthenticationTokenSharedSecret);
|
||||
}
|
||||
|
||||
public byte[] getUserAuthenticationTokenUserIdSecret() {
|
||||
return HexFormat.of().parseHex(userAuthenticationTokenUserIdSecret);
|
||||
}
|
||||
|
||||
public Duration getTokenExpiration() {
|
||||
return tokenExpiration;
|
||||
public record ArtServiceConfiguration(@ExactlySize(32) SecretBytes userAuthenticationTokenSharedSecret,
|
||||
@NotNull SecretBytes userAuthenticationTokenUserIdSecret,
|
||||
@NotNull Duration tokenExpiration) {
|
||||
public ArtServiceConfiguration {
|
||||
tokenExpiration = firstNonNull(tokenExpiration, Duration.ofDays(1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,43 +1,15 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
|
||||
public class AwsAttachmentsConfiguration {
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String accessKey;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String accessSecret;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String bucket;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String region;
|
||||
|
||||
public String getAccessKey() {
|
||||
return accessKey;
|
||||
}
|
||||
|
||||
public String getAccessSecret() {
|
||||
return accessSecret;
|
||||
}
|
||||
|
||||
public String getBucket() {
|
||||
return bucket;
|
||||
}
|
||||
|
||||
public String getRegion() {
|
||||
return region;
|
||||
}
|
||||
public record AwsAttachmentsConfiguration(@NotNull SecretString accessKey,
|
||||
@NotNull SecretString accessSecret,
|
||||
@NotBlank String bucket,
|
||||
@NotBlank String region) {
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
|
||||
/**
|
||||
* @param merchantId the Braintree merchant ID
|
||||
@@ -24,7 +25,7 @@ import javax.validation.constraints.NotNull;
|
||||
*/
|
||||
public record BraintreeConfiguration(@NotBlank String merchantId,
|
||||
@NotBlank String publicKey,
|
||||
@NotBlank String privateKey,
|
||||
@NotNull SecretString privateKey,
|
||||
@NotBlank String environment,
|
||||
@NotEmpty Set<@NotBlank String> supportedCurrencies,
|
||||
@NotBlank String graphqlUrl,
|
||||
|
||||
@@ -1,44 +1,16 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
|
||||
public class CdnConfiguration {
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String accessKey;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String accessSecret;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String bucket;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String region;
|
||||
|
||||
public String getAccessKey() {
|
||||
return accessKey;
|
||||
}
|
||||
|
||||
public String getAccessSecret() {
|
||||
return accessSecret;
|
||||
}
|
||||
|
||||
public String getBucket() {
|
||||
return bucket;
|
||||
}
|
||||
|
||||
public String getRegion() {
|
||||
return region;
|
||||
}
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
|
||||
public record CdnConfiguration(@NotNull SecretString accessKey,
|
||||
@NotNull SecretString accessSecret,
|
||||
@NotBlank String bucket,
|
||||
@NotBlank String region) {
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2021 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
@@ -7,16 +7,17 @@ package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.micrometer.datadog.DatadogConfig;
|
||||
import java.time.Duration;
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.time.Duration;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
|
||||
public class DatadogConfiguration implements DatadogConfig {
|
||||
|
||||
@JsonProperty
|
||||
@NotBlank
|
||||
private String apiKey;
|
||||
@NotNull
|
||||
private SecretString apiKey;
|
||||
|
||||
@JsonProperty
|
||||
@NotNull
|
||||
@@ -32,7 +33,7 @@ public class DatadogConfiguration implements DatadogConfig {
|
||||
|
||||
@Override
|
||||
public String apiKey() {
|
||||
return apiKey;
|
||||
return apiKey.value();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/*
|
||||
* Copyright 2013-2023 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||
import org.whispersystems.textsecuregcm.util.ExactlySize;
|
||||
|
||||
public record DirectoryV2ClientConfiguration(@ExactlySize({32}) byte[] userAuthenticationTokenSharedSecret,
|
||||
@ExactlySize({32}) byte[] userIdTokenSharedSecret) {
|
||||
public record DirectoryV2ClientConfiguration(@ExactlySize(32) SecretBytes userAuthenticationTokenSharedSecret,
|
||||
@ExactlySize(32) SecretBytes userIdTokenSharedSecret) {
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/*
|
||||
* Copyright 2013-2022 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
|
||||
public record FcmConfiguration(@NotBlank String credentials) {
|
||||
public record FcmConfiguration(@NotNull SecretString credentials) {
|
||||
}
|
||||
|
||||
@@ -1,57 +1,22 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.dropwizard.util.Strings;
|
||||
import io.dropwizard.validation.ValidationMethod;
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
|
||||
public class GcpAttachmentsConfiguration {
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String domain;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String email;
|
||||
|
||||
@JsonProperty
|
||||
@Min(1)
|
||||
private int maxSizeInBytes;
|
||||
|
||||
@JsonProperty
|
||||
private String pathPrefix;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String rsaSigningKey;
|
||||
|
||||
public String getDomain() {
|
||||
return domain;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public int getMaxSizeInBytes() {
|
||||
return maxSizeInBytes;
|
||||
}
|
||||
|
||||
public String getPathPrefix() {
|
||||
return pathPrefix;
|
||||
}
|
||||
|
||||
public String getRsaSigningKey() {
|
||||
return rsaSigningKey;
|
||||
}
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
|
||||
public record GcpAttachmentsConfiguration(@NotBlank String domain,
|
||||
@NotBlank String email,
|
||||
@Min(1) int maxSizeInBytes,
|
||||
String pathPrefix,
|
||||
@NotNull SecretString rsaSigningKey) {
|
||||
@SuppressWarnings("unused")
|
||||
@ValidationMethod(message = "pathPrefix must be empty or start with /")
|
||||
public boolean isPathPrefixValid() {
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import org.whispersystems.textsecuregcm.util.ByteArrayAdapter;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||
|
||||
public record GenericZkConfig (
|
||||
@JsonProperty
|
||||
@JsonSerialize(using = ByteArrayAdapter.Serializing.class)
|
||||
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
|
||||
@NotNull
|
||||
byte[] serverSecret
|
||||
) {}
|
||||
public record GenericZkConfig(@NotNull SecretBytes serverSecret) {
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/*
|
||||
* Copyright 2021-2022 Signal Messenger, LLC
|
||||
* Copyright 2021 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
|
||||
public record HCaptchaConfiguration(@NotBlank String apiKey) {
|
||||
public record HCaptchaConfiguration(@NotNull SecretString apiKey) {
|
||||
}
|
||||
|
||||
@@ -1,56 +1,21 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import java.util.HexFormat;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
|
||||
public class PaymentsServiceConfiguration {
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String userAuthenticationTokenSharedSecret;
|
||||
|
||||
@NotBlank
|
||||
@JsonProperty
|
||||
private String coinMarketCapApiKey;
|
||||
|
||||
@JsonProperty
|
||||
@NotEmpty
|
||||
private Map<@NotBlank String, Integer> coinMarketCapCurrencyIds;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String fixerApiKey;
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private List<String> paymentCurrencies;
|
||||
|
||||
public byte[] getUserAuthenticationTokenSharedSecret() {
|
||||
return HexFormat.of().parseHex(userAuthenticationTokenSharedSecret);
|
||||
}
|
||||
|
||||
public String getCoinMarketCapApiKey() {
|
||||
return coinMarketCapApiKey;
|
||||
}
|
||||
|
||||
public Map<String, Integer> getCoinMarketCapCurrencyIds() {
|
||||
return coinMarketCapCurrencyIds;
|
||||
}
|
||||
|
||||
public String getFixerApiKey() {
|
||||
return fixerApiKey;
|
||||
}
|
||||
|
||||
public List<String> getPaymentCurrencies() {
|
||||
return paymentCurrencies;
|
||||
}
|
||||
public record PaymentsServiceConfiguration(@NotNull SecretBytes userAuthenticationTokenSharedSecret,
|
||||
@NotNull SecretString coinMarketCapApiKey,
|
||||
@NotNull SecretString fixerApiKey,
|
||||
@NotEmpty Map<@NotBlank String, Integer> coinMarketCapCurrencyIds,
|
||||
@NotEmpty List<String> paymentCurrencies) {
|
||||
}
|
||||
|
||||
@@ -1,33 +1,14 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretStringList;
|
||||
|
||||
public class RemoteConfigConfiguration {
|
||||
|
||||
@JsonProperty
|
||||
@NotNull
|
||||
private List<String> authorizedTokens = new LinkedList<>();
|
||||
|
||||
@NotNull
|
||||
@JsonProperty
|
||||
private Map<String, String> globalConfig = new HashMap<>();
|
||||
|
||||
public List<String> getAuthorizedTokens() {
|
||||
return authorizedTokens;
|
||||
}
|
||||
|
||||
public Map<String, String> getGlobalConfig() {
|
||||
return globalConfig;
|
||||
}
|
||||
public record RemoteConfigConfiguration(@NotNull SecretStringList authorizedTokens,
|
||||
@NotNull Map<String, String> globalConfig) {
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
@@ -7,18 +7,18 @@ package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import java.util.HexFormat;
|
||||
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.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||
|
||||
public class SecureBackupServiceConfiguration {
|
||||
|
||||
@NotEmpty
|
||||
@NotNull
|
||||
@JsonProperty
|
||||
private String userAuthenticationTokenSharedSecret;
|
||||
private SecretBytes userAuthenticationTokenSharedSecret;
|
||||
|
||||
@NotBlank
|
||||
@JsonProperty
|
||||
@@ -38,8 +38,8 @@ public class SecureBackupServiceConfiguration {
|
||||
@JsonProperty
|
||||
private RetryConfiguration retry = new RetryConfiguration();
|
||||
|
||||
public byte[] getUserAuthenticationTokenSharedSecret() {
|
||||
return HexFormat.of().parseHex(userAuthenticationTokenSharedSecret);
|
||||
public SecretBytes userAuthenticationTokenSharedSecret() {
|
||||
return userAuthenticationTokenSharedSecret;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
|
||||
@@ -5,18 +5,18 @@
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import java.util.HexFormat;
|
||||
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.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||
|
||||
public record SecureStorageServiceConfiguration(@NotEmpty String userAuthenticationTokenSharedSecret,
|
||||
public record SecureStorageServiceConfiguration(@NotNull SecretBytes userAuthenticationTokenSharedSecret,
|
||||
@NotBlank String uri,
|
||||
@NotEmpty List<@NotBlank String> storageCaCertificates,
|
||||
@Valid CircuitBreakerConfiguration circuitBreaker,
|
||||
@Valid RetryConfiguration retry) {
|
||||
|
||||
public SecureStorageServiceConfiguration {
|
||||
if (circuitBreaker == null) {
|
||||
circuitBreaker = new CircuitBreakerConfiguration();
|
||||
@@ -25,8 +25,4 @@ public record SecureStorageServiceConfiguration(@NotEmpty String userAuthenticat
|
||||
retry = new RetryConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] decodeUserAuthenticationTokenSharedSecret() {
|
||||
return HexFormat.of().parseHex(userAuthenticationTokenSharedSecret);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,13 +9,14 @@ import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||
import org.whispersystems.textsecuregcm.util.ExactlySize;
|
||||
|
||||
public record SecureValueRecovery2Configuration(
|
||||
boolean enabled,
|
||||
@NotBlank String uri,
|
||||
@ExactlySize({32}) byte[] userAuthenticationTokenSharedSecret,
|
||||
@ExactlySize({32}) byte[] userIdTokenSharedSecret,
|
||||
@ExactlySize(32) SecretBytes userAuthenticationTokenSharedSecret,
|
||||
@ExactlySize(32) SecretBytes userIdTokenSharedSecret,
|
||||
@NotEmpty List<@NotBlank String> svrCaCertificates,
|
||||
@NotNull @Valid CircuitBreakerConfiguration circuitBreaker,
|
||||
@NotNull @Valid RetryConfiguration retry) {
|
||||
|
||||
@@ -8,10 +8,12 @@ package org.whispersystems.textsecuregcm.configuration;
|
||||
import java.util.Set;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
|
||||
public record StripeConfiguration(@NotBlank String apiKey,
|
||||
@NotEmpty byte[] idempotencyKeyGenerator,
|
||||
public record StripeConfiguration(@NotNull SecretString apiKey,
|
||||
@NotNull SecretBytes idempotencyKeyGenerator,
|
||||
@NotBlank String boostDescription,
|
||||
@NotEmpty Set<@NotBlank String> supportedCurrencies) {
|
||||
|
||||
}
|
||||
|
||||
@@ -1,48 +1,21 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.signal.libsignal.protocol.InvalidKeyException;
|
||||
import org.signal.libsignal.protocol.ecc.Curve;
|
||||
import org.signal.libsignal.protocol.ecc.ECPrivateKey;
|
||||
import org.whispersystems.textsecuregcm.util.ByteArrayAdapter;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||
import org.whispersystems.textsecuregcm.util.ExactlySize;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
public class UnidentifiedDeliveryConfiguration {
|
||||
|
||||
@JsonProperty
|
||||
@JsonSerialize(using = ByteArrayAdapter.Serializing.class)
|
||||
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
|
||||
@NotNull
|
||||
private byte[] certificate;
|
||||
|
||||
@JsonProperty
|
||||
@JsonSerialize(using = ByteArrayAdapter.Serializing.class)
|
||||
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
|
||||
@NotNull
|
||||
@Size(min = 32, max = 32)
|
||||
private byte[] privateKey;
|
||||
|
||||
@NotNull
|
||||
private int expiresDays;
|
||||
|
||||
public byte[] getCertificate() {
|
||||
return certificate;
|
||||
}
|
||||
|
||||
public ECPrivateKey getPrivateKey() throws InvalidKeyException {
|
||||
return Curve.decodePrivatePoint(privateKey);
|
||||
}
|
||||
|
||||
public int getExpiresDays() {
|
||||
return expiresDays;
|
||||
public record UnidentifiedDeliveryConfiguration(@NotNull SecretBytes certificate,
|
||||
@ExactlySize(32) SecretBytes privateKey,
|
||||
int expiresDays) {
|
||||
public ECPrivateKey ecPrivateKey() throws InvalidKeyException {
|
||||
return Curve.decodePrivatePoint(privateKey.value());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +1,14 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import org.whispersystems.textsecuregcm.util.ByteArrayAdapter;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||
|
||||
public class ZkConfig {
|
||||
|
||||
@JsonProperty
|
||||
@JsonSerialize(using = ByteArrayAdapter.Serializing.class)
|
||||
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
|
||||
@NotNull
|
||||
private byte[] serverSecret;
|
||||
|
||||
@JsonProperty
|
||||
@JsonSerialize(using = ByteArrayAdapter.Serializing.class)
|
||||
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
|
||||
@NotNull
|
||||
private byte[] serverPublic;
|
||||
|
||||
public byte[] getServerSecret() {
|
||||
return serverSecret;
|
||||
}
|
||||
|
||||
public byte[] getServerPublic() {
|
||||
return serverPublic;
|
||||
}
|
||||
public record ZkConfig(@NotNull SecretBytes serverSecret,
|
||||
@NotEmpty byte[] serverPublic) {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration.secrets;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import javax.validation.ConstraintValidator;
|
||||
import javax.validation.ConstraintValidatorContext;
|
||||
|
||||
public abstract class BaseSecretValidator<A extends Annotation, T, S extends Secret<? extends T>> implements ConstraintValidator<A, S> {
|
||||
|
||||
private final ConstraintValidator<A, T> validator;
|
||||
|
||||
|
||||
protected BaseSecretValidator(final ConstraintValidator<A, T> validator) {
|
||||
this.validator = requireNonNull(validator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(final A constraintAnnotation) {
|
||||
validator.initialize(constraintAnnotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(final S value, final ConstraintValidatorContext context) {
|
||||
return validator.isValid(value.value(), context);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration.secrets;
|
||||
|
||||
public class Secret<T> {
|
||||
|
||||
private final T value;
|
||||
|
||||
|
||||
public Secret(final T value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public T value() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[REDACTED]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration.secrets;
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
public class SecretBytes extends Secret<byte[]> {
|
||||
|
||||
public SecretBytes(final byte[] value) {
|
||||
super(requireNotEmpty(value));
|
||||
}
|
||||
|
||||
private static byte[] requireNotEmpty(final byte[] value) {
|
||||
Validate.isTrue(value.length > 0, "SecretBytes value must not be empty");
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration.secrets;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import org.hibernate.validator.internal.constraintvalidators.bv.notempty.NotEmptyValidatorForCollection;
|
||||
|
||||
public class SecretBytesList extends Secret<List<byte[]>> {
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public static class ValidatorNotEmpty extends BaseSecretValidator<NotEmpty, Collection, SecretBytesList> {
|
||||
public ValidatorNotEmpty() {
|
||||
super(new NotEmptyValidatorForCollection());
|
||||
}
|
||||
}
|
||||
|
||||
public SecretBytesList(final List<byte[]> value) {
|
||||
super(ImmutableList.copyOf(value));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration.secrets;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.whispersystems.textsecuregcm.util.SystemMapper;
|
||||
|
||||
public class SecretStore {
|
||||
|
||||
private final Map<String, Secret<?>> secrets;
|
||||
|
||||
|
||||
public static SecretStore fromYamlFileSecretsBundle(final String filename) {
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
final Map<String, Object> secretsBundle = SystemMapper.yamlMapper().readValue(new File(filename), Map.class);
|
||||
return fromSecretsBundle(secretsBundle);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException("Failed to parse YAML file [%s]".formatted(filename), e);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public SecretStore(final Map<String, Secret<?>> secrets) {
|
||||
this.secrets = Map.copyOf(secrets);
|
||||
}
|
||||
|
||||
public SecretString secretString(final String reference) {
|
||||
return fromStore(reference, SecretString.class);
|
||||
}
|
||||
|
||||
public SecretBytes secretBytesFromBase64String(final String reference) {
|
||||
final SecretString secret = fromStore(reference, SecretString.class);
|
||||
return new SecretBytes(decodeBase64(secret.value()));
|
||||
}
|
||||
|
||||
public SecretStringList secretStringList(final String reference) {
|
||||
return fromStore(reference, SecretStringList.class);
|
||||
}
|
||||
|
||||
public SecretBytesList secretBytesListFromBase64Strings(final String reference) {
|
||||
final List<String> secrets = secretStringList(reference).value();
|
||||
final List<byte[]> byteSecrets = secrets.stream().map(SecretStore::decodeBase64).toList();
|
||||
return new SecretBytesList(byteSecrets);
|
||||
}
|
||||
|
||||
private <T extends Secret<?>> T fromStore(final String name, final Class<T> expected) {
|
||||
final Secret<?> secret = secrets.get(name);
|
||||
if (secret == null) {
|
||||
throw new IllegalArgumentException("Secret [%s] is not present in the secrets bundle".formatted(name));
|
||||
}
|
||||
if (!expected.isInstance(secret)) {
|
||||
throw new IllegalArgumentException("Secret [%s] is of type [%s] but caller expects type [%s]".formatted(
|
||||
name, secret.getClass().getSimpleName(), expected.getSimpleName()));
|
||||
}
|
||||
return expected.cast(secret);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static SecretStore fromYamlStringSecretsBundle(final String secretsBundleYaml) {
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
final Map<String, Object> secretsBundle = SystemMapper.yamlMapper().readValue(secretsBundleYaml, Map.class);
|
||||
return fromSecretsBundle(secretsBundle);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException("Failed to parse JSON", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static SecretStore fromSecretsBundle(final Map<String, Object> secretsBundle) {
|
||||
final Map<String, Secret<?>> store = new HashMap<>();
|
||||
secretsBundle.forEach((k, v) -> {
|
||||
if (v instanceof final String str) {
|
||||
store.put(k, new SecretString(str));
|
||||
return;
|
||||
}
|
||||
if (v instanceof final List<?> list) {
|
||||
final List<String> secrets = list.stream().map(o -> {
|
||||
if (o instanceof final String s) {
|
||||
return s;
|
||||
}
|
||||
throw new IllegalArgumentException("Secrets bundle JSON object is only supposed to have values of types String and list of Strings");
|
||||
}).toList();
|
||||
store.put(k, new SecretStringList(secrets));
|
||||
return;
|
||||
}
|
||||
throw new IllegalArgumentException("Secrets bundle JSON object is only supposed to have values of types String and list of Strings");
|
||||
});
|
||||
return new SecretStore(store);
|
||||
}
|
||||
|
||||
private static byte[] decodeBase64(final String str) {
|
||||
return Base64.getDecoder().decode(str);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration.secrets;
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
public class SecretString extends Secret<String> {
|
||||
public SecretString(final String value) {
|
||||
super(Validate.notBlank(value, "SecretString value must not be blank"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration.secrets;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import org.hibernate.validator.internal.constraintvalidators.bv.notempty.NotEmptyValidatorForCollection;
|
||||
|
||||
public class SecretStringList extends Secret<List<String>> {
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public static class ValidatorNotEmpty extends BaseSecretValidator<NotEmpty, Collection, SecretStringList> {
|
||||
public ValidatorNotEmpty() {
|
||||
super(new NotEmptyValidatorForCollection());
|
||||
}
|
||||
}
|
||||
|
||||
public SecretStringList(final List<String> value) {
|
||||
super(ImmutableList.copyOf(value));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration.secrets;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import com.fasterxml.jackson.core.JacksonException;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
public class SecretsModule extends SimpleModule {
|
||||
|
||||
public static final SecretsModule INSTANCE = new SecretsModule();
|
||||
|
||||
public static final String PREFIX = "secret://";
|
||||
|
||||
private final AtomicReference<SecretStore> secretStoreHolder = new AtomicReference<>(null);
|
||||
|
||||
|
||||
private SecretsModule() {
|
||||
addDeserializer(SecretString.class, createDeserializer(SecretStore::secretString));
|
||||
addDeserializer(SecretBytes.class, createDeserializer(SecretStore::secretBytesFromBase64String));
|
||||
addDeserializer(SecretStringList.class, createDeserializer(SecretStore::secretStringList));
|
||||
addDeserializer(SecretBytesList.class, createDeserializer(SecretStore::secretBytesListFromBase64Strings));
|
||||
}
|
||||
|
||||
public void setSecretStore(final SecretStore secretStore) {
|
||||
this.secretStoreHolder.set(requireNonNull(secretStore));
|
||||
}
|
||||
|
||||
private <T> JsonDeserializer<T> createDeserializer(final BiFunction<SecretStore, String, T> constructor) {
|
||||
return new JsonDeserializer<>() {
|
||||
@Override
|
||||
public T deserialize(final JsonParser p, final DeserializationContext ctxt) throws IOException, JacksonException {
|
||||
final SecretStore secretStore = secretStoreHolder.get();
|
||||
if (secretStore == null) {
|
||||
throw new IllegalStateException(
|
||||
"An instance of a SecretStore must be set for the SecretsModule via setSecretStore() method");
|
||||
}
|
||||
final String reference = p.getValueAsString();
|
||||
if (!reference.startsWith(PREFIX) || reference.length() <= PREFIX.length()) {
|
||||
throw new IllegalArgumentException(
|
||||
"Value of a secret field must start with a [%s] prefix and refer to an entry in a secrets bundle".formatted(PREFIX));
|
||||
}
|
||||
return constructor.apply(secretStore, reference.substring(PREFIX.length()));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user