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