summaryrefslogtreecommitdiff
path: root/app/src/main/java
diff options
context:
space:
mode:
authorcyBerta <cyberta@riseup.net>2017-10-25 15:55:49 +0200
committercyBerta <cyberta@riseup.net>2017-10-25 15:55:49 +0200
commitc37149dec7dbc2ff2bccfa643792080c3c86ce18 (patch)
treef9e9c46c20448668639239e43bdbb9c88f198663 /app/src/main/java
parent0b146ea7ce6b628c7ff56a20318e7495960dfb7c (diff)
8757 fixes session cookie handling by implementing okHttpClient and custom cookiejar, enables TLS 1.2 on old devices, restricts allowed cipher suites on new devices in order to harden tls based communication
Diffstat (limited to 'app/src/main/java')
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/BitmaskApp.java17
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/Dashboard.java51
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/SrpCredentials.java26
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/SrpRegistrationData.java42
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/TLSCompatSocketFactory.java133
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/userstatus/SessionDialog.java15
6 files changed, 261 insertions, 23 deletions
diff --git a/app/src/main/java/se/leap/bitmaskclient/BitmaskApp.java b/app/src/main/java/se/leap/bitmaskclient/BitmaskApp.java
new file mode 100644
index 00000000..953a559d
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/BitmaskApp.java
@@ -0,0 +1,17 @@
+package se.leap.bitmaskclient;
+
+import android.app.Application;
+
+/**
+ * Created by cyberta on 24.10.17.
+ */
+
+public class BitmaskApp extends Application {
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ PRNGFixes.apply();
+ //TODO: add LeakCanary!
+ }
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/Dashboard.java b/app/src/main/java/se/leap/bitmaskclient/Dashboard.java
index a6a3717b..9fc7d593 100644
--- a/app/src/main/java/se/leap/bitmaskclient/Dashboard.java
+++ b/app/src/main/java/se/leap/bitmaskclient/Dashboard.java
@@ -32,24 +32,36 @@
*/
package se.leap.bitmaskclient;
-import android.annotation.*;
-import android.app.*;
-import android.content.*;
-import android.content.pm.PackageManager.*;
-import android.os.*;
-import android.util.*;
-import android.view.*;
-import android.widget.*;
-
-import org.jetbrains.annotations.*;
-import org.json.*;
-
-import java.net.*;
-
-import butterknife.*;
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.FragmentTransaction;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.TextView;
+
+import org.jetbrains.annotations.NotNull;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import butterknife.ButterKnife;
+import butterknife.InjectView;
import de.blinkt.openvpn.core.VpnStatus;
-import se.leap.bitmaskclient.eip.*;
-import se.leap.bitmaskclient.userstatus.*;
+import se.leap.bitmaskclient.eip.Constants;
+import se.leap.bitmaskclient.userstatus.SessionDialog;
+import se.leap.bitmaskclient.userstatus.User;
+import se.leap.bitmaskclient.userstatus.UserStatusFragment;
/**
* The main user facing Activity of Bitmask Android, consisting of status, controls,
@@ -99,7 +111,6 @@ public class Dashboard extends Activity implements ProviderAPIResultReceiver.Rec
if (app == null) {
app = this;
- PRNGFixes.apply();
VpnStatus.initLogCache(getApplicationContext().getCacheDir());
handleVersion();
User.init(getString(R.string.default_username));
@@ -352,7 +363,9 @@ public class Dashboard extends Activity implements ProviderAPIResultReceiver.Rec
@Override
public void onReceiveResult(int resultCode, Bundle resultData) {
- if (resultCode == ProviderAPI.SUCCESSFUL_SIGNUP) {
+ if (resultCode == ProviderAPI.INITIALIZATION_ERROR) {
+ sessionDialog(resultData);
+ } else if (resultCode == ProviderAPI.SUCCESSFUL_SIGNUP) {
String username = resultData.getString(SessionDialog.USERNAME);
String password = resultData.getString(SessionDialog.PASSWORD);
user_status_fragment.logIn(username, password);
diff --git a/app/src/main/java/se/leap/bitmaskclient/SrpCredentials.java b/app/src/main/java/se/leap/bitmaskclient/SrpCredentials.java
new file mode 100644
index 00000000..c1815ac5
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/SrpCredentials.java
@@ -0,0 +1,26 @@
+package se.leap.bitmaskclient;
+
+import com.google.gson.Gson;
+
+/**
+ * Created by cyberta on 23.10.17.
+ */
+
+public class SrpCredentials {
+
+ /**
+ * Parameter A of SRP authentication
+ */
+ private String A;
+ private String login;
+
+ public SrpCredentials(String username, String A) {
+ this.login = username;
+ this.A = A;
+ }
+
+ @Override
+ public String toString() {
+ return new Gson().toJson(this);
+ }
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/SrpRegistrationData.java b/app/src/main/java/se/leap/bitmaskclient/SrpRegistrationData.java
new file mode 100644
index 00000000..d4e00308
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/SrpRegistrationData.java
@@ -0,0 +1,42 @@
+package se.leap.bitmaskclient;
+
+import com.google.gson.Gson;
+
+/**
+ * Created by cyberta on 23.10.17.
+ */
+
+public class SrpRegistrationData {
+
+
+ private User user;
+
+ public SrpRegistrationData(String username, String passwordSalt, String passwordVerifier) {
+ user = new User(username, passwordSalt, passwordVerifier);
+ }
+
+
+ @Override
+ public String toString() {
+ return new Gson().toJson(this);
+ }
+
+
+ public class User {
+
+ String login;
+ String password_salt;
+ String password_verifier;
+
+ public User(String login, String password_salt, String password_verifier) {
+ this.login = login;
+ this.password_salt = password_salt;
+ this.password_verifier = password_verifier;
+ }
+
+ @Override
+ public String toString() {
+ return new Gson().toJson(this);
+ }
+ }
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/TLSCompatSocketFactory.java b/app/src/main/java/se/leap/bitmaskclient/TLSCompatSocketFactory.java
new file mode 100644
index 00000000..fdad6ba9
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/TLSCompatSocketFactory.java
@@ -0,0 +1,133 @@
+package se.leap.bitmaskclient;
+
+import android.util.Log;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.cert.CertificateException;
+import java.util.Arrays;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+
+import okhttp3.OkHttpClient;
+
+/**
+ * Created by cyberta on 24.10.17.
+ * This class ensures that modern TLS algorithms will also be used on old devices (Android 4.1 - Android 4.4.4) in order to avoid
+ * attacks like POODLE.
+ */
+
+public class TLSCompatSocketFactory extends SSLSocketFactory {
+
+ private static final String TAG = TLSCompatSocketFactory.class.getName();
+ private SSLSocketFactory internalSSLSocketFactory;
+ private SSLContext sslContext;
+ private TrustManager trustManager;
+
+ public TLSCompatSocketFactory(String trustedCaCert) throws KeyManagementException, NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException, NoSuchProviderException {
+
+ initTrustManager(trustedCaCert);
+ internalSSLSocketFactory = sslContext.getSocketFactory();
+
+ }
+
+ public void initSSLSocketFactory(OkHttpClient.Builder builder) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, KeyManagementException, IllegalStateException {
+ builder.sslSocketFactory(this, (X509TrustManager) trustManager);
+ }
+
+
+ private void initTrustManager(String trustedCaCert) throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException, IllegalStateException, KeyManagementException, NoSuchProviderException {
+ java.security.cert.Certificate provider_certificate = ConfigHelper.parseX509CertificateFromString(trustedCaCert);
+
+ // Create a KeyStore containing our trusted CAs
+ String defaultType = KeyStore.getDefaultType();
+ KeyStore keyStore = KeyStore.getInstance(defaultType);
+ keyStore.load(null, null);
+ keyStore.setCertificateEntry("provider_ca_certificate", provider_certificate);
+
+ // Create a TrustManager that trusts the CAs in our KeyStore
+ String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
+ tmf.init(keyStore);
+
+ // Check if there's only 1 X509Trustmanager -> from okttp3 source code example
+ TrustManager[] trustManagers = tmf.getTrustManagers();
+ if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
+ throw new IllegalStateException("Unexpected default trust managers:"
+ + Arrays.toString(trustManagers));
+ }
+
+ trustManager = trustManagers[0];
+
+ // Create an SSLContext that uses our TrustManager
+ sslContext = SSLContext.getInstance("TLS");
+ sslContext.init(null, tmf.getTrustManagers(), null);
+
+ }
+
+
+ @Override
+ public String[] getDefaultCipherSuites() {
+ return internalSSLSocketFactory.getDefaultCipherSuites();
+ }
+
+ @Override
+ public String[] getSupportedCipherSuites() {
+ return internalSSLSocketFactory.getSupportedCipherSuites();
+ }
+
+ @Override
+ public Socket createSocket() throws IOException {
+ return enableTLSOnSocket(internalSSLSocketFactory.createSocket());
+ }
+
+ @Override
+ public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
+ return enableTLSOnSocket(internalSSLSocketFactory.createSocket(s, host, port, autoClose));
+ }
+
+ @Override
+ public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
+ return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port));
+ }
+
+ @Override
+ public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
+ return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port, localHost, localPort));
+ }
+
+ @Override
+ public Socket createSocket(InetAddress host, int port) throws IOException {
+ return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port));
+ }
+
+ @Override
+ public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
+ return enableTLSOnSocket(internalSSLSocketFactory.createSocket(address, port, localAddress, localPort));
+ }
+
+ private Socket enableTLSOnSocket(Socket socket) {
+ if(socket != null && (socket instanceof SSLSocket)) {
+ ((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.2"});
+ ((SSLSocket)socket).setEnabledCipherSuites(getSupportedCipherSuites());
+ }
+ return socket;
+
+
+ }
+
+
+
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/userstatus/SessionDialog.java b/app/src/main/java/se/leap/bitmaskclient/userstatus/SessionDialog.java
index 7dbbe059..88dec39b 100644
--- a/app/src/main/java/se/leap/bitmaskclient/userstatus/SessionDialog.java
+++ b/app/src/main/java/se/leap/bitmaskclient/userstatus/SessionDialog.java
@@ -23,10 +23,13 @@ import android.view.*;
import android.widget.*;
import butterknife.*;
+import se.leap.bitmaskclient.ProviderAPI;
import se.leap.bitmaskclient.VpnFragment;
import se.leap.bitmaskclient.Provider;
import se.leap.bitmaskclient.R;
+import static android.view.View.VISIBLE;
+
/**
* Implements the log in dialog, currently without progress dialog.
* <p/>
@@ -47,7 +50,8 @@ public class SessionDialog extends DialogFragment {
public static enum ERRORS {
USERNAME_MISSING,
PASSWORD_INVALID_LENGTH,
- RISEUP_WARNING
+ RISEUP_WARNING,
+ INITIALIZATION_ERROR
}
@InjectView(R.id.user_message)
@@ -117,8 +121,11 @@ public class SessionDialog extends DialogFragment {
if (arguments.containsKey(ERRORS.PASSWORD_INVALID_LENGTH.toString()))
password_field.setError(getString(R.string.error_not_valid_password_user_message));
else if (arguments.containsKey(ERRORS.RISEUP_WARNING.toString())) {
- user_message.setVisibility(TextView.VISIBLE);
+ user_message.setVisibility(VISIBLE);
user_message.setText(R.string.login_riseup_warning);
+ } else if (arguments.containsKey(ERRORS.INITIALIZATION_ERROR.toString())) {
+ user_message.setVisibility(VISIBLE);
+ user_message.setText(String.valueOf(arguments.get(ERRORS.INITIALIZATION_ERROR.toString())));
}
if (arguments.containsKey(USERNAME)) {
String username = arguments.getString(USERNAME);
@@ -129,8 +136,8 @@ public class SessionDialog extends DialogFragment {
}
if (arguments.containsKey(getString(R.string.user_message))) {
user_message.setText(arguments.getString(getString(R.string.user_message)));
- user_message.setVisibility(View.VISIBLE);
- } else if (user_message.getVisibility() != TextView.VISIBLE)
+ user_message.setVisibility(VISIBLE);
+ } else if (user_message.getVisibility() != VISIBLE)
user_message.setVisibility(View.GONE);
if (!username_field.getText().toString().isEmpty() && password_field.isFocusable())