diff --git a/CHANGELOG.md b/CHANGELOG.md index e5b668f57..f67c07974 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ + +* add simple "Use Tor" preference + +* Enable TLS v1.2 for officials repo on all devices that support it + ### 0.98.1 (2016-02-14) * Fix crash when entering only a space into the search dialog diff --git a/F-Droid/build.gradle b/F-Droid/build.gradle index 1ad545715..92cabd569 100644 --- a/F-Droid/build.gradle +++ b/F-Droid/build.gradle @@ -17,6 +17,7 @@ dependencies { compile 'com.google.zxing:core:3.2.1' compile 'eu.chainfire:libsuperuser:1.0.0.201602011018' compile 'cc.mvdan.accesspoint:library:0.1.3' + compile 'info.guardianproject.netcipher:netcipher:1.2.1' compile 'commons-net:commons-net:3.4' compile 'org.openhab.jmdns:jmdns:3.4.2' compile('ch.acra:acra:4.8.2') { @@ -71,6 +72,7 @@ if (!hasProperty('sourceDeps')) { 'eu.chainfire:libsuperuser:952c5fc82f9c31d31d2b6a7054ee267dac1685fb037a254888c73c48de661eaf', 'cc.mvdan.accesspoint:library:dc89a085d6bc40381078b8dd7776b12bde0dbaf8ffbcddb17ec4ebc3edecc7ba', 'commons-net:commons-net:38cf2eca826b8bcdb236fc1f2e79e0c6dd8e7e0f5c44a3b8e839a1065b2fbe2e', + 'info.guardianproject.netcipher:netcipher:611ec5bde9d799fd57e1efec5c375f9f460de2cdda98918541decc9a7d02f2ad', 'org.openhab.jmdns:jmdns:7a4b34b5606bbd2aff7fdfe629edcb0416fccd367fb59a099f210b9aba4f0bce', 'com.madgag.spongycastle:pkix:6aba9b2210907a3d46dd3dcac782bb3424185290468d102d5207ebdc9796a905', 'com.madgag.spongycastle:prov:029f26cd6b67c06ffa05702d426d472c141789001bcb15b7262ed86c868e5643', diff --git a/F-Droid/proguard-rules.pro b/F-Droid/proguard-rules.pro index 4784add4a..65c2e1e09 100644 --- a/F-Droid/proguard-rules.pro +++ b/F-Droid/proguard-rules.pro @@ -10,6 +10,10 @@ -dontnote android.support.** -dontnote **ILicensingService +# StrongHttpsClient and its support classes are totally unused, so the +# ch.boye.httpclientandroidlib.** classes are also unneeded +-dontwarn info.guardianproject.netcipher.client.** + # These libraries are known to break if minification is enabled on them. They # use reflection to instantiate classes, for example. If the keep flags are # removed, proguard will strip classes which are required, which may result in diff --git a/F-Droid/res/values/strings.xml b/F-Droid/res/values/strings.xml index bd55734cb..e60b30152 100644 --- a/F-Droid/res/values/strings.xml +++ b/F-Droid/res/values/strings.xml @@ -166,6 +166,8 @@ Next Skip + Use Tor + Force download traffic through Tor for increased privacy Proxy Enable HTTP Proxy Configure HTTP Proxy for all network requests diff --git a/F-Droid/res/xml/preferences.xml b/F-Droid/res/xml/preferences.xml index 165b05864..43cb8b184 100644 --- a/F-Droid/res/xml/preferences.xml +++ b/F-Droid/res/xml/preferences.xml @@ -48,6 +48,10 @@ android:title="@string/local_repo_name" /> + () { @Override - public void run() { - SwapType.this.start(); + protected Void doInBackground(Void... params) { + start(); + return null; } - }.start(); + }.execute(); } private void ensureRunning() { @@ -85,21 +87,23 @@ public abstract class SwapType { } public void ensureRunningInBackground() { - new Thread() { + new AsyncTask() { @Override - public void run() { + protected Void doInBackground(Void... params) { ensureRunning(); + return null; } - }.start(); + }.execute(); } public void stopInBackground() { - new Thread() { + new AsyncTask() { @Override - public void run() { - SwapType.this.stop(); + protected Void doInBackground(Void... params) { + stop(); + return null; } - }.start(); + }.execute(); } } diff --git a/F-Droid/src/org/fdroid/fdroid/net/DownloaderFactory.java b/F-Droid/src/org/fdroid/fdroid/net/DownloaderFactory.java index b43227008..fa012d59f 100644 --- a/F-Droid/src/org/fdroid/fdroid/net/DownloaderFactory.java +++ b/F-Droid/src/org/fdroid/fdroid/net/DownloaderFactory.java @@ -55,9 +55,6 @@ public class DownloaderFactory { String macAddress = url.getHost().replace("-", ":"); return new BluetoothDownloader(context, macAddress, url, destFile); } - if (isOnionAddress(url)) { - return new TorHttpDownloader(context, url, destFile); - } if (isLocalFile(url)) { return new LocalFileDownloader(context, url, destFile); } diff --git a/F-Droid/src/org/fdroid/fdroid/net/HttpDownloader.java b/F-Droid/src/org/fdroid/fdroid/net/HttpDownloader.java index 9cfa0310c..aaaddea13 100644 --- a/F-Droid/src/org/fdroid/fdroid/net/HttpDownloader.java +++ b/F-Droid/src/org/fdroid/fdroid/net/HttpDownloader.java @@ -1,6 +1,7 @@ package org.fdroid.fdroid.net; import android.content.Context; +import android.text.TextUtils; import android.util.Log; import com.nostra13.universalimageloader.core.download.BaseImageDownloader; @@ -22,8 +23,11 @@ import java.net.Proxy; import java.net.SocketAddress; import java.net.URL; +import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLHandshakeException; +import info.guardianproject.netcipher.NetCipher; + public class HttpDownloader extends Downloader { private static final String TAG = "HttpDownloader"; @@ -32,7 +36,7 @@ public class HttpDownloader extends Downloader { protected HttpURLConnection connection; private Credentials credentials; - private int statusCode = -1; + private int statusCode = -1; HttpDownloader(Context context, URL url, File destFile) throws FileNotFoundException, MalformedURLException { @@ -52,6 +56,7 @@ public class HttpDownloader extends Downloader { * checking out that follows redirects up to a certain point. I guess though the correct way * is probably to check for a loop (keep a list of all URLs redirected to and if you hit the * same one twice, bail with an exception). + * * @throws IOException */ @Override @@ -92,17 +97,29 @@ public class HttpDownloader extends Downloader { if (connection != null) { return; } - Preferences prefs = Preferences.get(); - if (prefs.isProxyEnabled() && !isSwapUrl()) { - SocketAddress sa = new InetSocketAddress(prefs.getProxyHost(), prefs.getProxyPort()); - Proxy proxy = new Proxy(Proxy.Type.HTTP, sa); - connection = (HttpURLConnection) sourceUrl.openConnection(proxy); - } else { - + if (isSwapUrl()) { + // swap never works with a proxy, its unrouted IP on the same subnet connection = (HttpURLConnection) sourceUrl.openConnection(); - if (credentials != null) { - credentials.authenticate(connection); + } else { + Preferences prefs = Preferences.get(); + if (prefs.isProxyEnabled()) { + // if "Use Tor" is set, NetCipher will ignore these proxy settings + SocketAddress sa = new InetSocketAddress(prefs.getProxyHost(), prefs.getProxyPort()); + NetCipher.setProxy(new Proxy(Proxy.Type.HTTP, sa)); } + connection = NetCipher.getHttpURLConnection(sourceUrl); + } + + // workaround until NetCipher supports HTTPS SNI + // https://gitlab.com/fdroid/fdroidclient/issues/431 + if (connection instanceof HttpsURLConnection + && !TextUtils.equals(sourceUrl.getHost(), "f-droid.org") + && !TextUtils.equals(sourceUrl.getHost(), "guardianproject.info")) { + ((HttpsURLConnection) connection).setSSLSocketFactory(HttpsURLConnection.getDefaultSSLSocketFactory()); + } + + if (credentials != null) { + credentials.authenticate(connection); } } @@ -159,4 +176,4 @@ public class HttpDownloader extends Downloader { public void close() { connection.disconnect(); } -} \ No newline at end of file +} diff --git a/F-Droid/src/org/fdroid/fdroid/net/TorHttpDownloader.java b/F-Droid/src/org/fdroid/fdroid/net/TorHttpDownloader.java deleted file mode 100644 index 50bb50fa5..000000000 --- a/F-Droid/src/org/fdroid/fdroid/net/TorHttpDownloader.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.fdroid.fdroid.net; - -import android.content.Context; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.net.HttpURLConnection; -import java.net.InetSocketAddress; -import java.net.MalformedURLException; -import java.net.Proxy; -import java.net.SocketAddress; -import java.net.URL; - -public class TorHttpDownloader extends HttpDownloader { - - TorHttpDownloader(Context context, URL url, File destFile) - throws FileNotFoundException, MalformedURLException { - super(context, url, destFile); - } - - @Override - protected void setupConnection() throws IOException { - SocketAddress sa = new InetSocketAddress("127.0.0.1", 8118); - Proxy tor = new Proxy(Proxy.Type.HTTP, sa); - connection = (HttpURLConnection) sourceUrl.openConnection(tor); - } -} diff --git a/F-Droid/src/org/fdroid/fdroid/net/WifiStateChangeService.java b/F-Droid/src/org/fdroid/fdroid/net/WifiStateChangeService.java index ccf162460..8c3e3ef69 100644 --- a/F-Droid/src/org/fdroid/fdroid/net/WifiStateChangeService.java +++ b/F-Droid/src/org/fdroid/fdroid/net/WifiStateChangeService.java @@ -87,12 +87,11 @@ public class WifiStateChangeService extends Service { wifiInfo = wifiManager.getConnectionInfo(); FDroidApp.ipAddressString = formatIpAddress(wifiInfo.getIpAddress()); DhcpInfo dhcpInfo = wifiManager.getDhcpInfo(); - if (dhcpInfo == null) { - return null; - } - String netmask = formatIpAddress(dhcpInfo.netmask); - if (!TextUtils.isEmpty(FDroidApp.ipAddressString) && netmask != null) { - FDroidApp.subnetInfo = new SubnetUtils(FDroidApp.ipAddressString, netmask).getInfo(); + if (dhcpInfo != null) { + String netmask = formatIpAddress(dhcpInfo.netmask); + if (!TextUtils.isEmpty(FDroidApp.ipAddressString) && netmask != null) { + FDroidApp.subnetInfo = new SubnetUtils(FDroidApp.ipAddressString, netmask).getInfo(); + } } } else if (wifiState == WifiManager.WIFI_STATE_DISABLED || wifiState == WifiManager.WIFI_STATE_DISABLING) { diff --git a/F-Droid/src/org/fdroid/fdroid/net/bluetooth/BluetoothClient.java b/F-Droid/src/org/fdroid/fdroid/net/bluetooth/BluetoothClient.java index 44ec1a0d3..ccb907c8d 100644 --- a/F-Droid/src/org/fdroid/fdroid/net/bluetooth/BluetoothClient.java +++ b/F-Droid/src/org/fdroid/fdroid/net/bluetooth/BluetoothClient.java @@ -1,5 +1,6 @@ package org.fdroid.fdroid.net.bluetooth; +import android.annotation.TargetApi; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothSocket; @@ -21,6 +22,7 @@ public class BluetoothClient { device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(macAddress); } + @TargetApi(10) public BluetoothConnection openConnection() throws IOException { BluetoothSocket socket = null; diff --git a/F-Droid/src/org/fdroid/fdroid/net/bluetooth/BluetoothServer.java b/F-Droid/src/org/fdroid/fdroid/net/bluetooth/BluetoothServer.java index 3c39917f9..848192731 100644 --- a/F-Droid/src/org/fdroid/fdroid/net/bluetooth/BluetoothServer.java +++ b/F-Droid/src/org/fdroid/fdroid/net/bluetooth/BluetoothServer.java @@ -1,5 +1,6 @@ package org.fdroid.fdroid.net.bluetooth; +import android.annotation.TargetApi; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothServerSocket; import android.bluetooth.BluetoothSocket; @@ -61,6 +62,7 @@ public class BluetoothServer extends Thread { } } + @TargetApi(10) @Override public void run() { diff --git a/F-Droid/src/org/fdroid/fdroid/views/ManageReposActivity.java b/F-Droid/src/org/fdroid/fdroid/views/ManageReposActivity.java index b28449847..cc081f817 100644 --- a/F-Droid/src/org/fdroid/fdroid/views/ManageReposActivity.java +++ b/F-Droid/src/org/fdroid/fdroid/views/ManageReposActivity.java @@ -127,6 +127,7 @@ public class ManageReposActivity extends ActionBarActivity { @Override protected void onResume() { super.onResume(); + FDroidApp.checkStartTor(this); /* let's see if someone is trying to send us a new repo */ addRepoFromIntent(getIntent()); diff --git a/F-Droid/src/org/fdroid/fdroid/views/fragments/PreferencesFragment.java b/F-Droid/src/org/fdroid/fdroid/views/fragments/PreferencesFragment.java index 8e15e6e2f..576aaafa7 100644 --- a/F-Droid/src/org/fdroid/fdroid/views/fragments/PreferencesFragment.java +++ b/F-Droid/src/org/fdroid/fdroid/views/fragments/PreferencesFragment.java @@ -21,6 +21,9 @@ import org.fdroid.fdroid.PreferencesActivity; import org.fdroid.fdroid.R; import org.fdroid.fdroid.installer.PrivilegedInstaller; +import info.guardianproject.netcipher.NetCipher; +import info.guardianproject.netcipher.proxy.OrbotHelper; + public class PreferencesFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener { @@ -43,10 +46,16 @@ public class PreferencesFragment extends PreferenceFragment Preferences.PREF_PROXY_PORT, }; + private static final int REQUEST_INSTALL_ORBOT = 0x1234; + private CheckBoxPreference enableProxyCheckPref; + private CheckBoxPreference useTorCheckPref; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.preferences); + useTorCheckPref = (CheckBoxPreference) findPreference(Preferences.PREF_USE_TOR); + enableProxyCheckPref = (CheckBoxPreference) findPreference(Preferences.PREF_ENABLE_PROXY); } private void checkSummary(String key, int resId) { @@ -279,6 +288,29 @@ public class PreferencesFragment extends PreferenceFragment initPrivilegedInstallerPreference(); initManagePrivilegedAppPreference(); + // this pref's default is dynamically set based on whether Orbot is installed + boolean useTor = Preferences.get().isTorEnabled(); + useTorCheckPref.setDefaultValue(useTor); + useTorCheckPref.setChecked(useTor); + useTorCheckPref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object enabled) { + if ((Boolean) enabled) { + final Activity activity = getActivity(); + enableProxyCheckPref.setEnabled(false); + if (OrbotHelper.isOrbotInstalled(activity)) { + NetCipher.useTor(); + } else { + Intent intent = OrbotHelper.getOrbotInstallIntent(activity); + activity.startActivityForResult(intent, REQUEST_INSTALL_ORBOT); + } + } else { + enableProxyCheckPref.setEnabled(true); + NetCipher.clearProxy(); + } + return true; + } + }); } @Override