Refactor ServiceConfig and add sandbox config

This commit is contained in:
AsamK 2021-01-23 23:06:58 +01:00
parent 4eaec83594
commit 7d802fb8c5
15 changed files with 433 additions and 203 deletions

View file

@ -16,6 +16,9 @@
*/ */
package org.asamk.signal.manager; package org.asamk.signal.manager;
import org.asamk.signal.manager.config.ServiceConfig;
import org.asamk.signal.manager.config.ServiceEnvironment;
import org.asamk.signal.manager.config.ServiceEnvironmentConfig;
import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupId;
import org.asamk.signal.manager.groups.GroupIdV1; import org.asamk.signal.manager.groups.GroupIdV1;
import org.asamk.signal.manager.groups.GroupIdV2; import org.asamk.signal.manager.groups.GroupIdV2;
@ -131,7 +134,6 @@ import org.whispersystems.signalservice.api.util.SleepTimer;
import org.whispersystems.signalservice.api.util.StreamDetails; import org.whispersystems.signalservice.api.util.StreamDetails;
import org.whispersystems.signalservice.api.util.UptimeSleepTimer; import org.whispersystems.signalservice.api.util.UptimeSleepTimer;
import org.whispersystems.signalservice.api.util.UuidUtil; import org.whispersystems.signalservice.api.util.UuidUtil;
import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration;
import org.whispersystems.signalservice.internal.contacts.crypto.Quote; import org.whispersystems.signalservice.internal.contacts.crypto.Quote;
import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedQuoteException; import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedQuoteException;
import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedResponseException; import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedResponseException;
@ -169,17 +171,15 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.asamk.signal.manager.ServiceConfig.CDS_MRENCLAVE; import static org.asamk.signal.manager.config.ServiceConfig.capabilities;
import static org.asamk.signal.manager.ServiceConfig.capabilities;
import static org.asamk.signal.manager.ServiceConfig.getIasKeyStore;
public class Manager implements Closeable { public class Manager implements Closeable {
private final static Logger logger = LoggerFactory.getLogger(Manager.class); private final static Logger logger = LoggerFactory.getLogger(Manager.class);
private final CertificateValidator certificateValidator = new CertificateValidator(ServiceConfig.getUnidentifiedSenderTrustRoot()); private final CertificateValidator certificateValidator;
private final SignalServiceConfiguration serviceConfiguration; private final ServiceEnvironmentConfig serviceEnvironmentConfig;
private final String userAgent; private final String userAgent;
private SignalAccount account; private SignalAccount account;
@ -202,16 +202,17 @@ public class Manager implements Closeable {
Manager( Manager(
SignalAccount account, SignalAccount account,
PathConfig pathConfig, PathConfig pathConfig,
SignalServiceConfiguration serviceConfiguration, ServiceEnvironmentConfig serviceEnvironmentConfig,
String userAgent String userAgent
) { ) {
this.account = account; this.account = account;
this.serviceConfiguration = serviceConfiguration; this.serviceEnvironmentConfig = serviceEnvironmentConfig;
this.certificateValidator = new CertificateValidator(serviceEnvironmentConfig.getUnidentifiedSenderTrustRoot());
this.userAgent = userAgent; this.userAgent = userAgent;
this.groupsV2Operations = capabilities.isGv2() ? new GroupsV2Operations(ClientZkOperations.create( this.groupsV2Operations = capabilities.isGv2() ? new GroupsV2Operations(ClientZkOperations.create(
serviceConfiguration)) : null; serviceEnvironmentConfig.getSignalServiceConfiguration())) : null;
final SleepTimer timer = new UptimeSleepTimer(); final SleepTimer timer = new UptimeSleepTimer();
this.accountManager = new SignalServiceAccountManager(serviceConfiguration, this.accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(),
new DynamicCredentialsProvider(account.getUuid(), new DynamicCredentialsProvider(account.getUuid(),
account.getUsername(), account.getUsername(),
account.getPassword(), account.getPassword(),
@ -222,11 +223,18 @@ public class Manager implements Closeable {
ServiceConfig.AUTOMATIC_NETWORK_RETRY, ServiceConfig.AUTOMATIC_NETWORK_RETRY,
timer); timer);
this.groupsV2Api = accountManager.getGroupsV2Api(); this.groupsV2Api = accountManager.getGroupsV2Api();
final KeyBackupService keyBackupService = ServiceConfig.createKeyBackupService(accountManager); final KeyBackupService keyBackupService = accountManager.getKeyBackupService(ServiceConfig.getIasKeyStore(),
serviceEnvironmentConfig.getKeyBackupConfig().getEnclaveName(),
serviceEnvironmentConfig.getKeyBackupConfig().getServiceId(),
serviceEnvironmentConfig.getKeyBackupConfig().getMrenclave(),
10);
this.pinHelper = new PinHelper(keyBackupService); this.pinHelper = new PinHelper(keyBackupService);
this.clientZkProfileOperations = capabilities.isGv2() ? ClientZkOperations.create(serviceConfiguration) this.clientZkProfileOperations = capabilities.isGv2()
.getProfileOperations() : null; ? ClientZkOperations.create(serviceEnvironmentConfig.getSignalServiceConfiguration())
this.messageReceiver = new SignalServiceMessageReceiver(serviceConfiguration, .getProfileOperations()
: null;
this.messageReceiver = new SignalServiceMessageReceiver(serviceEnvironmentConfig.getSignalServiceConfiguration(),
account.getUuid(), account.getUuid(),
account.getUsername(), account.getUsername(),
account.getPassword(), account.getPassword(),
@ -275,7 +283,7 @@ public class Manager implements Closeable {
} }
public static Manager init( public static Manager init(
String username, File settingsPath, SignalServiceConfiguration serviceConfiguration, String userAgent String username, File settingsPath, ServiceEnvironment serviceEnvironment, String userAgent
) throws IOException, NotRegisteredException { ) throws IOException, NotRegisteredException {
PathConfig pathConfig = PathConfig.createDefault(settingsPath); PathConfig pathConfig = PathConfig.createDefault(settingsPath);
@ -289,7 +297,11 @@ public class Manager implements Closeable {
throw new NotRegisteredException(); throw new NotRegisteredException();
} }
return new Manager(account, pathConfig, serviceConfiguration, userAgent); final ServiceEnvironmentConfig serviceEnvironmentConfig = ServiceConfig.getServiceEnvironmentConfig(
serviceEnvironment,
userAgent);
return new Manager(account, pathConfig, serviceEnvironmentConfig, userAgent);
} }
public static List<String> getAllLocalUsernames(File settingsPath) { public static List<String> getAllLocalUsernames(File settingsPath) {
@ -498,7 +510,7 @@ public class Manager implements Closeable {
private SignalServiceMessageSender createMessageSender() { private SignalServiceMessageSender createMessageSender() {
final ExecutorService executor = null; final ExecutorService executor = null;
return new SignalServiceMessageSender(serviceConfiguration, return new SignalServiceMessageSender(serviceEnvironmentConfig.getSignalServiceConfiguration(),
account.getUuid(), account.getUuid(),
account.getUsername(), account.getUsername(),
account.getPassword(), account.getPassword(),
@ -1262,7 +1274,9 @@ public class Manager implements Closeable {
private Map<String, UUID> getRegisteredUsers(final Set<String> numbersMissingUuid) throws IOException { private Map<String, UUID> getRegisteredUsers(final Set<String> numbersMissingUuid) throws IOException {
try { try {
return accountManager.getRegisteredUsers(getIasKeyStore(), numbersMissingUuid, CDS_MRENCLAVE); return accountManager.getRegisteredUsers(ServiceConfig.getIasKeyStore(),
numbersMissingUuid,
serviceEnvironmentConfig.getCdsMrenclave());
} catch (Quote.InvalidQuoteFormatException | UnauthenticatedQuoteException | SignatureException | UnauthenticatedResponseException | InvalidKeyException e) { } catch (Quote.InvalidQuoteFormatException | UnauthenticatedQuoteException | SignatureException | UnauthenticatedResponseException | InvalidKeyException e) {
throw new IOException(e); throw new IOException(e);
} }

View file

@ -16,6 +16,9 @@
*/ */
package org.asamk.signal.manager; package org.asamk.signal.manager;
import org.asamk.signal.manager.config.ServiceConfig;
import org.asamk.signal.manager.config.ServiceEnvironment;
import org.asamk.signal.manager.config.ServiceEnvironmentConfig;
import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.storage.SignalAccount;
import org.asamk.signal.manager.util.KeyUtils; import org.asamk.signal.manager.util.KeyUtils;
import org.signal.zkgroup.InvalidInputException; import org.signal.zkgroup.InvalidInputException;
@ -31,7 +34,6 @@ import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations;
import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.util.SleepTimer; import org.whispersystems.signalservice.api.util.SleepTimer;
import org.whispersystems.signalservice.api.util.UptimeSleepTimer; import org.whispersystems.signalservice.api.util.UptimeSleepTimer;
import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration;
import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider; import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider;
import java.io.File; import java.io.File;
@ -43,7 +45,7 @@ public class ProvisioningManager {
private final static Logger logger = LoggerFactory.getLogger(ProvisioningManager.class); private final static Logger logger = LoggerFactory.getLogger(ProvisioningManager.class);
private final PathConfig pathConfig; private final PathConfig pathConfig;
private final SignalServiceConfiguration serviceConfiguration; private final ServiceEnvironmentConfig serviceEnvironmentConfig;
private final String userAgent; private final String userAgent;
private final SignalServiceAccountManager accountManager; private final SignalServiceAccountManager accountManager;
@ -51,9 +53,9 @@ public class ProvisioningManager {
private final int registrationId; private final int registrationId;
private final String password; private final String password;
public ProvisioningManager(File settingsPath, SignalServiceConfiguration serviceConfiguration, String userAgent) { ProvisioningManager(PathConfig pathConfig, ServiceEnvironmentConfig serviceEnvironmentConfig, String userAgent) {
this.pathConfig = PathConfig.createDefault(settingsPath); this.pathConfig = pathConfig;
this.serviceConfiguration = serviceConfiguration; this.serviceEnvironmentConfig = serviceEnvironmentConfig;
this.userAgent = userAgent; this.userAgent = userAgent;
identityKey = KeyUtils.generateIdentityKeyPair(); identityKey = KeyUtils.generateIdentityKeyPair();
@ -62,11 +64,11 @@ public class ProvisioningManager {
final SleepTimer timer = new UptimeSleepTimer(); final SleepTimer timer = new UptimeSleepTimer();
GroupsV2Operations groupsV2Operations; GroupsV2Operations groupsV2Operations;
try { try {
groupsV2Operations = new GroupsV2Operations(ClientZkOperations.create(serviceConfiguration)); groupsV2Operations = new GroupsV2Operations(ClientZkOperations.create(serviceEnvironmentConfig.getSignalServiceConfiguration()));
} catch (Throwable ignored) { } catch (Throwable ignored) {
groupsV2Operations = null; groupsV2Operations = null;
} }
accountManager = new SignalServiceAccountManager(serviceConfiguration, accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(),
new DynamicCredentialsProvider(null, null, password, null, SignalServiceAddress.DEFAULT_DEVICE_ID), new DynamicCredentialsProvider(null, null, password, null, SignalServiceAddress.DEFAULT_DEVICE_ID),
userAgent, userAgent,
groupsV2Operations, groupsV2Operations,
@ -74,6 +76,18 @@ public class ProvisioningManager {
timer); timer);
} }
public static ProvisioningManager init(
File settingsPath, ServiceEnvironment serviceEnvironment, String userAgent
) {
PathConfig pathConfig = PathConfig.createDefault(settingsPath);
final ServiceEnvironmentConfig serviceConfiguration = ServiceConfig.getServiceEnvironmentConfig(
serviceEnvironment,
userAgent);
return new ProvisioningManager(pathConfig, serviceConfiguration, userAgent);
}
public String getDeviceLinkUri() throws TimeoutException, IOException { public String getDeviceLinkUri() throws TimeoutException, IOException {
String deviceUuid = accountManager.getNewDeviceUuid(); String deviceUuid = accountManager.getNewDeviceUuid();
@ -120,7 +134,7 @@ public class ProvisioningManager {
profileKey)) { profileKey)) {
account.save(); account.save();
try (Manager m = new Manager(account, pathConfig, serviceConfiguration, userAgent)) { try (Manager m = new Manager(account, pathConfig, serviceEnvironmentConfig, userAgent)) {
try { try {
m.refreshPreKeys(); m.refreshPreKeys();

View file

@ -16,6 +16,9 @@
*/ */
package org.asamk.signal.manager; package org.asamk.signal.manager;
import org.asamk.signal.manager.config.ServiceConfig;
import org.asamk.signal.manager.config.ServiceEnvironment;
import org.asamk.signal.manager.config.ServiceEnvironmentConfig;
import org.asamk.signal.manager.helper.PinHelper; import org.asamk.signal.manager.helper.PinHelper;
import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.storage.SignalAccount;
import org.asamk.signal.manager.util.KeyUtils; import org.asamk.signal.manager.util.KeyUtils;
@ -28,11 +31,12 @@ import org.whispersystems.signalservice.api.KeyBackupService;
import org.whispersystems.signalservice.api.KeyBackupServicePinException; import org.whispersystems.signalservice.api.KeyBackupServicePinException;
import org.whispersystems.signalservice.api.KeyBackupSystemNoDataException; import org.whispersystems.signalservice.api.KeyBackupSystemNoDataException;
import org.whispersystems.signalservice.api.SignalServiceAccountManager; import org.whispersystems.signalservice.api.SignalServiceAccountManager;
import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations;
import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations;
import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.util.SleepTimer; import org.whispersystems.signalservice.api.util.SleepTimer;
import org.whispersystems.signalservice.api.util.UptimeSleepTimer; import org.whispersystems.signalservice.api.util.UptimeSleepTimer;
import org.whispersystems.signalservice.api.util.UuidUtil; import org.whispersystems.signalservice.api.util.UuidUtil;
import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration;
import org.whispersystems.signalservice.internal.push.LockedException; import org.whispersystems.signalservice.internal.push.LockedException;
import org.whispersystems.signalservice.internal.push.VerifyAccountResponse; import org.whispersystems.signalservice.internal.push.VerifyAccountResponse;
import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider; import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider;
@ -46,7 +50,7 @@ public class RegistrationManager implements Closeable {
private SignalAccount account; private SignalAccount account;
private final PathConfig pathConfig; private final PathConfig pathConfig;
private final SignalServiceConfiguration serviceConfiguration; private final ServiceEnvironmentConfig serviceEnvironmentConfig;
private final String userAgent; private final String userAgent;
private final SignalServiceAccountManager accountManager; private final SignalServiceAccountManager accountManager;
@ -55,31 +59,49 @@ public class RegistrationManager implements Closeable {
public RegistrationManager( public RegistrationManager(
SignalAccount account, SignalAccount account,
PathConfig pathConfig, PathConfig pathConfig,
SignalServiceConfiguration serviceConfiguration, ServiceEnvironmentConfig serviceEnvironmentConfig,
String userAgent String userAgent
) { ) {
this.account = account; this.account = account;
this.pathConfig = pathConfig; this.pathConfig = pathConfig;
this.serviceConfiguration = serviceConfiguration; this.serviceEnvironmentConfig = serviceEnvironmentConfig;
this.userAgent = userAgent; this.userAgent = userAgent;
final SleepTimer timer = new UptimeSleepTimer(); final SleepTimer timer = new UptimeSleepTimer();
this.accountManager = new SignalServiceAccountManager(serviceConfiguration, new DynamicCredentialsProvider( GroupsV2Operations groupsV2Operations;
try {
groupsV2Operations = new GroupsV2Operations(ClientZkOperations.create(serviceEnvironmentConfig.getSignalServiceConfiguration()));
} catch (Throwable ignored) {
groupsV2Operations = null;
}
this.accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(),
new DynamicCredentialsProvider(
// Using empty UUID, because registering doesn't work otherwise // Using empty UUID, because registering doesn't work otherwise
null, null,
account.getUsername(), account.getUsername(),
account.getPassword(), account.getPassword(),
account.getSignalingKey(), account.getSignalingKey(),
SignalServiceAddress.DEFAULT_DEVICE_ID), userAgent, null, ServiceConfig.AUTOMATIC_NETWORK_RETRY, timer); SignalServiceAddress.DEFAULT_DEVICE_ID),
final KeyBackupService keyBackupService = ServiceConfig.createKeyBackupService(accountManager); userAgent,
groupsV2Operations,
ServiceConfig.AUTOMATIC_NETWORK_RETRY,
timer);
final KeyBackupService keyBackupService = accountManager.getKeyBackupService(ServiceConfig.getIasKeyStore(),
serviceEnvironmentConfig.getKeyBackupConfig().getEnclaveName(),
serviceEnvironmentConfig.getKeyBackupConfig().getServiceId(),
serviceEnvironmentConfig.getKeyBackupConfig().getMrenclave(),
10);
this.pinHelper = new PinHelper(keyBackupService); this.pinHelper = new PinHelper(keyBackupService);
} }
public static RegistrationManager init( public static RegistrationManager init(
String username, File settingsPath, SignalServiceConfiguration serviceConfiguration, String userAgent String username, File settingsPath, ServiceEnvironment serviceEnvironment, String userAgent
) throws IOException { ) throws IOException {
PathConfig pathConfig = PathConfig.createDefault(settingsPath); PathConfig pathConfig = PathConfig.createDefault(settingsPath);
final ServiceEnvironmentConfig serviceConfiguration = ServiceConfig.getServiceEnvironmentConfig(
serviceEnvironment,
userAgent);
if (!SignalAccount.userExists(pathConfig.getDataPath(), username)) { if (!SignalAccount.userExists(pathConfig.getDataPath(), username)) {
IdentityKeyPair identityKey = KeyUtils.generateIdentityKeyPair(); IdentityKeyPair identityKey = KeyUtils.generateIdentityKeyPair();
int registrationId = KeyHelper.generateRegistrationId(false); int registrationId = KeyHelper.generateRegistrationId(false);
@ -159,7 +181,7 @@ public class RegistrationManager implements Closeable {
account.getSignalProtocolStore().getIdentityKeyPair().getPublicKey(), account.getSignalProtocolStore().getIdentityKeyPair().getPublicKey(),
TrustLevel.TRUSTED_VERIFIED); TrustLevel.TRUSTED_VERIFIED);
try (Manager m = new Manager(account, pathConfig, serviceConfiguration, userAgent)) { try (Manager m = new Manager(account, pathConfig, serviceEnvironmentConfig, userAgent)) {
m.refreshPreKeys(); m.refreshPreKeys();

View file

@ -1,141 +0,0 @@
package org.asamk.signal.manager;
import org.bouncycastle.util.encoders.Hex;
import org.signal.zkgroup.ServerPublicParams;
import org.whispersystems.libsignal.InvalidKeyException;
import org.whispersystems.libsignal.ecc.Curve;
import org.whispersystems.libsignal.ecc.ECPublicKey;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.KeyBackupService;
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
import org.whispersystems.signalservice.api.account.AccountAttributes;
import org.whispersystems.signalservice.api.push.TrustStore;
import org.whispersystems.signalservice.internal.configuration.SignalCdnUrl;
import org.whispersystems.signalservice.internal.configuration.SignalContactDiscoveryUrl;
import org.whispersystems.signalservice.internal.configuration.SignalKeyBackupServiceUrl;
import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration;
import org.whispersystems.signalservice.internal.configuration.SignalServiceUrl;
import org.whispersystems.signalservice.internal.configuration.SignalStorageUrl;
import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.Base64;
import java.util.List;
import java.util.Map;
import okhttp3.Dns;
import okhttp3.Interceptor;
public class ServiceConfig {
final static byte[] UNIDENTIFIED_SENDER_TRUST_ROOT = Base64.getDecoder()
.decode("BXu6QIKVz5MA8gstzfOgRQGqyLqOwNKHL6INkv3IHWMF");
final static int PREKEY_MINIMUM_COUNT = 20;
final static int PREKEY_BATCH_SIZE = 100;
final static int MAX_ATTACHMENT_SIZE = 150 * 1024 * 1024;
final static long MAX_ENVELOPE_SIZE = 0;
final static long AVATAR_DOWNLOAD_FAILSAFE_MAX_SIZE = 10 * 1024 * 1024;
final static boolean AUTOMATIC_NETWORK_RETRY = true;
final static String CDS_MRENCLAVE = "c98e00a4e3ff977a56afefe7362a27e4961e4f19e211febfbb19b897e6b80b15";
final static String KEY_BACKUP_ENCLAVE_NAME = "fe7c1bfae98f9b073d220366ea31163ee82f6d04bead774f71ca8e5c40847bfe";
final static byte[] KEY_BACKUP_SERVICE_ID = Hex.decode(
"fe7c1bfae98f9b073d220366ea31163ee82f6d04bead774f71ca8e5c40847bfe");
final static String KEY_BACKUP_MRENCLAVE = "a3baab19ef6ce6f34ab9ebb25ba722725ae44a8872dc0ff08ad6d83a9489de87";
private final static String URL = "https://textsecure-service.whispersystems.org";
private final static String CDN_URL = "https://cdn.signal.org";
private final static String CDN2_URL = "https://cdn2.signal.org";
private final static String SIGNAL_CONTACT_DISCOVERY_URL = "https://api.directory.signal.org";
private final static String SIGNAL_KEY_BACKUP_URL = "https://api.backup.signal.org";
private final static String STORAGE_URL = "https://storage.signal.org";
private final static TrustStore TRUST_STORE = new WhisperTrustStore();
private final static TrustStore IAS_TRUST_STORE = new IasTrustStore();
private final static Optional<Dns> dns = Optional.absent();
private final static byte[] zkGroupServerPublicParams = Base64.getDecoder()
.decode("AMhf5ywVwITZMsff/eCyudZx9JDmkkkbV6PInzG4p8x3VqVJSFiMvnvlEKWuRob/1eaIetR31IYeAbm0NdOuHH8Qi+Rexi1wLlpzIo1gstHWBfZzy1+qHRV5A4TqPp15YzBPm0WSggW6PbSn+F4lf57VCnHF7p8SvzAA2ZZJPYJURt8X7bbg+H3i+PEjH9DXItNEqs2sNcug37xZQDLm7X0=");
static final AccountAttributes.Capabilities capabilities;
static {
boolean zkGroupAvailable;
try {
new ServerPublicParams(zkGroupServerPublicParams);
zkGroupAvailable = true;
} catch (Throwable ignored) {
zkGroupAvailable = false;
}
capabilities = new AccountAttributes.Capabilities(false, zkGroupAvailable, false, zkGroupAvailable);
}
public static SignalServiceConfiguration createDefaultServiceConfiguration(String userAgent) {
final Interceptor userAgentInterceptor = chain -> chain.proceed(chain.request()
.newBuilder()
.header("User-Agent", userAgent)
.build());
final List<Interceptor> interceptors = List.of(userAgentInterceptor);
return new SignalServiceConfiguration(new SignalServiceUrl[]{new SignalServiceUrl(URL, TRUST_STORE)},
makeSignalCdnUrlMapFor(new SignalCdnUrl[]{new SignalCdnUrl(CDN_URL, TRUST_STORE)},
new SignalCdnUrl[]{new SignalCdnUrl(CDN2_URL, TRUST_STORE)}),
new SignalContactDiscoveryUrl[]{new SignalContactDiscoveryUrl(SIGNAL_CONTACT_DISCOVERY_URL,
TRUST_STORE)},
new SignalKeyBackupServiceUrl[]{new SignalKeyBackupServiceUrl(SIGNAL_KEY_BACKUP_URL, TRUST_STORE)},
new SignalStorageUrl[]{new SignalStorageUrl(STORAGE_URL, TRUST_STORE)},
interceptors,
dns,
zkGroupServerPublicParams);
}
public static AccountAttributes.Capabilities getCapabilities() {
return capabilities;
}
static KeyStore getIasKeyStore() {
try {
TrustStore contactTrustStore = IAS_TRUST_STORE;
KeyStore keyStore = KeyStore.getInstance("BKS");
keyStore.load(contactTrustStore.getKeyStoreInputStream(),
contactTrustStore.getKeyStorePassword().toCharArray());
return keyStore;
} catch (KeyStoreException | CertificateException | IOException | NoSuchAlgorithmException e) {
throw new AssertionError(e);
}
}
static KeyBackupService createKeyBackupService(SignalServiceAccountManager accountManager) {
KeyStore keyStore = ServiceConfig.getIasKeyStore();
return accountManager.getKeyBackupService(keyStore,
ServiceConfig.KEY_BACKUP_ENCLAVE_NAME,
ServiceConfig.KEY_BACKUP_SERVICE_ID,
ServiceConfig.KEY_BACKUP_MRENCLAVE,
10);
}
static ECPublicKey getUnidentifiedSenderTrustRoot() {
try {
return Curve.decodePoint(UNIDENTIFIED_SENDER_TRUST_ROOT, 0);
} catch (InvalidKeyException e) {
throw new AssertionError(e);
}
}
private static Map<Integer, SignalCdnUrl[]> makeSignalCdnUrlMapFor(
SignalCdnUrl[] cdn0Urls, SignalCdnUrl[] cdn2Urls
) {
return Map.of(0, cdn0Urls, 2, cdn2Urls);
}
private ServiceConfig() {
}
}

View file

@ -1,4 +1,4 @@
package org.asamk.signal.manager; package org.asamk.signal.manager.config;
import org.whispersystems.signalservice.api.push.TrustStore; import org.whispersystems.signalservice.api.push.TrustStore;

View file

@ -0,0 +1,26 @@
package org.asamk.signal.manager.config;
public class KeyBackupConfig {
private final String enclaveName;
private final byte[] serviceId;
private final String mrenclave;
public KeyBackupConfig(final String enclaveName, final byte[] serviceId, final String mrenclave) {
this.enclaveName = enclaveName;
this.serviceId = serviceId;
this.mrenclave = mrenclave;
}
public String getEnclaveName() {
return enclaveName;
}
public byte[] getServiceId() {
return serviceId;
}
public String getMrenclave() {
return mrenclave;
}
}

View file

@ -0,0 +1,82 @@
package org.asamk.signal.manager.config;
import org.bouncycastle.util.encoders.Hex;
import org.whispersystems.libsignal.InvalidKeyException;
import org.whispersystems.libsignal.ecc.Curve;
import org.whispersystems.libsignal.ecc.ECPublicKey;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.push.TrustStore;
import org.whispersystems.signalservice.internal.configuration.SignalCdnUrl;
import org.whispersystems.signalservice.internal.configuration.SignalContactDiscoveryUrl;
import org.whispersystems.signalservice.internal.configuration.SignalKeyBackupServiceUrl;
import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration;
import org.whispersystems.signalservice.internal.configuration.SignalServiceUrl;
import org.whispersystems.signalservice.internal.configuration.SignalStorageUrl;
import java.util.Base64;
import java.util.List;
import java.util.Map;
import okhttp3.Dns;
import okhttp3.Interceptor;
class LiveConfig {
private final static byte[] UNIDENTIFIED_SENDER_TRUST_ROOT = Base64.getDecoder()
.decode("BXu6QIKVz5MA8gstzfOgRQGqyLqOwNKHL6INkv3IHWMF");
private final static String CDS_MRENCLAVE = "c98e00a4e3ff977a56afefe7362a27e4961e4f19e211febfbb19b897e6b80b15";
private final static String KEY_BACKUP_ENCLAVE_NAME = "fe7c1bfae98f9b073d220366ea31163ee82f6d04bead774f71ca8e5c40847bfe";
private final static byte[] KEY_BACKUP_SERVICE_ID = Hex.decode(
"fe7c1bfae98f9b073d220366ea31163ee82f6d04bead774f71ca8e5c40847bfe");
private final static String KEY_BACKUP_MRENCLAVE = "a3baab19ef6ce6f34ab9ebb25ba722725ae44a8872dc0ff08ad6d83a9489de87";
private final static String URL = "https://textsecure-service.whispersystems.org";
private final static String CDN_URL = "https://cdn.signal.org";
private final static String CDN2_URL = "https://cdn2.signal.org";
private final static String SIGNAL_CONTACT_DISCOVERY_URL = "https://api.directory.signal.org";
private final static String SIGNAL_KEY_BACKUP_URL = "https://api.backup.signal.org";
private final static String STORAGE_URL = "https://storage.signal.org";
private final static TrustStore TRUST_STORE = new WhisperTrustStore();
private final static Optional<Dns> dns = Optional.absent();
private final static byte[] zkGroupServerPublicParams = Base64.getDecoder()
.decode("AMhf5ywVwITZMsff/eCyudZx9JDmkkkbV6PInzG4p8x3VqVJSFiMvnvlEKWuRob/1eaIetR31IYeAbm0NdOuHH8Qi+Rexi1wLlpzIo1gstHWBfZzy1+qHRV5A4TqPp15YzBPm0WSggW6PbSn+F4lf57VCnHF7p8SvzAA2ZZJPYJURt8X7bbg+H3i+PEjH9DXItNEqs2sNcug37xZQDLm7X0=");
static SignalServiceConfiguration createDefaultServiceConfiguration(
final List<Interceptor> interceptors
) {
return new SignalServiceConfiguration(new SignalServiceUrl[]{new SignalServiceUrl(URL, TRUST_STORE)},
Map.of(0,
new SignalCdnUrl[]{new SignalCdnUrl(CDN_URL, TRUST_STORE)},
2,
new SignalCdnUrl[]{new SignalCdnUrl(CDN2_URL, TRUST_STORE)}),
new SignalContactDiscoveryUrl[]{new SignalContactDiscoveryUrl(SIGNAL_CONTACT_DISCOVERY_URL,
TRUST_STORE)},
new SignalKeyBackupServiceUrl[]{new SignalKeyBackupServiceUrl(SIGNAL_KEY_BACKUP_URL, TRUST_STORE)},
new SignalStorageUrl[]{new SignalStorageUrl(STORAGE_URL, TRUST_STORE)},
interceptors,
dns,
zkGroupServerPublicParams);
}
static ECPublicKey getUnidentifiedSenderTrustRoot() {
try {
return Curve.decodePoint(UNIDENTIFIED_SENDER_TRUST_ROOT, 0);
} catch (InvalidKeyException e) {
throw new AssertionError(e);
}
}
static KeyBackupConfig createKeyBackupConfig() {
return new KeyBackupConfig(KEY_BACKUP_ENCLAVE_NAME, KEY_BACKUP_SERVICE_ID, KEY_BACKUP_MRENCLAVE);
}
static String getCdsMrenclave() {
return CDS_MRENCLAVE;
}
private LiveConfig() {
}
}

View file

@ -0,0 +1,82 @@
package org.asamk.signal.manager.config;
import org.bouncycastle.util.encoders.Hex;
import org.whispersystems.libsignal.InvalidKeyException;
import org.whispersystems.libsignal.ecc.Curve;
import org.whispersystems.libsignal.ecc.ECPublicKey;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.push.TrustStore;
import org.whispersystems.signalservice.internal.configuration.SignalCdnUrl;
import org.whispersystems.signalservice.internal.configuration.SignalContactDiscoveryUrl;
import org.whispersystems.signalservice.internal.configuration.SignalKeyBackupServiceUrl;
import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration;
import org.whispersystems.signalservice.internal.configuration.SignalServiceUrl;
import org.whispersystems.signalservice.internal.configuration.SignalStorageUrl;
import java.util.Base64;
import java.util.List;
import java.util.Map;
import okhttp3.Dns;
import okhttp3.Interceptor;
class SandboxConfig {
private final static byte[] UNIDENTIFIED_SENDER_TRUST_ROOT = Base64.getDecoder()
.decode("BbqY1DzohE4NUZoVF+L18oUPrK3kILllLEJh2UnPSsEx");
private final static String CDS_MRENCLAVE = "c98e00a4e3ff977a56afefe7362a27e4961e4f19e211febfbb19b897e6b80b15";
private final static String KEY_BACKUP_ENCLAVE_NAME = "823a3b2c037ff0cbe305cc48928cfcc97c9ed4a8ca6d49af6f7d6981fb60a4e9";
private final static byte[] KEY_BACKUP_SERVICE_ID = Hex.decode(
"038c40bbbacdc873caa81ac793bb75afde6dfe436a99ab1f15e3f0cbb7434ced");
private final static String KEY_BACKUP_MRENCLAVE = "a3baab19ef6ce6f34ab9ebb25ba722725ae44a8872dc0ff08ad6d83a9489de87";
private final static String URL = "https://textsecure-service-staging.whispersystems.org";
private final static String CDN_URL = "https://cdn-staging.signal.org";
private final static String CDN2_URL = "https://cdn2-staging.signal.org";
private final static String SIGNAL_CONTACT_DISCOVERY_URL = "https://api-staging.directory.signal.org";
private final static String SIGNAL_KEY_BACKUP_URL = "https://api-staging.backup.signal.org";
private final static String STORAGE_URL = "https://storage-staging.signal.org";
private final static TrustStore TRUST_STORE = new WhisperTrustStore();
private final static Optional<Dns> dns = Optional.absent();
private final static byte[] zkGroupServerPublicParams = Base64.getDecoder()
.decode("ABSY21VckQcbSXVNCGRYJcfWHiAMZmpTtTELcDmxgdFbtp/bWsSxZdMKzfCp8rvIs8ocCU3B37fT3r4Mi5qAemeGeR2X+/YmOGR5ofui7tD5mDQfstAI9i+4WpMtIe8KC3wU5w3Inq3uNWVmoGtpKndsNfwJrCg0Hd9zmObhypUnSkfYn2ooMOOnBpfdanRtrvetZUayDMSC5iSRcXKpdls=");
static SignalServiceConfiguration createDefaultServiceConfiguration(
final List<Interceptor> interceptors
) {
return new SignalServiceConfiguration(new SignalServiceUrl[]{new SignalServiceUrl(URL, TRUST_STORE)},
Map.of(0,
new SignalCdnUrl[]{new SignalCdnUrl(CDN_URL, TRUST_STORE)},
2,
new SignalCdnUrl[]{new SignalCdnUrl(CDN2_URL, TRUST_STORE)}),
new SignalContactDiscoveryUrl[]{new SignalContactDiscoveryUrl(SIGNAL_CONTACT_DISCOVERY_URL,
TRUST_STORE)},
new SignalKeyBackupServiceUrl[]{new SignalKeyBackupServiceUrl(SIGNAL_KEY_BACKUP_URL, TRUST_STORE)},
new SignalStorageUrl[]{new SignalStorageUrl(STORAGE_URL, TRUST_STORE)},
interceptors,
dns,
zkGroupServerPublicParams);
}
static ECPublicKey getUnidentifiedSenderTrustRoot() {
try {
return Curve.decodePoint(UNIDENTIFIED_SENDER_TRUST_ROOT, 0);
} catch (InvalidKeyException e) {
throw new AssertionError(e);
}
}
static KeyBackupConfig createKeyBackupConfig() {
return new KeyBackupConfig(KEY_BACKUP_ENCLAVE_NAME, KEY_BACKUP_SERVICE_ID, KEY_BACKUP_MRENCLAVE);
}
static String getCdsMrenclave() {
return CDS_MRENCLAVE;
}
private SandboxConfig() {
}
}

View file

@ -0,0 +1,85 @@
package org.asamk.signal.manager.config;
import org.signal.zkgroup.internal.Native;
import org.whispersystems.signalservice.api.account.AccountAttributes;
import org.whispersystems.signalservice.api.push.TrustStore;
import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.List;
import okhttp3.Interceptor;
public class ServiceConfig {
public final static int PREKEY_MINIMUM_COUNT = 20;
public final static int PREKEY_BATCH_SIZE = 100;
public final static int MAX_ATTACHMENT_SIZE = 150 * 1024 * 1024;
public final static long MAX_ENVELOPE_SIZE = 0;
public final static long AVATAR_DOWNLOAD_FAILSAFE_MAX_SIZE = 10 * 1024 * 1024;
public final static boolean AUTOMATIC_NETWORK_RETRY = true;
private final static KeyStore iasKeyStore;
public static final AccountAttributes.Capabilities capabilities;
static {
boolean zkGroupAvailable;
try {
Native.serverPublicParamsCheckValidContentsJNI(new byte[]{});
zkGroupAvailable = true;
} catch (Throwable ignored) {
zkGroupAvailable = false;
}
capabilities = new AccountAttributes.Capabilities(false, zkGroupAvailable, false, zkGroupAvailable);
try {
TrustStore contactTrustStore = new IasTrustStore();
KeyStore keyStore = KeyStore.getInstance("BKS");
keyStore.load(contactTrustStore.getKeyStoreInputStream(),
contactTrustStore.getKeyStorePassword().toCharArray());
iasKeyStore = keyStore;
} catch (KeyStoreException | CertificateException | IOException | NoSuchAlgorithmException e) {
throw new AssertionError(e);
}
}
public static AccountAttributes.Capabilities getCapabilities() {
return capabilities;
}
public static KeyStore getIasKeyStore() {
return iasKeyStore;
}
public static ServiceEnvironmentConfig getServiceEnvironmentConfig(
ServiceEnvironment serviceEnvironment, String userAgent
) {
final Interceptor userAgentInterceptor = chain -> chain.proceed(chain.request()
.newBuilder()
.header("User-Agent", userAgent)
.build());
final List<Interceptor> interceptors = List.of(userAgentInterceptor);
switch (serviceEnvironment) {
case LIVE:
return new ServiceEnvironmentConfig(LiveConfig.createDefaultServiceConfiguration(interceptors),
LiveConfig.getUnidentifiedSenderTrustRoot(),
LiveConfig.createKeyBackupConfig(),
LiveConfig.getCdsMrenclave());
case SANDBOX:
return new ServiceEnvironmentConfig(SandboxConfig.createDefaultServiceConfiguration(interceptors),
SandboxConfig.getUnidentifiedSenderTrustRoot(),
SandboxConfig.createKeyBackupConfig(),
SandboxConfig.getCdsMrenclave());
default:
throw new IllegalArgumentException("Unsupported environment");
}
}
}

View file

@ -0,0 +1,6 @@
package org.asamk.signal.manager.config;
public enum ServiceEnvironment {
LIVE,
SANDBOX,
}

View file

@ -0,0 +1,43 @@
package org.asamk.signal.manager.config;
import org.whispersystems.libsignal.ecc.ECPublicKey;
import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration;
public class ServiceEnvironmentConfig {
private final SignalServiceConfiguration signalServiceConfiguration;
private final ECPublicKey unidentifiedSenderTrustRoot;
private final KeyBackupConfig keyBackupConfig;
private final String cdsMrenclave;
public ServiceEnvironmentConfig(
final SignalServiceConfiguration signalServiceConfiguration,
final ECPublicKey unidentifiedSenderTrustRoot,
final KeyBackupConfig keyBackupConfig,
final String cdsMrenclave
) {
this.signalServiceConfiguration = signalServiceConfiguration;
this.unidentifiedSenderTrustRoot = unidentifiedSenderTrustRoot;
this.keyBackupConfig = keyBackupConfig;
this.cdsMrenclave = cdsMrenclave;
}
public SignalServiceConfiguration getSignalServiceConfiguration() {
return signalServiceConfiguration;
}
public ECPublicKey getUnidentifiedSenderTrustRoot() {
return unidentifiedSenderTrustRoot;
}
public KeyBackupConfig getKeyBackupConfig() {
return keyBackupConfig;
}
public String getCdsMrenclave() {
return cdsMrenclave;
}
}

View file

@ -1,4 +1,4 @@
package org.asamk.signal.manager; package org.asamk.signal.manager.config;
import org.whispersystems.signalservice.api.push.TrustStore; import org.whispersystems.signalservice.api.push.TrustStore;

View file

@ -21,14 +21,14 @@ import org.asamk.signal.manager.Manager;
import org.asamk.signal.manager.NotRegisteredException; import org.asamk.signal.manager.NotRegisteredException;
import org.asamk.signal.manager.ProvisioningManager; import org.asamk.signal.manager.ProvisioningManager;
import org.asamk.signal.manager.RegistrationManager; import org.asamk.signal.manager.RegistrationManager;
import org.asamk.signal.manager.ServiceConfig; import org.asamk.signal.manager.config.ServiceConfig;
import org.asamk.signal.manager.config.ServiceEnvironment;
import org.asamk.signal.util.IOUtils; import org.asamk.signal.util.IOUtils;
import org.freedesktop.dbus.connections.impl.DBusConnection; import org.freedesktop.dbus.connections.impl.DBusConnection;
import org.freedesktop.dbus.exceptions.DBusException; import org.freedesktop.dbus.exceptions.DBusException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@ -114,8 +114,7 @@ public class App {
dataPath = getDefaultDataPath(); dataPath = getDefaultDataPath();
} }
final SignalServiceConfiguration serviceConfiguration = ServiceConfig.createDefaultServiceConfiguration( final ServiceEnvironment serviceEnvironment = ServiceEnvironment.LIVE;
BaseConfig.USER_AGENT);
if (!ServiceConfig.getCapabilities().isGv2()) { if (!ServiceConfig.getCapabilities().isGv2()) {
logger.warn("WARNING: Support for new group V2 is disabled," logger.warn("WARNING: Support for new group V2 is disabled,"
@ -128,7 +127,7 @@ public class App {
return 1; return 1;
} }
return handleProvisioningCommand((ProvisioningCommand) command, dataPath, serviceConfiguration); return handleProvisioningCommand((ProvisioningCommand) command, dataPath, serviceEnvironment);
} }
if (username == null) { if (username == null) {
@ -139,7 +138,7 @@ public class App {
} }
if (command instanceof MultiLocalCommand) { if (command instanceof MultiLocalCommand) {
return handleMultiLocalCommand((MultiLocalCommand) command, dataPath, serviceConfiguration, usernames); return handleMultiLocalCommand((MultiLocalCommand) command, dataPath, serviceEnvironment, usernames);
} }
if (usernames.size() > 1) { if (usernames.size() > 1) {
@ -154,7 +153,7 @@ public class App {
} }
if (command instanceof RegistrationCommand) { if (command instanceof RegistrationCommand) {
return handleRegistrationCommand((RegistrationCommand) command, username, dataPath, serviceConfiguration); return handleRegistrationCommand((RegistrationCommand) command, username, dataPath, serviceEnvironment);
} }
if (!(command instanceof LocalCommand)) { if (!(command instanceof LocalCommand)) {
@ -162,15 +161,13 @@ public class App {
return 1; return 1;
} }
return handleLocalCommand((LocalCommand) command, username, dataPath, serviceConfiguration); return handleLocalCommand((LocalCommand) command, username, dataPath, serviceEnvironment);
} }
private int handleProvisioningCommand( private int handleProvisioningCommand(
final ProvisioningCommand command, final ProvisioningCommand command, final File dataPath, final ServiceEnvironment serviceEnvironment
final File dataPath,
final SignalServiceConfiguration serviceConfiguration
) { ) {
ProvisioningManager pm = new ProvisioningManager(dataPath, serviceConfiguration, BaseConfig.USER_AGENT); ProvisioningManager pm = ProvisioningManager.init(dataPath, serviceEnvironment, BaseConfig.USER_AGENT);
return command.handleCommand(ns, pm); return command.handleCommand(ns, pm);
} }
@ -178,11 +175,11 @@ public class App {
final RegistrationCommand command, final RegistrationCommand command,
final String username, final String username,
final File dataPath, final File dataPath,
final SignalServiceConfiguration serviceConfiguration final ServiceEnvironment serviceEnvironment
) { ) {
final RegistrationManager manager; final RegistrationManager manager;
try { try {
manager = RegistrationManager.init(username, dataPath, serviceConfiguration, BaseConfig.USER_AGENT); manager = RegistrationManager.init(username, dataPath, serviceEnvironment, BaseConfig.USER_AGENT);
} catch (Throwable e) { } catch (Throwable e) {
logger.error("Error loading or creating state file: {}", e.getMessage()); logger.error("Error loading or creating state file: {}", e.getMessage());
return 2; return 2;
@ -199,9 +196,9 @@ public class App {
final LocalCommand command, final LocalCommand command,
final String username, final String username,
final File dataPath, final File dataPath,
final SignalServiceConfiguration serviceConfiguration final ServiceEnvironment serviceEnvironment
) { ) {
try (Manager m = loadManager(username, dataPath, serviceConfiguration)) { try (Manager m = loadManager(username, dataPath, serviceEnvironment)) {
if (m == null) { if (m == null) {
return 2; return 2;
} }
@ -216,11 +213,11 @@ public class App {
private int handleMultiLocalCommand( private int handleMultiLocalCommand(
final MultiLocalCommand command, final MultiLocalCommand command,
final File dataPath, final File dataPath,
final SignalServiceConfiguration serviceConfiguration, final ServiceEnvironment serviceEnvironment,
final List<String> usernames final List<String> usernames
) { ) {
final List<Manager> managers = usernames.stream() final List<Manager> managers = usernames.stream()
.map(u -> loadManager(u, dataPath, serviceConfiguration)) .map(u -> loadManager(u, dataPath, serviceEnvironment))
.filter(Objects::nonNull) .filter(Objects::nonNull)
.collect(Collectors.toList()); .collect(Collectors.toList());
@ -237,11 +234,11 @@ public class App {
} }
private Manager loadManager( private Manager loadManager(
final String username, final File dataPath, final SignalServiceConfiguration serviceConfiguration final String username, final File dataPath, final ServiceEnvironment serviceEnvironment
) { ) {
Manager manager; Manager manager;
try { try {
manager = Manager.init(username, dataPath, serviceConfiguration, BaseConfig.USER_AGENT); manager = Manager.init(username, dataPath, serviceEnvironment, BaseConfig.USER_AGENT);
} catch (NotRegisteredException e) { } catch (NotRegisteredException e) {
logger.error("User " + username + " is not registered."); logger.error("User " + username + " is not registered.");
return null; return null;