diff options
Diffstat (limited to 'app/src/main')
9 files changed, 265 insertions, 26 deletions
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 67fd0a1a..d751d9f0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -27,10 +27,11 @@ android:maxSdkVersion="18"/> <uses-sdk - android:minSdkVersion="14" + android:minSdkVersion="16" android:targetSdkVersion="26"/> <application + android:name=".BitmaskApp" android:allowBackup="true" android:icon="@drawable/icon" android:logo="@drawable/icon" 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()) diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 7f0670b8..09bac1ef 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -38,7 +38,7 @@ <string name="error_client_http_user_message">Inténtalo de nuevo: error en el cliente HTTP</string> <string name="error_io_exception_user_message">Inténtalo de nuevo: error de E/S</string> <string name="error_json_exception_user_message">Inténtalo de nuevo: respuesta mal formada del servidor</string> - <string name="error_no_such_algorithm_exception_user_message">Actualiza Bitmask</string> + <string name="error_no_such_algorithm_exception_user_message">Algoritmo de cifrado no encontrado. Por favor actualice su sistema operativo!</string> <string name="signup_or_login_button">Registrarse/Iniciar sesión</string> <string name="login_button">Iniciar sesión</string> <string name="logout_button">Cerrar sesión</string> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 06c80f12..b1fce0ad 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -37,7 +37,7 @@ <string name="error_client_http_user_message">Try again: Client HTTP error</string> <string name="error_io_exception_user_message">Try again: I/O error</string> <string name="error_json_exception_user_message">Try again: Bad response from the server</string> - <string name="error_no_such_algorithm_exception_user_message">Update the app</string> + <string name="error_no_such_algorithm_exception_user_message">Encryption algorithm not found. Please update your OS!</string> <string name="signup_or_login_button">Sign Up/Log In</string> <string name="login_button">Log In</string> <string name="logout_button">Log Out</string> |