diff options
author | cyBerta <cyberta@riseup.net> | 2018-01-09 20:55:10 +0100 |
---|---|---|
committer | cyBerta <cyberta@riseup.net> | 2018-01-09 20:55:10 +0100 |
commit | 0b647ea5e7ff67747080b2ffcebc948da0fbecb5 (patch) | |
tree | e01b59b626aded786cca8d25f7749c52b9150194 /app/src/main/java | |
parent | 81a732702f7b3125ac543f92d8a5ec33cce972fe (diff) |
8773 refactoring ProviderAPI for testability, setting up basic unit test framework
Diffstat (limited to 'app/src/main/java')
-rw-r--r-- | app/src/main/java/se/leap/bitmaskclient/BaseConfigurationWizard.java | 4 | ||||
-rw-r--r-- | app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java | 60 | ||||
-rw-r--r-- | app/src/main/java/se/leap/bitmaskclient/DownloadFailedDialog.java | 16 | ||||
-rw-r--r-- | app/src/main/java/se/leap/bitmaskclient/OkHttpClientGenerator.java | 166 | ||||
-rw-r--r-- | app/src/main/java/se/leap/bitmaskclient/ProviderAPI.java | 124 | ||||
-rw-r--r-- | app/src/main/java/se/leap/bitmaskclient/ProviderAPIResultReceiver.java | 4 | ||||
-rw-r--r-- | app/src/main/java/se/leap/bitmaskclient/ProviderApiConnector.java | 102 | ||||
-rw-r--r-- | app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java (renamed from app/src/main/java/se/leap/bitmaskclient/ProviderApiBase.java) | 343 | ||||
-rw-r--r-- | app/src/main/java/se/leap/bitmaskclient/userstatus/SessionDialog.java | 2 |
9 files changed, 578 insertions, 243 deletions
diff --git a/app/src/main/java/se/leap/bitmaskclient/BaseConfigurationWizard.java b/app/src/main/java/se/leap/bitmaskclient/BaseConfigurationWizard.java index 2c169e3d..c26184bb 100644 --- a/app/src/main/java/se/leap/bitmaskclient/BaseConfigurationWizard.java +++ b/app/src/main/java/se/leap/bitmaskclient/BaseConfigurationWizard.java @@ -56,7 +56,7 @@ import se.leap.bitmaskclient.userstatus.SessionDialog; import static android.view.View.GONE; import static android.view.View.INVISIBLE; import static android.view.View.VISIBLE; -import static se.leap.bitmaskclient.ProviderApiBase.ERRORS; +import static se.leap.bitmaskclient.ProviderAPI.ERRORS; /** * abstract base Activity that builds and shows the list of known available providers. @@ -352,7 +352,7 @@ public abstract class BaseConfigurationWizard extends Activity } /** - * Asks ProviderAPI to download an anonymous (anon) VPN certificate. + * Asks ProviderApiService to download an anonymous (anon) VPN certificate. */ private void downloadVpnCertificate() { Intent provider_API_command = new Intent(this, ProviderAPI.class); diff --git a/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java b/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java index ed527a54..54bcc1f4 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java +++ b/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java @@ -16,16 +16,33 @@ */ package se.leap.bitmaskclient; -import android.util.*; - -import org.json.*; - -import java.io.*; -import java.math.*; -import java.security.*; -import java.security.cert.*; -import java.security.interfaces.*; -import java.security.spec.*; +import android.support.annotation.NonNull; +import android.util.Log; + +import org.json.JSONException; +import org.json.JSONObject; +import org.spongycastle.util.encoders.Base64; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.math.BigInteger; +import java.security.KeyFactory; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.security.interfaces.RSAPrivateKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; import static android.R.attr.name; @@ -82,7 +99,7 @@ public class ConfigHelper { cf = CertificateFactory.getInstance("X.509"); certificate_string = certificate_string.replaceFirst("-----BEGIN CERTIFICATE-----", "").replaceFirst("-----END CERTIFICATE-----", "").trim(); - byte[] cert_bytes = Base64.decode(certificate_string, Base64.DEFAULT); + byte[] cert_bytes = Base64.decode(certificate_string); InputStream caInput = new ByteArrayInputStream(cert_bytes); try { certificate = cf.generateCertificate(caInput); @@ -90,15 +107,9 @@ public class ConfigHelper { } finally { caInput.close(); } - } catch (CertificateException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (IOException e) { - return null; - } catch (IllegalArgumentException e) { + } catch (NullPointerException | CertificateException | IOException | IllegalArgumentException e) { return null; } - return (X509Certificate) certificate; } @@ -139,7 +150,7 @@ public class ConfigHelper { try { KeyFactory kf = KeyFactory.getInstance("RSA", "BC"); rsaKeyString = rsaKeyString.replaceFirst("-----BEGIN RSA PRIVATE KEY-----", "").replaceFirst("-----END RSA PRIVATE KEY-----", ""); - PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.decode(rsaKeyString, Base64.DEFAULT)); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.decode(rsaKeyString)); key = (RSAPrivateKey) kf.generatePrivate(keySpec); } catch (InvalidKeySpecException e) { // TODO Auto-generated catch block @@ -162,7 +173,7 @@ public class ConfigHelper { } public static String base64toHex(String base64_input) { - byte[] byteArray = Base64.decode(base64_input, Base64.DEFAULT); + byte[] byteArray = Base64.decode(base64_input); int readBytes = byteArray.length; StringBuffer hexData = new StringBuffer(); int onebyte; @@ -172,6 +183,15 @@ public class ConfigHelper { } return hexData.toString(); } + + @NonNull + public static String getFingerprintFromCertificate(X509Certificate certificate, String encoding) throws NoSuchAlgorithmException, CertificateEncodingException /*, UnsupportedEncodingException*/ { + return base64toHex( + //new String(Base64.encode(MessageDigest.getInstance(encoding).digest(certificate.getEncoded())), "US-ASCII")); + android.util.Base64.encodeToString( + MessageDigest.getInstance(encoding).digest(certificate.getEncoded()), + android.util.Base64.DEFAULT)); + } /** * Adds a new X509 certificate given its input stream and its provider name diff --git a/app/src/main/java/se/leap/bitmaskclient/DownloadFailedDialog.java b/app/src/main/java/se/leap/bitmaskclient/DownloadFailedDialog.java index 6f6a14de..9d3f4b52 100644 --- a/app/src/main/java/se/leap/bitmaskclient/DownloadFailedDialog.java +++ b/app/src/main/java/se/leap/bitmaskclient/DownloadFailedDialog.java @@ -16,19 +16,19 @@ */ package se.leap.bitmaskclient; -import android.app.*; -import android.content.*; -import android.os.*; +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.content.DialogInterface; +import android.os.Bundle; -import org.json.JSONException; import org.json.JSONObject; -import se.leap.bitmaskclient.userstatus.SessionDialog; - import static se.leap.bitmaskclient.DownloadFailedDialog.DOWNLOAD_ERRORS.DEFAULT; import static se.leap.bitmaskclient.DownloadFailedDialog.DOWNLOAD_ERRORS.valueOf; -import static se.leap.bitmaskclient.ProviderApiBase.ERRORID; -import static se.leap.bitmaskclient.ProviderApiBase.ERRORS; +import static se.leap.bitmaskclient.ProviderAPI.ERRORID; +import static se.leap.bitmaskclient.ProviderAPI.ERRORS; /** * Implements a dialog to show why a download failed. diff --git a/app/src/main/java/se/leap/bitmaskclient/OkHttpClientGenerator.java b/app/src/main/java/se/leap/bitmaskclient/OkHttpClientGenerator.java new file mode 100644 index 00000000..1bf679f8 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/OkHttpClientGenerator.java @@ -0,0 +1,166 @@ +/** + * Copyright (c) 2018 LEAP Encryption Access Project and contributers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package se.leap.bitmaskclient; + +import android.content.SharedPreferences; +import android.content.res.Resources; +import android.os.Build; +import android.support.annotation.NonNull; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.IOException; +import java.net.UnknownHostException; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.cert.CertificateException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; + +import okhttp3.CipherSuite; +import okhttp3.ConnectionSpec; +import okhttp3.Cookie; +import okhttp3.CookieJar; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.TlsVersion; + +import static android.text.TextUtils.isEmpty; +import static se.leap.bitmaskclient.ProviderAPI.ERRORS; +import static se.leap.bitmaskclient.R.string.certificate_error; +import static se.leap.bitmaskclient.R.string.error_io_exception_user_message; +import static se.leap.bitmaskclient.R.string.error_no_such_algorithm_exception_user_message; +import static se.leap.bitmaskclient.R.string.keyChainAccessError; +import static se.leap.bitmaskclient.R.string.server_unreachable_message; + +/** + * Created by cyberta on 08.01.18. + */ + +public class OkHttpClientGenerator { + + SharedPreferences preferences; + Resources resources; + + public OkHttpClientGenerator(SharedPreferences preferences, Resources resources) { + this.preferences = preferences; + this.resources = resources; + } + + public OkHttpClient initCommercialCAHttpClient(JSONObject initError) { + return initHttpClient(initError, null); + } + + public OkHttpClient initSelfSignedCAHttpClient(JSONObject initError) { + String certificate = preferences.getString(Provider.CA_CERT, ""); + return initHttpClient(initError, certificate); + } + + public OkHttpClient initSelfSignedCAHttpClient(JSONObject initError, String certificate) { + return initHttpClient(initError, certificate); + } + + + private OkHttpClient initHttpClient(JSONObject initError, String certificate) { + try { + TLSCompatSocketFactory sslCompatFactory; + ConnectionSpec spec = getConnectionSpec(); + OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder(); + + if (!isEmpty(certificate)) { + sslCompatFactory = new TLSCompatSocketFactory(certificate); + } else { + sslCompatFactory = new TLSCompatSocketFactory(); + } + sslCompatFactory.initSSLSocketFactory(clientBuilder); + clientBuilder.cookieJar(getCookieJar()) + .connectionSpecs(Collections.singletonList(spec)); + return clientBuilder.build(); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + addErrorMessageToJson(initError, resources.getString(R.string.certificate_error)); + } catch (IllegalStateException | KeyManagementException | KeyStoreException e) { + e.printStackTrace(); + addErrorMessageToJson(initError, String.format(resources.getString(keyChainAccessError), e.getLocalizedMessage())); + } catch (NoSuchAlgorithmException | NoSuchProviderException e) { + e.printStackTrace(); + addErrorMessageToJson(initError, resources.getString(error_no_such_algorithm_exception_user_message)); + } catch (CertificateException e) { + e.printStackTrace(); + addErrorMessageToJson(initError, resources.getString(certificate_error)); + } catch (UnknownHostException e) { + e.printStackTrace(); + addErrorMessageToJson(initError, resources.getString(server_unreachable_message)); + } catch (IOException e) { + e.printStackTrace(); + addErrorMessageToJson(initError, resources.getString(error_io_exception_user_message)); + } + return null; + } + + + + @NonNull + private ConnectionSpec getConnectionSpec() { + ConnectionSpec.Builder connectionSpecbuilder = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) + .tlsVersions(TlsVersion.TLS_1_2, TlsVersion.TLS_1_3); + //FIXME: restrict connection further to the following recommended cipher suites for ALL supported API levels + //figure out how to use bcjsse for that purpose + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) + connectionSpecbuilder.cipherSuites( + CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 + ); + return connectionSpecbuilder.build(); + } + + @NonNull + private CookieJar getCookieJar() { + return new CookieJar() { + private final HashMap<String, List<Cookie>> cookieStore = new HashMap<>(); + + @Override + public void saveFromResponse(HttpUrl url, List<Cookie> cookies) { + cookieStore.put(url.host(), cookies); + } + + @Override + public List<Cookie> loadForRequest(HttpUrl url) { + List<Cookie> cookies = cookieStore.get(url.host()); + return cookies != null ? cookies : new ArrayList<Cookie>(); + } + }; + } + + private void addErrorMessageToJson(JSONObject jsonObject, String errorMessage) { + try { + jsonObject.put(ERRORS, errorMessage); + } catch (JSONException e) { + e.printStackTrace(); + } + } +} diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderAPI.java b/app/src/main/java/se/leap/bitmaskclient/ProviderAPI.java new file mode 100644 index 00000000..5a6aabc0 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/ProviderAPI.java @@ -0,0 +1,124 @@ +/** + * Copyright (c) 2017 LEAP Encryption Access Project and contributers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package se.leap.bitmaskclient; + +import android.annotation.SuppressLint; +import android.app.IntentService; +import android.content.Intent; +import android.content.SharedPreferences; + +import de.blinkt.openvpn.core.Preferences; + +/** + * Implements HTTP api methods (encapsulated in {{@link ProviderApiManager}}) + * used to manage communications with the provider server. + * <p/> + * It's an IntentService because it downloads data from the Internet, so it operates in the background. + * + * @author parmegv + * @author MeanderingCode + * @author cyberta + */ + +public class ProviderAPI extends IntentService implements ProviderApiManagerBase.ProviderApiServiceCallback { + + final public static String + TAG = ProviderAPI.class.getSimpleName(), + SET_UP_PROVIDER = "setUpProvider", + UPDATE_PROVIDER_DETAILS = "updateProviderDetails", + DOWNLOAD_NEW_PROVIDER_DOTJSON = "downloadNewProviderDotJSON", + SIGN_UP = "srpRegister", + LOG_IN = "srpAuth", + LOG_OUT = "logOut", + DOWNLOAD_CERTIFICATE = "downloadUserAuthedCertificate", + PARAMETERS = "parameters", + RESULT_KEY = "result", + RECEIVER_KEY = "receiver", + ERRORS = "errors", + ERRORID = "errorId", + UPDATE_PROGRESSBAR = "update_progressbar", + CURRENT_PROGRESS = "current_progress", + DOWNLOAD_EIP_SERVICE = TAG + ".DOWNLOAD_EIP_SERVICE"; + + final public static int + SUCCESSFUL_LOGIN = 3, + FAILED_LOGIN = 4, + SUCCESSFUL_SIGNUP = 5, + FAILED_SIGNUP = 6, + SUCCESSFUL_LOGOUT = 7, + LOGOUT_FAILED = 8, + CORRECTLY_DOWNLOADED_CERTIFICATE = 9, + INCORRECTLY_DOWNLOADED_CERTIFICATE = 10, + PROVIDER_OK = 11, + PROVIDER_NOK = 12, + CORRECTLY_DOWNLOADED_EIP_SERVICE = 13, + INCORRECTLY_DOWNLOADED_EIP_SERVICE = 14; + + ProviderApiManager providerApiManager; + + + + public ProviderAPI() { + super(TAG); + } + + //TODO: refactor me, please! + public static void stop() { + ProviderApiManager.stop(); + } + + //TODO: refactor me, please! + public static boolean caCertDownloaded() { + return ProviderApiManager.caCertDownloaded(); + } + + //TODO: refactor me, please! + public static String lastProviderMainUrl() { + return ProviderApiManager.lastProviderMainUrl(); + } + + //TODO: refactor me, please! + //used in insecure flavor only + @SuppressLint("unused") + public static boolean lastDangerOn() { + return ProviderApiManager.lastDangerOn(); + } + + + @Override + public void onCreate() { + super.onCreate(); + providerApiManager = initApiManager(); + } + + @Override + public void broadcastProgress(Intent intent) { + sendBroadcast(intent); + } + + @Override + protected void onHandleIntent(Intent command) { + providerApiManager.handleIntent(command); + } + + + private ProviderApiManager initApiManager() { + SharedPreferences preferences = getSharedPreferences(Constants.SHARED_PREFERENCES, MODE_PRIVATE); + OkHttpClientGenerator clientGenerator = new OkHttpClientGenerator(preferences, getResources()); + return new ProviderApiManager(preferences, getResources(), clientGenerator, this); + } +} diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderAPIResultReceiver.java b/app/src/main/java/se/leap/bitmaskclient/ProviderAPIResultReceiver.java index 9b880f89..9b777e5a 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ProviderAPIResultReceiver.java +++ b/app/src/main/java/se/leap/bitmaskclient/ProviderAPIResultReceiver.java @@ -16,7 +16,9 @@ */
package se.leap.bitmaskclient;
-import android.os.*;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.ResultReceiver;
/**
* Implements the ResultReceiver needed by Activities using ProviderAPI to receive the results of its operations.
diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderApiConnector.java b/app/src/main/java/se/leap/bitmaskclient/ProviderApiConnector.java new file mode 100644 index 00000000..9aad14d5 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/ProviderApiConnector.java @@ -0,0 +1,102 @@ +/** + * Copyright (c) 2018 LEAP Encryption Access Project and contributers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package se.leap.bitmaskclient; + +import android.support.annotation.NonNull; +import android.util.Pair; + +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.Locale; +import java.util.Scanner; + +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; + +/** + * Created by cyberta on 08.01.18. + */ + +public class ProviderApiConnector { + + private static final MediaType JSON + = MediaType.parse("application/json; charset=utf-8"); + + + public static boolean delete(OkHttpClient okHttpClient, String deleteUrl) { + try { + Request.Builder requestBuilder = new Request.Builder() + .url(deleteUrl) + .delete(); + Request request = requestBuilder.build(); + + Response response = okHttpClient.newCall(request).execute(); + if (response.isSuccessful() || response.code() == 401) { + return true; + } + } catch (IOException | RuntimeException e) { + return false; + } + + return false; + } + + public static boolean canConnect(@NonNull OkHttpClient okHttpClient, String url) { + try { + Request.Builder requestBuilder = new Request.Builder() + .url(url) + .method("GET", null); + Request request = requestBuilder.build(); + + Response response = okHttpClient.newCall(request).execute(); + return response.isSuccessful(); + } catch (RuntimeException | IOException e) { + return false; + } + + } + + public static String requestStringFromServer(@NonNull String url, @NonNull String request_method, String jsonString, @NonNull List<Pair<String, String>> headerArgs, @NonNull OkHttpClient okHttpClient) throws RuntimeException, IOException { + + RequestBody jsonBody = jsonString != null ? RequestBody.create(JSON, jsonString) : null; + Request.Builder requestBuilder = new Request.Builder() + .url(url) + .method(request_method, jsonBody); + if (headerArgs != null) { + for (Pair<String, String> keyValPair : headerArgs) { + requestBuilder.addHeader(keyValPair.first, keyValPair.second); + } + } + //TODO: move to getHeaderArgs()? + String locale = Locale.getDefault().getLanguage() + Locale.getDefault().getCountry(); + requestBuilder.addHeader("Accept-Language", locale); + Request request = requestBuilder.build(); + + Response response = okHttpClient.newCall(request).execute(); + InputStream inputStream = response.body().byteStream(); + Scanner scanner = new Scanner(inputStream).useDelimiter("\\A"); + if (scanner.hasNext()) { + return scanner.next(); + } + return null; + } +} diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderApiBase.java b/app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java index 0013d2c2..396d642b 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ProviderApiBase.java +++ b/app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2017 LEAP Encryption Access Project and contributers + * Copyright (c) 2018 LEAP Encryption Access Project and contributers * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -14,13 +14,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + package se.leap.bitmaskclient; -import android.app.IntentService; import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Resources; -import android.os.Build; import android.os.Bundle; import android.os.ResultReceiver; import android.support.annotation.NonNull; @@ -38,11 +37,7 @@ import java.net.MalformedURLException; import java.net.SocketTimeoutException; import java.net.UnknownHostException; import java.net.UnknownServiceException; -import java.security.KeyManagementException; -import java.security.KeyStoreException; -import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.CertificateExpiredException; @@ -50,90 +45,75 @@ import java.security.cert.CertificateNotYetValidException; import java.security.cert.X509Certificate; import java.security.interfaces.RSAPrivateKey; import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Scanner; import javax.net.ssl.SSLHandshakeException; -import okhttp3.CipherSuite; -import okhttp3.ConnectionSpec; -import okhttp3.Cookie; -import okhttp3.CookieJar; -import okhttp3.HttpUrl; import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; -import okhttp3.TlsVersion; import se.leap.bitmaskclient.userstatus.SessionDialog; import se.leap.bitmaskclient.userstatus.User; import se.leap.bitmaskclient.userstatus.UserStatus; -import static android.text.TextUtils.isEmpty; -import static se.leap.bitmaskclient.ConfigHelper.base64toHex; +import static se.leap.bitmaskclient.ConfigHelper.getFingerprintFromCertificate; import static se.leap.bitmaskclient.DownloadFailedDialog.DOWNLOAD_ERRORS.ERROR_CERTIFICATE_PINNING; import static se.leap.bitmaskclient.DownloadFailedDialog.DOWNLOAD_ERRORS.ERROR_CORRUPTED_PROVIDER_JSON; import static se.leap.bitmaskclient.DownloadFailedDialog.DOWNLOAD_ERRORS.ERROR_INVALID_CERTIFICATE; import static se.leap.bitmaskclient.Provider.MAIN_URL; +import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_DOWNLOADED_CERTIFICATE; +import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_DOWNLOADED_EIP_SERVICE; +import static se.leap.bitmaskclient.ProviderAPI.CURRENT_PROGRESS; +import static se.leap.bitmaskclient.ProviderAPI.DOWNLOAD_CERTIFICATE; +import static se.leap.bitmaskclient.ProviderAPI.DOWNLOAD_EIP_SERVICE; +import static se.leap.bitmaskclient.ProviderAPI.ERRORID; +import static se.leap.bitmaskclient.ProviderAPI.ERRORS; +import static se.leap.bitmaskclient.ProviderAPI.FAILED_LOGIN; +import static se.leap.bitmaskclient.ProviderAPI.FAILED_SIGNUP; +import static se.leap.bitmaskclient.ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE; +import static se.leap.bitmaskclient.ProviderAPI.INCORRECTLY_DOWNLOADED_EIP_SERVICE; +import static se.leap.bitmaskclient.ProviderAPI.LOGOUT_FAILED; +import static se.leap.bitmaskclient.ProviderAPI.LOG_IN; +import static se.leap.bitmaskclient.ProviderAPI.LOG_OUT; +import static se.leap.bitmaskclient.ProviderAPI.PARAMETERS; +import static se.leap.bitmaskclient.ProviderAPI.PROVIDER_NOK; +import static se.leap.bitmaskclient.ProviderAPI.PROVIDER_OK; +import static se.leap.bitmaskclient.ProviderAPI.RECEIVER_KEY; +import static se.leap.bitmaskclient.ProviderAPI.RESULT_KEY; +import static se.leap.bitmaskclient.ProviderAPI.SET_UP_PROVIDER; +import static se.leap.bitmaskclient.ProviderAPI.SIGN_UP; +import static se.leap.bitmaskclient.ProviderAPI.SUCCESSFUL_LOGIN; +import static se.leap.bitmaskclient.ProviderAPI.SUCCESSFUL_LOGOUT; +import static se.leap.bitmaskclient.ProviderAPI.SUCCESSFUL_SIGNUP; +import static se.leap.bitmaskclient.ProviderAPI.UPDATE_PROGRESSBAR; +import static se.leap.bitmaskclient.ProviderAPI.UPDATE_PROVIDER_DETAILS; import static se.leap.bitmaskclient.R.string.certificate_error; import static se.leap.bitmaskclient.R.string.error_io_exception_user_message; import static se.leap.bitmaskclient.R.string.error_json_exception_user_message; import static se.leap.bitmaskclient.R.string.error_no_such_algorithm_exception_user_message; -import static se.leap.bitmaskclient.R.string.keyChainAccessError; import static se.leap.bitmaskclient.R.string.malformed_url; +import static se.leap.bitmaskclient.R.string.retry; import static se.leap.bitmaskclient.R.string.server_unreachable_message; import static se.leap.bitmaskclient.R.string.service_is_down_error; /** - * Implements HTTP api methods used to manage communications with the provider server. - * The implemented methods are commonly used by insecure's and production's flavor of ProviderAPI. - * <p/> - * It's an IntentService because it downloads data from the Internet, so it operates in the background. - * - * @author parmegv - * @author MeanderingCode - * @author cyberta + * Implements the logic of the http api calls. The methods of this class needs to be called from + * a background thread. */ -public abstract class ProviderApiBase extends IntentService { - - final public static String - TAG = ProviderAPI.class.getSimpleName(), - SET_UP_PROVIDER = "setUpProvider", - UPDATE_PROVIDER_DETAILS = "updateProviderDetails", - DOWNLOAD_NEW_PROVIDER_DOTJSON = "downloadNewProviderDotJSON", - SIGN_UP = "srpRegister", - LOG_IN = "srpAuth", - LOG_OUT = "logOut", - DOWNLOAD_CERTIFICATE = "downloadUserAuthedCertificate", - PARAMETERS = "parameters", - RESULT_KEY = "result", - RECEIVER_KEY = "receiver", - ERRORS = "errors", - ERRORID = "errorId", - UPDATE_PROGRESSBAR = "update_progressbar", - CURRENT_PROGRESS = "current_progress", - DOWNLOAD_EIP_SERVICE = TAG + ".DOWNLOAD_EIP_SERVICE"; - - final public static int - SUCCESSFUL_LOGIN = 3, - FAILED_LOGIN = 4, - SUCCESSFUL_SIGNUP = 5, - FAILED_SIGNUP = 6, - SUCCESSFUL_LOGOUT = 7, - LOGOUT_FAILED = 8, - CORRECTLY_DOWNLOADED_CERTIFICATE = 9, - INCORRECTLY_DOWNLOADED_CERTIFICATE = 10, - PROVIDER_OK = 11, - PROVIDER_NOK = 12, - CORRECTLY_DOWNLOADED_EIP_SERVICE = 13, - INCORRECTLY_DOWNLOADED_EIP_SERVICE = 14; - - protected static boolean +public abstract class ProviderApiManagerBase { + + public interface ProviderApiServiceCallback { + void broadcastProgress(Intent intent); + } + + private ProviderApiServiceCallback serviceCallback; + + protected static volatile boolean CA_CERT_DOWNLOADED = false, PROVIDER_JSON_DOWNLOADED = false, EIP_SERVICE_JSON_DOWNLOADED = false; @@ -146,32 +126,28 @@ public abstract class ProviderApiBase extends IntentService { protected static String providerCaCert; protected static JSONObject providerDefinition; protected Resources resources; + protected OkHttpClientGenerator clientGenerator; public static void stop() { go_ahead = false; } - private final MediaType JSON - = MediaType.parse("application/json; charset=utf-8"); - - public ProviderApiBase() { - super(TAG); + public static String lastProviderMainUrl() { + return lastProviderMainUrl; } - @Override - public void onCreate() { - super.onCreate(); - preferences = getSharedPreferences(Constants.SHARED_PREFERENCES, MODE_PRIVATE); - resources = getResources(); - } + private final MediaType JSON + = MediaType.parse("application/json; charset=utf-8"); - public static String lastProviderMainUrl() { - return lastProviderMainUrl; + public ProviderApiManagerBase(SharedPreferences preferences, Resources resources, OkHttpClientGenerator clientGenerator, ProviderApiServiceCallback callback) { + this.preferences = preferences; + this.resources = resources; + this.serviceCallback = callback; + this.clientGenerator = clientGenerator; } - @Override - protected void onHandleIntent(Intent command) { + public void handleIntent(Intent command) { final ResultReceiver receiver = command.getParcelableExtra(RECEIVER_KEY); String action = command.getAction(); Bundle parameters = command.getBundleExtra(PARAMETERS); @@ -185,6 +161,7 @@ public abstract class ProviderApiBase extends IntentService { go_ahead = false; } } + if (action.equals(UPDATE_PROVIDER_DETAILS)) { resetProviderDetails(); Bundle task = new Bundle(); @@ -255,7 +232,7 @@ public abstract class ProviderApiBase extends IntentService { } protected String formatErrorMessage(final int toastStringId) { - return formatErrorMessage(getResources().getString(toastStringId)); + return formatErrorMessage(resources.getString(toastStringId)); } private String formatErrorMessage(String errorMessage) { @@ -288,91 +265,7 @@ public abstract class ProviderApiBase extends IntentService { } } - private OkHttpClient initHttpClient(JSONObject initError, String certificate) { - try { - TLSCompatSocketFactory sslCompatFactory; - ConnectionSpec spec = getConnectionSpec(); - OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder(); - if (!isEmpty(certificate)) { - sslCompatFactory = new TLSCompatSocketFactory(certificate); - } else { - sslCompatFactory = new TLSCompatSocketFactory(); - } - sslCompatFactory.initSSLSocketFactory(clientBuilder); - clientBuilder.cookieJar(getCookieJar()) - .connectionSpecs(Collections.singletonList(spec)); - return clientBuilder.build(); - } catch (IllegalArgumentException e) { - e.printStackTrace(); - addErrorMessageToJson(initError, resources.getString(R.string.certificate_error)); - } catch (IllegalStateException | KeyManagementException | KeyStoreException e) { - e.printStackTrace(); - addErrorMessageToJson(initError, String.format(resources.getString(keyChainAccessError), e.getLocalizedMessage())); - } catch (NoSuchAlgorithmException | NoSuchProviderException e) { - e.printStackTrace(); - addErrorMessageToJson(initError, resources.getString(error_no_such_algorithm_exception_user_message)); - } catch (CertificateException e) { - e.printStackTrace(); - addErrorMessageToJson(initError, resources.getString(certificate_error)); - } catch (UnknownHostException e) { - e.printStackTrace(); - addErrorMessageToJson(initError, resources.getString(server_unreachable_message)); - } catch (IOException e) { - e.printStackTrace(); - addErrorMessageToJson(initError, resources.getString(error_io_exception_user_message)); - } - return null; - } - - protected OkHttpClient initCommercialCAHttpClient(JSONObject initError) { - return initHttpClient(initError, null); - } - - protected OkHttpClient initSelfSignedCAHttpClient(JSONObject initError) { - String certificate = preferences.getString(Provider.CA_CERT, ""); - return initHttpClient(initError, certificate); - } - - protected OkHttpClient initSelfSignedCAHttpClient(JSONObject initError, String certificate) { - return initHttpClient(initError, certificate); - } - - @NonNull - private ConnectionSpec getConnectionSpec() { - ConnectionSpec.Builder connectionSpecbuilder = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) - .tlsVersions(TlsVersion.TLS_1_2, TlsVersion.TLS_1_3); - //FIXME: restrict connection further to the following recommended cipher suites for ALL supported API levels - //figure out how to use bcjsse for that purpose - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) - connectionSpecbuilder.cipherSuites( - CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, - CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 - ); - return connectionSpecbuilder.build(); - } - - @NonNull - private CookieJar getCookieJar() { - return new CookieJar() { - private final HashMap<String, List<Cookie>> cookieStore = new HashMap<>(); - - @Override - public void saveFromResponse(HttpUrl url, List<Cookie> cookies) { - cookieStore.put(url.host(), cookies); - } - - @Override - public List<Cookie> loadForRequest(HttpUrl url) { - List<Cookie> cookies = cookieStore.get(url.host()); - return cookies != null ? cookies : new ArrayList<Cookie>(); - } - }; - } private Bundle tryToRegister(Bundle task) { @@ -402,7 +295,7 @@ public abstract class ProviderApiBase extends IntentService { private Bundle register(String username, String password) { JSONObject stepResult = null; - OkHttpClient okHttpClient = initSelfSignedCAHttpClient(stepResult); + OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(stepResult); if (okHttpClient == null) { return authFailedNotification(stepResult, username); } @@ -459,7 +352,7 @@ public abstract class ProviderApiBase extends IntentService { private Bundle authenticate(String username, String password) { Bundle result = new Bundle(); JSONObject stepResult = new JSONObject(); - OkHttpClient okHttpClient = initSelfSignedCAHttpClient(stepResult); + OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(stepResult); if (okHttpClient == null) { return authFailedNotification(stepResult, username); } @@ -544,7 +437,8 @@ public abstract class ProviderApiBase extends IntentService { intentUpdate.setAction(UPDATE_PROGRESSBAR); intentUpdate.addCategory(Intent.CATEGORY_DEFAULT); intentUpdate.putExtra(CURRENT_PROGRESS, progress); - sendBroadcast(intentUpdate); + serviceCallback.broadcastProgress(intentUpdate); + //sendBroadcast(intentUpdate); } /** @@ -646,10 +540,10 @@ public abstract class ProviderApiBase extends IntentService { } private String requestStringFromServer(String url, String request_method, String jsonString, List<Pair<String, String>> headerArgs, @NonNull OkHttpClient okHttpClient) { - Response response; + //Response response; String plainResponseBody = null; - RequestBody jsonBody = jsonString != null ? RequestBody.create(JSON, jsonString) : null; + /*RequestBody jsonBody = jsonString != null ? RequestBody.create(JSON, jsonString) : null; Request.Builder requestBuilder = new Request.Builder() .url(url) .method(request_method, jsonBody); @@ -662,16 +556,17 @@ public abstract class ProviderApiBase extends IntentService { String locale = Locale.getDefault().getLanguage() + Locale.getDefault().getCountry(); requestBuilder.addHeader("Accept-Language", locale); Request request = requestBuilder.build(); - +*/ try { - response = okHttpClient.newCall(request).execute(); - - InputStream inputStream = response.body().byteStream(); - Scanner scanner = new Scanner(inputStream).useDelimiter("\\A"); - if (scanner.hasNext()) { - plainResponseBody = scanner.next(); - } - + //response = okHttpClient.newCall(request).execute(); + //response = ProviderApiConnector.requestStringFromServer(url, request_method, jsonString, headerArgs, okHttpClient); + //InputStream inputStream = response.body().byteStream(); + //Scanner scanner = new Scanner(inputStream).useDelimiter("\\A"); + //if (scanner.hasNext()) { + // plainResponseBody = scanner.next(); + //} + + plainResponseBody = ProviderApiConnector.requestStringFromServer(url, request_method, jsonString, headerArgs, okHttpClient); } catch (NullPointerException npe) { plainResponseBody = formatErrorMessage(error_json_exception_user_message); } catch (UnknownHostException | SocketTimeoutException e) { @@ -742,14 +637,12 @@ public abstract class ProviderApiBase extends IntentService { String fingerprint = provider_json.getString(Provider.CA_CERT_FINGERPRINT); String encoding = fingerprint.split(":")[0]; String expected_fingerprint = fingerprint.split(":")[1]; - String real_fingerprint = base64toHex(Base64.encodeToString( - MessageDigest.getInstance(encoding).digest(certificate.getEncoded()), - Base64.DEFAULT)); + String real_fingerprint = getFingerprintFromCertificate(certificate, encoding); result = real_fingerprint.trim().equalsIgnoreCase(expected_fingerprint.trim()); } else result = false; - } catch (JSONException | NoSuchAlgorithmException | CertificateEncodingException e) { + } catch (JSONException | NoSuchAlgorithmException | CertificateEncodingException /*| UnsupportedEncodingException*/ e) { result = false; } } @@ -757,8 +650,11 @@ public abstract class ProviderApiBase extends IntentService { return result; } + + protected void checkPersistedProviderUpdates() { - String providerDomain = getProviderDomain(providerDefinition); + //String providerDomain = getProviderDomain(providerDefinition); + String providerDomain = getDomainFromMainURL(lastProviderMainUrl); if (hasUpdatedProviderDetails(providerDomain)) { providerCaCert = getPersistedProviderCA(providerDomain); providerDefinition = getPersistedProviderDefinition(providerDomain); @@ -787,7 +683,7 @@ public abstract class ProviderApiBase extends IntentService { } catch (JSONException e) { e.printStackTrace(); result.putBoolean(RESULT_KEY, false); - result = setErrorResult(result, getString(R.string.warning_corrupted_provider_details), ERROR_CORRUPTED_PROVIDER_JSON.toString()); + result = setErrorResult(result, resources.getString(R.string.warning_corrupted_provider_details), ERROR_CORRUPTED_PROVIDER_JSON.toString()); } return result; @@ -803,22 +699,21 @@ public abstract class ProviderApiBase extends IntentService { X509Certificate certificate = ConfigHelper.parseX509CertificateFromString(cert_string); if (certificate == null) { - return setErrorResult(result, getString(R.string.warning_corrupted_provider_cert), ERROR_INVALID_CERTIFICATE.toString()); + return setErrorResult(result, resources.getString(R.string.warning_corrupted_provider_cert), ERROR_INVALID_CERTIFICATE.toString()); } try { certificate.checkValidity(); String fingerprint = getCaCertFingerprint(providerDefinition); String encoding = fingerprint.split(":")[0]; String expected_fingerprint = fingerprint.split(":")[1]; - String real_fingerprint = base64toHex(Base64.encodeToString( - MessageDigest.getInstance(encoding).digest(certificate.getEncoded()), - Base64.DEFAULT)); + String real_fingerprint = getFingerprintFromCertificate(certificate, encoding); if (!real_fingerprint.trim().equalsIgnoreCase(expected_fingerprint.trim())) { - return setErrorResult(result, getString(R.string.warning_corrupted_provider_cert), ERROR_CERTIFICATE_PINNING.toString()); + return setErrorResult(result, resources.getString(R.string.warning_corrupted_provider_cert), ERROR_CERTIFICATE_PINNING.toString()); } + if (!hasApiUrlExpectedDomain(providerDefinition, mainUrl)){ - return setErrorResult(result, getString(R.string.warning_corrupted_provider_details), ERROR_CORRUPTED_PROVIDER_JSON.toString()); + return setErrorResult(result, resources.getString(R.string.warning_corrupted_provider_details), ERROR_CORRUPTED_PROVIDER_JSON.toString()); } if (!canConnect(cert_string, providerDefinition, result)) { @@ -827,10 +722,12 @@ public abstract class ProviderApiBase extends IntentService { } catch (NoSuchAlgorithmException e ) { return setErrorResult(result, resources.getString(error_no_such_algorithm_exception_user_message), null); } catch (ArrayIndexOutOfBoundsException e) { - return setErrorResult(result, getString(R.string.warning_corrupted_provider_details), ERROR_CORRUPTED_PROVIDER_JSON.toString()); + return setErrorResult(result, resources.getString(R.string.warning_corrupted_provider_details), ERROR_CORRUPTED_PROVIDER_JSON.toString()); } catch (CertificateEncodingException | CertificateNotYetValidException | CertificateExpiredException e) { - return setErrorResult(result, getString(R.string.warning_expired_provider_cert), ERROR_INVALID_CERTIFICATE.toString()); - } + return setErrorResult(result, resources.getString(R.string.warning_expired_provider_cert), ERROR_INVALID_CERTIFICATE.toString()); + } /*catch (UnsupportedEncodingException e) { + return setErrorResult(result, resources.getString(R.string.warning_corrupted_provider_cert), ERROR_CERTIFICATE_PINNING.toString()); + }*/ result.putBoolean(RESULT_KEY, true); return result; @@ -870,13 +767,23 @@ public abstract class ProviderApiBase extends IntentService { JSONObject errorJson = new JSONObject(); String baseUrl = getApiUrl(providerDefinition); - OkHttpClient okHttpClient = initSelfSignedCAHttpClient(errorJson, caCert); + OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(errorJson, caCert); if (okHttpClient == null) { result.putString(ERRORS, errorJson.toString()); return false; } - List<Pair<String, String>> headerArgs = getAuthorizationHeader(); + //try { + + return ProviderApiConnector.canConnect(okHttpClient, baseUrl); + /*} catch (RuntimeException | IOException e) { + e.printStackTrace(); + }*/ + + //return false; + + + /*List<Pair<String, String>> headerArgs = getAuthorizationHeader(); String plain_response = requestStringFromServer(baseUrl, "GET", null, headerArgs, okHttpClient); try { @@ -888,7 +795,7 @@ public abstract class ProviderApiBase extends IntentService { //eat me } - return true; + return true;*/ } protected String getCaCertFingerprint(JSONObject providerDefinition) { @@ -964,8 +871,13 @@ public abstract class ProviderApiBase extends IntentService { return ""; } - protected boolean hasUpdatedProviderDetails(String providerDomain) { - return preferences.contains(Provider.KEY + "." + providerDomain) && preferences.contains(Provider.CA_CERT + "." + providerDomain); + protected boolean hasUpdatedProviderDetails(String domain) { + return preferences.contains(Provider.KEY + "." + domain) && preferences.contains(Provider.CA_CERT + "." + domain); + } + + protected String getDomainFromMainURL(String mainUrl) { + return mainUrl.replaceFirst("http[s]?://", "").replaceFirst("/.*", ""); + } /** @@ -983,6 +895,8 @@ public abstract class ProviderApiBase extends IntentService { } catch (JSONException e) { // TODO Auto-generated catch block error_message = string_json_error_message; + } catch (NullPointerException e) { + //do nothing } return error_message; @@ -999,7 +913,7 @@ public abstract class ProviderApiBase extends IntentService { } private boolean logOut() { - OkHttpClient okHttpClient = initSelfSignedCAHttpClient(new JSONObject()); + OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(new JSONObject()); if (okHttpClient == null) { return false; } @@ -1007,23 +921,32 @@ public abstract class ProviderApiBase extends IntentService { String deleteUrl = providerApiUrl + "/logout"; int progress = 0; - Request.Builder requestBuilder = new Request.Builder() + /* Request.Builder requestBuilder = new Request.Builder() .url(deleteUrl) .delete(); - Request request = requestBuilder.build(); - - try { - Response response = okHttpClient.newCall(request).execute(); - // v---- was already not authorized - if (response.isSuccessful() || response.code() == 401) { - broadcastProgress(progress++); - LeapSRPSession.setToken(""); - } - - } catch (IOException e) { - return false; + Request request = requestBuilder.build();*/ + + //try { + + //Response response = okHttpClient.newCall(request).execute(); +// Response response = ProviderApiConnector.delete(okHttpClient, deleteUrl); +// // v---- was already not authorized +// if (response.isSuccessful() || response.code() == 401) { +// broadcastProgress(progress++); +// LeapSRPSession.setToken(""); +// } +// +// } catch (IOException | RuntimeException e) { +// return false; +// } +// return true; + + if (ProviderApiConnector.delete(okHttpClient, deleteUrl)) { + broadcastProgress(progress++); + LeapSRPSession.setToken(""); + return true; } - return true; + return false; } //FIXME: don't save private keys in shared preferences! use the keystore 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 61349490..bd9324bb 100644 --- a/app/src/main/java/se/leap/bitmaskclient/userstatus/SessionDialog.java +++ b/app/src/main/java/se/leap/bitmaskclient/userstatus/SessionDialog.java @@ -23,8 +23,6 @@ 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; |