From 94a29e375f1209e39f58b7f52c1ec1ccb5ae75ce Mon Sep 17 00:00:00 2001 From: Moxie Marlinspike Date: Wed, 11 Jan 2017 15:37:51 -0800 Subject: [PATCH] Specify ConnectionSpecs for domain fronts // FREEBIE --- build.gradle | 6 +- .../securesms/ApplicationContext.java | 24 +++- .../SignalCommunicationModule.java | 15 ++- .../securesms/push/AccountManagerFactory.java | 25 +++- .../push/SignalServiceNetworkAccess.java | 107 ++++++++++++++++-- 5 files changed, 153 insertions(+), 24 deletions(-) diff --git a/build.gradle b/build.gradle index 72b3fbe1a1..fd9d67bee5 100644 --- a/build.gradle +++ b/build.gradle @@ -57,7 +57,7 @@ dependencies { compile 'org.whispersystems:jobmanager:1.0.2' compile 'org.whispersystems:libpastelog:1.0.7' - compile 'org.whispersystems:signal-service-android:2.4.6' + compile 'org.whispersystems:signal-service-android:2.4.7' compile 'me.leolin:ShortcutBadger:1.1.0-WS1' compile 'se.emilsjolander:stickylistheaders:2.7.0' @@ -123,7 +123,7 @@ dependencyVerification { 'com.google.android.gms:play-services-places:abf3a4a3b146ec7e6e753be62775e512868cf37d6f88ffe2d81167b33b57132b', 'org.whispersystems:jobmanager:506f679fc2fcf7bb6d10f00f41d6f6ea0abf75c70dc95b913398661ad538a181', 'org.whispersystems:libpastelog:bb331d9a98240fc139101128ba836c1edec3c40e000597cdbb29ebf4cbf34d88', - 'org.whispersystems:signal-service-android:737c3e8e572bd5f149d2202d768c4e0f50205f85794e05fee4045d3dfca1fc27', + 'org.whispersystems:signal-service-android:c7835376e426d4789444f1e8c02c3528fb57f148441d8f96d5d595244d7120a2', 'me.leolin:ShortcutBadger:3142d017234bfa0cdd69ccded7cc5ea63f13b97574803c8c616c9bbeaad33ad9', 'se.emilsjolander:stickylistheaders:a08ca948aa6b220f09d82f16bbbac395f6b78897e9eeac6a9f0b0ba755928eeb', 'com.jpardogo.materialtabstrip:library:c6ef812fba4f74be7dc4a905faa4c2908cba261a94c13d4f96d5e67e4aad4aaa', @@ -157,7 +157,7 @@ dependencyVerification { 'com.google.android.gms:play-services-basement:95dd882c5ffba15b9a99de3fefb05d3a01946623af67454ca00055d222f85a8d', 'com.google.android.gms:play-services-iid:54e919f9957b8b7820da7ee9b83471d00d0cac1cf08ddea8b5b41aea80bb1a70', 'org.whispersystems:signal-protocol-android:1b4b9d557c8eaf861797ff683990d482d4aa8e9f23d9b17ff0cc67a02f38cb19', - 'org.whispersystems:signal-service-java:c9b9c8585ba60e391ff08dce2351068b576d3c55531fc46141dfdae8dea20bff', + 'org.whispersystems:signal-service-java:60aad86f4af3e93220c980e3ac9b75015e1f65b6ab02418492e9c6c1d1dfe0be', 'com.nineoldandroids:library:68025a14e3e7673d6ad2f95e4b46d78d7d068343aa99256b686fe59de1b3163a', 'javax.inject:javax.inject:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff', 'com.madgag.spongycastle:core:8d6240b974b0aca4d3da9c7dd44d42339d8a374358aca5fc98e50a995764511f', diff --git a/src/org/thoughtcrime/securesms/ApplicationContext.java b/src/org/thoughtcrime/securesms/ApplicationContext.java index 0f2086a5fb..63a36dc42c 100644 --- a/src/org/thoughtcrime/securesms/ApplicationContext.java +++ b/src/org/thoughtcrime/securesms/ApplicationContext.java @@ -16,12 +16,15 @@ */ package org.thoughtcrime.securesms; -import android.app.Application; import android.content.Context; +import android.os.AsyncTask; import android.os.StrictMode; import android.os.StrictMode.ThreadPolicy; import android.os.StrictMode.VmPolicy; import android.support.multidex.MultiDexApplication; +import android.util.Log; + +import com.google.android.gms.security.ProviderInstaller; import org.thoughtcrime.securesms.crypto.PRNGFixes; import org.thoughtcrime.securesms.dependencies.AxolotlStorageModule; @@ -57,6 +60,8 @@ import dagger.ObjectGraph; */ public class ApplicationContext extends MultiDexApplication implements DependencyInjector { + private static final String TAG = ApplicationContext.class.getName(); + private ExpiringMessageManager expiringMessageManager; private JobManager jobManager; private ObjectGraph objectGraph; @@ -79,6 +84,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc initializeGcmCheck(); initializeSignedPreKeyCheck(); initializePeriodicTasks(); + initializeCircumvention(); } @Override @@ -159,4 +165,20 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc DirectoryRefreshListener.schedule(this); } + private void initializeCircumvention() { + new AsyncTask() { + @Override + protected Void doInBackground(Void... params) { + if (new SignalServiceNetworkAccess(ApplicationContext.this).isCensored(ApplicationContext.this)) { + try { + ProviderInstaller.installIfNeeded(ApplicationContext.this); + } catch (Throwable t) { + Log.w(TAG, t); + } + } + return null; + } + }.execute(); + } + } diff --git a/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java b/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java index 37b2938962..d9e8c0f6e6 100644 --- a/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java +++ b/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java @@ -33,7 +33,6 @@ import org.whispersystems.signalservice.api.SignalServiceAccountManager; import org.whispersystems.signalservice.api.SignalServiceMessageReceiver; import org.whispersystems.signalservice.api.SignalServiceMessageSender; import org.whispersystems.signalservice.api.util.CredentialsProvider; -import org.whispersystems.signalservice.internal.push.SignalServiceUrl; import dagger.Module; import dagger.Provides; @@ -61,16 +60,16 @@ import dagger.Provides; RotateSignedPreKeyJob.class}) public class SignalCommunicationModule { - private final Context context; - private final SignalServiceUrl[] urls; + private final Context context; + private final SignalServiceNetworkAccess networkAccess; public SignalCommunicationModule(Context context, SignalServiceNetworkAccess networkAccess) { - this.context = context; - this.urls = networkAccess.getConfiguration(context); + this.context = context; + this.networkAccess = networkAccess; } @Provides SignalServiceAccountManager provideSignalAccountManager() { - return new SignalServiceAccountManager(urls, + return new SignalServiceAccountManager(networkAccess.getConfiguration(context), TextSecurePreferences.getLocalNumber(context), TextSecurePreferences.getPushServerPassword(context), BuildConfig.USER_AGENT); @@ -81,7 +80,7 @@ public class SignalCommunicationModule { return new SignalMessageSenderFactory() { @Override public SignalServiceMessageSender create() { - return new SignalServiceMessageSender(urls, + return new SignalServiceMessageSender(networkAccess.getConfiguration(context), TextSecurePreferences.getLocalNumber(context), TextSecurePreferences.getPushServerPassword(context), new SignalProtocolStoreImpl(context), @@ -93,7 +92,7 @@ public class SignalCommunicationModule { } @Provides SignalServiceMessageReceiver provideSignalMessageReceiver() { - return new SignalServiceMessageReceiver(urls, + return new SignalServiceMessageReceiver(networkAccess.getConfiguration(context), new DynamicCredentialsProvider(context), BuildConfig.USER_AGENT); } diff --git a/src/org/thoughtcrime/securesms/push/AccountManagerFactory.java b/src/org/thoughtcrime/securesms/push/AccountManagerFactory.java index 694aa5db92..544e12c3d9 100644 --- a/src/org/thoughtcrime/securesms/push/AccountManagerFactory.java +++ b/src/org/thoughtcrime/securesms/push/AccountManagerFactory.java @@ -1,6 +1,13 @@ package org.thoughtcrime.securesms.push; import android.content.Context; +import android.content.Intent; +import android.os.AsyncTask; +import android.util.Log; + +import com.google.android.gms.common.GooglePlayServicesNotAvailableException; +import com.google.android.gms.common.GooglePlayServicesRepairableException; +import com.google.android.gms.security.ProviderInstaller; import org.thoughtcrime.securesms.BuildConfig; import org.thoughtcrime.securesms.util.TextSecurePreferences; @@ -9,6 +16,8 @@ import org.whispersystems.signalservice.internal.push.SignalServiceUrl; public class AccountManagerFactory { + private static final String TAG = AccountManagerFactory.class.getName(); + public static SignalServiceAccountManager createManager(Context context) { return new SignalServiceAccountManager(new SignalServiceNetworkAccess(context).getConfiguration(context), TextSecurePreferences.getLocalNumber(context), @@ -16,7 +25,21 @@ public class AccountManagerFactory { BuildConfig.USER_AGENT); } - public static SignalServiceAccountManager createManager(Context context, String number, String password) { + public static SignalServiceAccountManager createManager(final Context context, String number, String password) { + if (new SignalServiceNetworkAccess(context).isCensored(number)) { + new AsyncTask() { + @Override + protected Void doInBackground(Void... params) { + try { + ProviderInstaller.installIfNeeded(context); + } catch (Throwable t) { + Log.w(TAG, t); + } + return null; + } + }.execute(); + } + return new SignalServiceAccountManager(new SignalServiceNetworkAccess(context).getConfiguration(number), number, password, BuildConfig.USER_AGENT); } diff --git a/src/org/thoughtcrime/securesms/push/SignalServiceNetworkAccess.java b/src/org/thoughtcrime/securesms/push/SignalServiceNetworkAccess.java index 33d3d55e35..aed8369507 100644 --- a/src/org/thoughtcrime/securesms/push/SignalServiceNetworkAccess.java +++ b/src/org/thoughtcrime/securesms/push/SignalServiceNetworkAccess.java @@ -2,6 +2,8 @@ package org.thoughtcrime.securesms.push; import android.content.Context; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import org.thoughtcrime.securesms.BuildConfig; import org.thoughtcrime.securesms.util.TextSecurePreferences; @@ -11,41 +13,118 @@ import org.whispersystems.signalservice.internal.push.SignalServiceUrl; import java.util.HashMap; import java.util.Map; +import okhttp3.CipherSuite; +import okhttp3.ConnectionSpec; +import okhttp3.TlsVersion; + public class SignalServiceNetworkAccess { private static final String TAG = SignalServiceNetworkAccess.class.getName(); private static final String APPSPOT_REFLECTOR_HOST = "signal-reflector-meek.appspot.com"; + private static final ConnectionSpec GMAPS_CONNECTION_SPEC = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) + .tlsVersions(TlsVersion.TLS_1_2) + .cipherSuites(CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, + CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384, + CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA) + .supportsTlsExtensions(true) + .build(); + + private static final ConnectionSpec GMAIL_CONNECTION_SPEC = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) + .tlsVersions(TlsVersion.TLS_1_2) + .cipherSuites(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, + CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, + CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA, + CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384, + CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA, + CipherSuite.TLS_RSA_WITH_RC4_128_SHA, + CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV) + .supportsTlsExtensions(true) + .build(); + + private static final ConnectionSpec PLAY_CONNECTION_SPEC = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) + .tlsVersions(TlsVersion.TLS_1_2) + .cipherSuites(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, + CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA, + CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA, + CipherSuite.TLS_RSA_WITH_RC4_128_SHA, + CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV) + .supportsTlsExtensions(true) + .build(); + + private final Map censorshipConfiguration; private final String[] censoredCountries; private final SignalServiceUrl[] uncensoredConfiguration; public SignalServiceNetworkAccess(Context context) { final TrustStore googleTrustStore = new GoogleFrontingTrustStore(context); - final SignalServiceUrl baseGoogle = new SignalServiceUrl("https://www.google.com", APPSPOT_REFLECTOR_HOST, googleTrustStore); - final SignalServiceUrl baseAndroid = new SignalServiceUrl("https://android.clients.google.com", APPSPOT_REFLECTOR_HOST, googleTrustStore); + final SignalServiceUrl baseGoogle = new SignalServiceUrl("https://www.google.com", APPSPOT_REFLECTOR_HOST, googleTrustStore, GMAIL_CONNECTION_SPEC); + final SignalServiceUrl baseAndroid = new SignalServiceUrl("https://android.clients.google.com", APPSPOT_REFLECTOR_HOST, googleTrustStore, PLAY_CONNECTION_SPEC); + final SignalServiceUrl mapsOneAndroid = new SignalServiceUrl("https://clients3.google.com", APPSPOT_REFLECTOR_HOST, googleTrustStore, GMAPS_CONNECTION_SPEC); + final SignalServiceUrl mapsTwoAndroid = new SignalServiceUrl("https://clients4.google.com", APPSPOT_REFLECTOR_HOST, googleTrustStore, GMAPS_CONNECTION_SPEC); + final SignalServiceUrl mailAndroid = new SignalServiceUrl("https://mail.google.com", APPSPOT_REFLECTOR_HOST, googleTrustStore, GMAIL_CONNECTION_SPEC); this.censorshipConfiguration = new HashMap() {{ put("+20", new SignalServiceUrl[] {new SignalServiceUrl("https://www.google.com.eg", APPSPOT_REFLECTOR_HOST, - googleTrustStore), - baseAndroid}); + googleTrustStore, GMAIL_CONNECTION_SPEC), + baseAndroid, mapsOneAndroid, mapsTwoAndroid, mailAndroid}); put("+971", new SignalServiceUrl[] {new SignalServiceUrl("https://www.google.ae", APPSPOT_REFLECTOR_HOST, - googleTrustStore), - baseAndroid, baseGoogle}); + googleTrustStore, GMAIL_CONNECTION_SPEC), + baseAndroid, baseGoogle, mapsOneAndroid, mapsTwoAndroid, mailAndroid}); put("+53", new SignalServiceUrl[] {new SignalServiceUrl("https://www.google.com.cu", APPSPOT_REFLECTOR_HOST, - googleTrustStore), - baseAndroid, baseGoogle}); + googleTrustStore, GMAIL_CONNECTION_SPEC), + baseAndroid, baseGoogle, mapsOneAndroid, mapsTwoAndroid, mailAndroid}); put("+968", new SignalServiceUrl[] {new SignalServiceUrl("https://www.google.com.om", APPSPOT_REFLECTOR_HOST, - googleTrustStore), - baseAndroid, baseGoogle}); + googleTrustStore, GMAIL_CONNECTION_SPEC), + baseAndroid, baseGoogle, mapsOneAndroid, mapsTwoAndroid, mailAndroid}); }}; this.uncensoredConfiguration = new SignalServiceUrl[] { @@ -60,7 +139,9 @@ public class SignalServiceNetworkAccess { return getConfiguration(localNumber); } - public SignalServiceUrl[] getConfiguration(String localNumber) { + public SignalServiceUrl[] getConfiguration(@Nullable String localNumber) { + if (localNumber == null) return this.uncensoredConfiguration; + for (String censoredRegion : this.censoredCountries) { if (localNumber.startsWith(censoredRegion)) { return this.censorshipConfiguration.get(censoredRegion); @@ -74,4 +155,8 @@ public class SignalServiceNetworkAccess { return getConfiguration(context) != this.uncensoredConfiguration; } + public boolean isCensored(String number) { + return getConfiguration(number) != this.uncensoredConfiguration; + } + }