From: AsamK Date: Sat, 23 Jan 2021 22:06:58 +0000 (+0100) Subject: Refactor ServiceConfig and add sandbox config X-Git-Tag: v0.8.0~10 X-Git-Url: https://git.nmode.ca/signal-cli/commitdiff_plain/7d802fb8c5152c5fc089052e5915c7f588057e53 Refactor ServiceConfig and add sandbox config --- diff --git a/lib/src/main/java/org/asamk/signal/manager/Manager.java b/lib/src/main/java/org/asamk/signal/manager/Manager.java index c4f32460..11ee137d 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -16,6 +16,9 @@ */ 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.GroupIdV1; 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.UptimeSleepTimer; 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.UnauthenticatedQuoteException; 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.stream.Collectors; -import static org.asamk.signal.manager.ServiceConfig.CDS_MRENCLAVE; -import static org.asamk.signal.manager.ServiceConfig.capabilities; -import static org.asamk.signal.manager.ServiceConfig.getIasKeyStore; +import static org.asamk.signal.manager.config.ServiceConfig.capabilities; public class Manager implements Closeable { 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 SignalAccount account; @@ -202,16 +202,17 @@ public class Manager implements Closeable { Manager( SignalAccount account, PathConfig pathConfig, - SignalServiceConfiguration serviceConfiguration, + ServiceEnvironmentConfig serviceEnvironmentConfig, String userAgent ) { this.account = account; - this.serviceConfiguration = serviceConfiguration; + this.serviceEnvironmentConfig = serviceEnvironmentConfig; + this.certificateValidator = new CertificateValidator(serviceEnvironmentConfig.getUnidentifiedSenderTrustRoot()); this.userAgent = userAgent; this.groupsV2Operations = capabilities.isGv2() ? new GroupsV2Operations(ClientZkOperations.create( - serviceConfiguration)) : null; + serviceEnvironmentConfig.getSignalServiceConfiguration())) : null; final SleepTimer timer = new UptimeSleepTimer(); - this.accountManager = new SignalServiceAccountManager(serviceConfiguration, + this.accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(), new DynamicCredentialsProvider(account.getUuid(), account.getUsername(), account.getPassword(), @@ -222,11 +223,18 @@ public class Manager implements Closeable { ServiceConfig.AUTOMATIC_NETWORK_RETRY, timer); 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.clientZkProfileOperations = capabilities.isGv2() ? ClientZkOperations.create(serviceConfiguration) - .getProfileOperations() : null; - this.messageReceiver = new SignalServiceMessageReceiver(serviceConfiguration, + this.clientZkProfileOperations = capabilities.isGv2() + ? ClientZkOperations.create(serviceEnvironmentConfig.getSignalServiceConfiguration()) + .getProfileOperations() + : null; + this.messageReceiver = new SignalServiceMessageReceiver(serviceEnvironmentConfig.getSignalServiceConfiguration(), account.getUuid(), account.getUsername(), account.getPassword(), @@ -275,7 +283,7 @@ public class Manager implements Closeable { } public static Manager init( - String username, File settingsPath, SignalServiceConfiguration serviceConfiguration, String userAgent + String username, File settingsPath, ServiceEnvironment serviceEnvironment, String userAgent ) throws IOException, NotRegisteredException { PathConfig pathConfig = PathConfig.createDefault(settingsPath); @@ -289,7 +297,11 @@ public class Manager implements Closeable { 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 getAllLocalUsernames(File settingsPath) { @@ -498,7 +510,7 @@ public class Manager implements Closeable { private SignalServiceMessageSender createMessageSender() { final ExecutorService executor = null; - return new SignalServiceMessageSender(serviceConfiguration, + return new SignalServiceMessageSender(serviceEnvironmentConfig.getSignalServiceConfiguration(), account.getUuid(), account.getUsername(), account.getPassword(), @@ -1262,7 +1274,9 @@ public class Manager implements Closeable { private Map getRegisteredUsers(final Set numbersMissingUuid) throws IOException { try { - return accountManager.getRegisteredUsers(getIasKeyStore(), numbersMissingUuid, CDS_MRENCLAVE); + return accountManager.getRegisteredUsers(ServiceConfig.getIasKeyStore(), + numbersMissingUuid, + serviceEnvironmentConfig.getCdsMrenclave()); } catch (Quote.InvalidQuoteFormatException | UnauthenticatedQuoteException | SignatureException | UnauthenticatedResponseException | InvalidKeyException e) { throw new IOException(e); } diff --git a/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java index 0c26cf77..c01ceabf 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java +++ b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java @@ -16,6 +16,9 @@ */ 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.util.KeyUtils; 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.util.SleepTimer; import org.whispersystems.signalservice.api.util.UptimeSleepTimer; -import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration; import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider; import java.io.File; @@ -43,7 +45,7 @@ public class ProvisioningManager { private final static Logger logger = LoggerFactory.getLogger(ProvisioningManager.class); private final PathConfig pathConfig; - private final SignalServiceConfiguration serviceConfiguration; + private final ServiceEnvironmentConfig serviceEnvironmentConfig; private final String userAgent; private final SignalServiceAccountManager accountManager; @@ -51,9 +53,9 @@ public class ProvisioningManager { private final int registrationId; private final String password; - public ProvisioningManager(File settingsPath, SignalServiceConfiguration serviceConfiguration, String userAgent) { - this.pathConfig = PathConfig.createDefault(settingsPath); - this.serviceConfiguration = serviceConfiguration; + ProvisioningManager(PathConfig pathConfig, ServiceEnvironmentConfig serviceEnvironmentConfig, String userAgent) { + this.pathConfig = pathConfig; + this.serviceEnvironmentConfig = serviceEnvironmentConfig; this.userAgent = userAgent; identityKey = KeyUtils.generateIdentityKeyPair(); @@ -62,11 +64,11 @@ public class ProvisioningManager { final SleepTimer timer = new UptimeSleepTimer(); GroupsV2Operations groupsV2Operations; try { - groupsV2Operations = new GroupsV2Operations(ClientZkOperations.create(serviceConfiguration)); + groupsV2Operations = new GroupsV2Operations(ClientZkOperations.create(serviceEnvironmentConfig.getSignalServiceConfiguration())); } catch (Throwable ignored) { groupsV2Operations = null; } - accountManager = new SignalServiceAccountManager(serviceConfiguration, + accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(), new DynamicCredentialsProvider(null, null, password, null, SignalServiceAddress.DEFAULT_DEVICE_ID), userAgent, groupsV2Operations, @@ -74,6 +76,18 @@ public class ProvisioningManager { 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 { String deviceUuid = accountManager.getNewDeviceUuid(); @@ -120,7 +134,7 @@ public class ProvisioningManager { profileKey)) { account.save(); - try (Manager m = new Manager(account, pathConfig, serviceConfiguration, userAgent)) { + try (Manager m = new Manager(account, pathConfig, serviceEnvironmentConfig, userAgent)) { try { m.refreshPreKeys(); diff --git a/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java b/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java index 506948ba..914a5fcf 100644 --- a/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java +++ b/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java @@ -16,6 +16,9 @@ */ 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.storage.SignalAccount; 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.KeyBackupSystemNoDataException; 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.util.SleepTimer; import org.whispersystems.signalservice.api.util.UptimeSleepTimer; 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.VerifyAccountResponse; import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider; @@ -46,7 +50,7 @@ public class RegistrationManager implements Closeable { private SignalAccount account; private final PathConfig pathConfig; - private final SignalServiceConfiguration serviceConfiguration; + private final ServiceEnvironmentConfig serviceEnvironmentConfig; private final String userAgent; private final SignalServiceAccountManager accountManager; @@ -55,31 +59,49 @@ public class RegistrationManager implements Closeable { public RegistrationManager( SignalAccount account, PathConfig pathConfig, - SignalServiceConfiguration serviceConfiguration, + ServiceEnvironmentConfig serviceEnvironmentConfig, String userAgent ) { this.account = account; this.pathConfig = pathConfig; - this.serviceConfiguration = serviceConfiguration; + this.serviceEnvironmentConfig = serviceEnvironmentConfig; this.userAgent = userAgent; final SleepTimer timer = new UptimeSleepTimer(); - this.accountManager = new SignalServiceAccountManager(serviceConfiguration, new DynamicCredentialsProvider( - // Using empty UUID, because registering doesn't work otherwise - null, - account.getUsername(), - account.getPassword(), - account.getSignalingKey(), - SignalServiceAddress.DEFAULT_DEVICE_ID), userAgent, null, ServiceConfig.AUTOMATIC_NETWORK_RETRY, timer); - final KeyBackupService keyBackupService = ServiceConfig.createKeyBackupService(accountManager); + 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 + null, + account.getUsername(), + account.getPassword(), + account.getSignalingKey(), + SignalServiceAddress.DEFAULT_DEVICE_ID), + 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); } public static RegistrationManager init( - String username, File settingsPath, SignalServiceConfiguration serviceConfiguration, String userAgent + String username, File settingsPath, ServiceEnvironment serviceEnvironment, String userAgent ) throws IOException { PathConfig pathConfig = PathConfig.createDefault(settingsPath); + final ServiceEnvironmentConfig serviceConfiguration = ServiceConfig.getServiceEnvironmentConfig( + serviceEnvironment, + userAgent); if (!SignalAccount.userExists(pathConfig.getDataPath(), username)) { IdentityKeyPair identityKey = KeyUtils.generateIdentityKeyPair(); int registrationId = KeyHelper.generateRegistrationId(false); @@ -159,7 +181,7 @@ public class RegistrationManager implements Closeable { account.getSignalProtocolStore().getIdentityKeyPair().getPublicKey(), TrustLevel.TRUSTED_VERIFIED); - try (Manager m = new Manager(account, pathConfig, serviceConfiguration, userAgent)) { + try (Manager m = new Manager(account, pathConfig, serviceEnvironmentConfig, userAgent)) { m.refreshPreKeys(); diff --git a/lib/src/main/java/org/asamk/signal/manager/ServiceConfig.java b/lib/src/main/java/org/asamk/signal/manager/ServiceConfig.java deleted file mode 100644 index c68e5773..00000000 --- a/lib/src/main/java/org/asamk/signal/manager/ServiceConfig.java +++ /dev/null @@ -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 = 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 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 makeSignalCdnUrlMapFor( - SignalCdnUrl[] cdn0Urls, SignalCdnUrl[] cdn2Urls - ) { - return Map.of(0, cdn0Urls, 2, cdn2Urls); - } - - private ServiceConfig() { - } -} diff --git a/lib/src/main/java/org/asamk/signal/manager/IasTrustStore.java b/lib/src/main/java/org/asamk/signal/manager/config/IasTrustStore.java similarity index 89% rename from lib/src/main/java/org/asamk/signal/manager/IasTrustStore.java rename to lib/src/main/java/org/asamk/signal/manager/config/IasTrustStore.java index f9bbb0b3..4d70809b 100644 --- a/lib/src/main/java/org/asamk/signal/manager/IasTrustStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/config/IasTrustStore.java @@ -1,4 +1,4 @@ -package org.asamk.signal.manager; +package org.asamk.signal.manager.config; import org.whispersystems.signalservice.api.push.TrustStore; diff --git a/lib/src/main/java/org/asamk/signal/manager/config/KeyBackupConfig.java b/lib/src/main/java/org/asamk/signal/manager/config/KeyBackupConfig.java new file mode 100644 index 00000000..60173c1d --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/config/KeyBackupConfig.java @@ -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; + } +} diff --git a/lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java b/lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java new file mode 100644 index 00000000..80df8bbb --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java @@ -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 = Optional.absent(); + + private final static byte[] zkGroupServerPublicParams = Base64.getDecoder() + .decode("AMhf5ywVwITZMsff/eCyudZx9JDmkkkbV6PInzG4p8x3VqVJSFiMvnvlEKWuRob/1eaIetR31IYeAbm0NdOuHH8Qi+Rexi1wLlpzIo1gstHWBfZzy1+qHRV5A4TqPp15YzBPm0WSggW6PbSn+F4lf57VCnHF7p8SvzAA2ZZJPYJURt8X7bbg+H3i+PEjH9DXItNEqs2sNcug37xZQDLm7X0="); + + static SignalServiceConfiguration createDefaultServiceConfiguration( + final List 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() { + } +} diff --git a/lib/src/main/java/org/asamk/signal/manager/config/SandboxConfig.java b/lib/src/main/java/org/asamk/signal/manager/config/SandboxConfig.java new file mode 100644 index 00000000..edb180ac --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/config/SandboxConfig.java @@ -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 = Optional.absent(); + + private final static byte[] zkGroupServerPublicParams = Base64.getDecoder() + .decode("ABSY21VckQcbSXVNCGRYJcfWHiAMZmpTtTELcDmxgdFbtp/bWsSxZdMKzfCp8rvIs8ocCU3B37fT3r4Mi5qAemeGeR2X+/YmOGR5ofui7tD5mDQfstAI9i+4WpMtIe8KC3wU5w3Inq3uNWVmoGtpKndsNfwJrCg0Hd9zmObhypUnSkfYn2ooMOOnBpfdanRtrvetZUayDMSC5iSRcXKpdls="); + + static SignalServiceConfiguration createDefaultServiceConfiguration( + final List 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() { + } +} diff --git a/lib/src/main/java/org/asamk/signal/manager/config/ServiceConfig.java b/lib/src/main/java/org/asamk/signal/manager/config/ServiceConfig.java new file mode 100644 index 00000000..7a54edd1 --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/config/ServiceConfig.java @@ -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 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"); + } + } +} diff --git a/lib/src/main/java/org/asamk/signal/manager/config/ServiceEnvironment.java b/lib/src/main/java/org/asamk/signal/manager/config/ServiceEnvironment.java new file mode 100644 index 00000000..142a2dd4 --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/config/ServiceEnvironment.java @@ -0,0 +1,6 @@ +package org.asamk.signal.manager.config; + +public enum ServiceEnvironment { + LIVE, + SANDBOX, +} diff --git a/lib/src/main/java/org/asamk/signal/manager/config/ServiceEnvironmentConfig.java b/lib/src/main/java/org/asamk/signal/manager/config/ServiceEnvironmentConfig.java new file mode 100644 index 00000000..e64472a0 --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/config/ServiceEnvironmentConfig.java @@ -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; + } +} diff --git a/lib/src/main/java/org/asamk/signal/manager/WhisperTrustStore.java b/lib/src/main/java/org/asamk/signal/manager/config/WhisperTrustStore.java similarity index 90% rename from lib/src/main/java/org/asamk/signal/manager/WhisperTrustStore.java rename to lib/src/main/java/org/asamk/signal/manager/config/WhisperTrustStore.java index 185ab599..7add1d79 100644 --- a/lib/src/main/java/org/asamk/signal/manager/WhisperTrustStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/config/WhisperTrustStore.java @@ -1,4 +1,4 @@ -package org.asamk.signal.manager; +package org.asamk.signal.manager.config; import org.whispersystems.signalservice.api.push.TrustStore; diff --git a/lib/src/main/resources/org/asamk/signal/manager/ias.store b/lib/src/main/resources/org/asamk/signal/manager/config/ias.store similarity index 100% rename from lib/src/main/resources/org/asamk/signal/manager/ias.store rename to lib/src/main/resources/org/asamk/signal/manager/config/ias.store diff --git a/lib/src/main/resources/org/asamk/signal/manager/whisper.store b/lib/src/main/resources/org/asamk/signal/manager/config/whisper.store similarity index 100% rename from lib/src/main/resources/org/asamk/signal/manager/whisper.store rename to lib/src/main/resources/org/asamk/signal/manager/config/whisper.store diff --git a/src/main/java/org/asamk/signal/App.java b/src/main/java/org/asamk/signal/App.java index 6dc3d799..e35285c3 100644 --- a/src/main/java/org/asamk/signal/App.java +++ b/src/main/java/org/asamk/signal/App.java @@ -21,14 +21,14 @@ import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.NotRegisteredException; import org.asamk.signal.manager.ProvisioningManager; 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.freedesktop.dbus.connections.impl.DBusConnection; import org.freedesktop.dbus.exceptions.DBusException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; -import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration; import java.io.File; import java.io.IOException; @@ -114,8 +114,7 @@ public class App { dataPath = getDefaultDataPath(); } - final SignalServiceConfiguration serviceConfiguration = ServiceConfig.createDefaultServiceConfiguration( - BaseConfig.USER_AGENT); + final ServiceEnvironment serviceEnvironment = ServiceEnvironment.LIVE; if (!ServiceConfig.getCapabilities().isGv2()) { logger.warn("WARNING: Support for new group V2 is disabled," @@ -128,7 +127,7 @@ public class App { return 1; } - return handleProvisioningCommand((ProvisioningCommand) command, dataPath, serviceConfiguration); + return handleProvisioningCommand((ProvisioningCommand) command, dataPath, serviceEnvironment); } if (username == null) { @@ -139,7 +138,7 @@ public class App { } if (command instanceof MultiLocalCommand) { - return handleMultiLocalCommand((MultiLocalCommand) command, dataPath, serviceConfiguration, usernames); + return handleMultiLocalCommand((MultiLocalCommand) command, dataPath, serviceEnvironment, usernames); } if (usernames.size() > 1) { @@ -154,7 +153,7 @@ public class App { } if (command instanceof RegistrationCommand) { - return handleRegistrationCommand((RegistrationCommand) command, username, dataPath, serviceConfiguration); + return handleRegistrationCommand((RegistrationCommand) command, username, dataPath, serviceEnvironment); } if (!(command instanceof LocalCommand)) { @@ -162,15 +161,13 @@ public class App { return 1; } - return handleLocalCommand((LocalCommand) command, username, dataPath, serviceConfiguration); + return handleLocalCommand((LocalCommand) command, username, dataPath, serviceEnvironment); } private int handleProvisioningCommand( - final ProvisioningCommand command, - final File dataPath, - final SignalServiceConfiguration serviceConfiguration + final ProvisioningCommand command, final File dataPath, final ServiceEnvironment serviceEnvironment ) { - ProvisioningManager pm = new ProvisioningManager(dataPath, serviceConfiguration, BaseConfig.USER_AGENT); + ProvisioningManager pm = ProvisioningManager.init(dataPath, serviceEnvironment, BaseConfig.USER_AGENT); return command.handleCommand(ns, pm); } @@ -178,11 +175,11 @@ public class App { final RegistrationCommand command, final String username, final File dataPath, - final SignalServiceConfiguration serviceConfiguration + final ServiceEnvironment serviceEnvironment ) { final RegistrationManager manager; try { - manager = RegistrationManager.init(username, dataPath, serviceConfiguration, BaseConfig.USER_AGENT); + manager = RegistrationManager.init(username, dataPath, serviceEnvironment, BaseConfig.USER_AGENT); } catch (Throwable e) { logger.error("Error loading or creating state file: {}", e.getMessage()); return 2; @@ -199,9 +196,9 @@ public class App { final LocalCommand command, final String username, 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) { return 2; } @@ -216,11 +213,11 @@ public class App { private int handleMultiLocalCommand( final MultiLocalCommand command, final File dataPath, - final SignalServiceConfiguration serviceConfiguration, + final ServiceEnvironment serviceEnvironment, final List usernames ) { final List managers = usernames.stream() - .map(u -> loadManager(u, dataPath, serviceConfiguration)) + .map(u -> loadManager(u, dataPath, serviceEnvironment)) .filter(Objects::nonNull) .collect(Collectors.toList()); @@ -237,11 +234,11 @@ public class App { } private Manager loadManager( - final String username, final File dataPath, final SignalServiceConfiguration serviceConfiguration + final String username, final File dataPath, final ServiceEnvironment serviceEnvironment ) { Manager manager; try { - manager = Manager.init(username, dataPath, serviceConfiguration, BaseConfig.USER_AGENT); + manager = Manager.init(username, dataPath, serviceEnvironment, BaseConfig.USER_AGENT); } catch (NotRegisteredException e) { logger.error("User " + username + " is not registered."); return null;