summaryrefslogtreecommitdiff
path: root/app/src
diff options
context:
space:
mode:
Diffstat (limited to 'app/src')
-rw-r--r--app/src/debug/java/se/leap/bitmaskclient/ConfigurationWizard.java28
-rw-r--r--app/src/debug/java/se/leap/bitmaskclient/ProviderAPI.java68
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/Provider.java14
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/ProviderManager.java52
-rw-r--r--app/src/main/res/values-es/strings.xml1
-rw-r--r--app/src/main/res/values/strings.xml1
-rw-r--r--app/src/release/java/se/leap/bitmaskclient/ConfigurationWizard.java23
-rw-r--r--app/src/release/java/se/leap/bitmaskclient/ProviderAPI.java43
8 files changed, 159 insertions, 71 deletions
diff --git a/app/src/debug/java/se/leap/bitmaskclient/ConfigurationWizard.java b/app/src/debug/java/se/leap/bitmaskclient/ConfigurationWizard.java
index 59d77d83..aac53a07 100644
--- a/app/src/debug/java/se/leap/bitmaskclient/ConfigurationWizard.java
+++ b/app/src/debug/java/se/leap/bitmaskclient/ConfigurationWizard.java
@@ -126,7 +126,7 @@ public class ConfigurationWizard extends Activity
if (fragment_manager.findFragmentByTag(ProviderDetailFragment.TAG) == null && setting_up_provider) {
if (selected_provider != null)
- onItemSelectedUi(selected_provider);
+ onItemSelectedUi();
if (progress > 0)
mProgressBar.setProgress(progress);
}
@@ -231,20 +231,18 @@ public class ConfigurationWizard extends Activity
void onItemSelected(int position) {
//TODO Code 2 pane view
selected_provider = adapter.getItem(position);
- onItemSelectedLogic(selected_provider);
- onItemSelectedUi(selected_provider);
+ onItemSelectedLogic();
+ onItemSelectedUi();
}
- private void onItemSelectedLogic(Provider selected_provider) {
- boolean danger_on = true;
- if (preferences.contains(ProviderItem.DANGER_ON))
- danger_on = preferences.getBoolean(ProviderItem.DANGER_ON, false);
- setUpProvider(selected_provider.mainUrl(), danger_on);
+ private void onItemSelectedLogic() {
+ boolean danger_on = preferences.getBoolean(ProviderItem.DANGER_ON, true);
+ setUpProvider(danger_on);
}
- private void onItemSelectedUi(Provider provider) {
+ private void onItemSelectedUi() {
startProgressBar();
- adapter.hideAllBut(adapter.indexOf(provider));
+ adapter.hideAllBut(adapter.indexOf(selected_provider));
}
@Override
@@ -383,21 +381,21 @@ public class ConfigurationWizard extends Activity
private void autoSelectProvider(Provider provider, boolean danger_on) {
preferences.edit().putBoolean(ProviderItem.DANGER_ON, danger_on).apply();
selected_provider = provider;
- onItemSelectedLogic(selected_provider);
- onItemSelectedUi(selected_provider);
+ onItemSelectedLogic();
+ onItemSelectedUi();
}
/**
* Asks ProviderAPI to download a new provider.json file
*
- * @param provider_main_url
* @param danger_on tells if HTTPS client should bypass certificate errors
*/
- public void setUpProvider(URL provider_main_url, boolean danger_on) {
+ public void setUpProvider(boolean danger_on) {
Intent provider_API_command = new Intent(this, ProviderAPI.class);
Bundle parameters = new Bundle();
- parameters.putString(Provider.MAIN_URL, provider_main_url.toString());
+ parameters.putString(Provider.MAIN_URL, selected_provider.mainUrl().toString());
parameters.putBoolean(ProviderItem.DANGER_ON, danger_on);
+ parameters.putString(Provider.CA_CERT_FINGERPRINT, selected_provider.certificatePin());
provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER);
provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters);
diff --git a/app/src/debug/java/se/leap/bitmaskclient/ProviderAPI.java b/app/src/debug/java/se/leap/bitmaskclient/ProviderAPI.java
index 9b5601a9..6c57fca2 100644
--- a/app/src/debug/java/se/leap/bitmaskclient/ProviderAPI.java
+++ b/app/src/debug/java/se/leap/bitmaskclient/ProviderAPI.java
@@ -22,6 +22,10 @@ import android.content.res.*;
import android.os.*;
import android.util.*;
+import org.apache.http.client.*;
+import org.json.*;
+import org.thoughtcrime.ssl.pinning.util.*;
+
import java.io.*;
import java.math.*;
import java.net.*;
@@ -32,10 +36,7 @@ import java.util.*;
import javax.net.ssl.*;
-import org.apache.http.client.*;
-import org.json.*;
-
-import se.leap.bitmaskclient.ProviderListContent.ProviderItem;
+import se.leap.bitmaskclient.ProviderListContent.*;
import se.leap.bitmaskclient.eip.*;
/**
@@ -88,6 +89,7 @@ public class ProviderAPI extends IntentService {
private static boolean go_ahead = true;
private static SharedPreferences preferences;
private static String provider_api_url;
+ private static String provider_ca_cert_fingerprint;
private Resources resources;
public static void stop() {
@@ -102,6 +104,7 @@ public class ProviderAPI extends IntentService {
public void onCreate() {
super.onCreate();
+
preferences = getSharedPreferences(Dashboard.SHARED_PREFERENCES, MODE_PRIVATE);
resources = getResources();
CookieHandler.setDefault(new CookieManager(null, CookiePolicy.ACCEPT_ORIGINAL_SERVER));
@@ -124,7 +127,7 @@ public class ProviderAPI extends IntentService {
final ResultReceiver receiver = command.getParcelableExtra(RECEIVER_KEY);
String action = command.getAction();
Bundle parameters = command.getBundleExtra(PARAMETERS);
- if (provider_api_url == null) {
+ if (provider_api_url == null && preferences.contains(Provider.KEY)) {
try {
JSONObject provider_json = new JSONObject(preferences.getString(Provider.KEY, "no provider"));
provider_api_url = provider_json.getString(Provider.API_URL) + "/" + provider_json.getString(Provider.API_VERSION);
@@ -136,12 +139,10 @@ public class ProviderAPI extends IntentService {
if (action.equalsIgnoreCase(SET_UP_PROVIDER)) {
Bundle result = setUpProvider(parameters);
- if (go_ahead) {
- if (result.getBoolean(RESULT_KEY)) {
- receiver.send(PROVIDER_OK, result);
- } else {
- receiver.send(PROVIDER_NOK, result);
- }
+ if (result.getBoolean(RESULT_KEY)) {
+ receiver.send(PROVIDER_OK, result);
+ } else {
+ receiver.send(PROVIDER_NOK, result);
}
} else if (action.equalsIgnoreCase(SIGN_UP)) {
UserSessionStatus.updateStatus(UserSessionStatus.SessionStatus.SIGNING_UP, resources);
@@ -511,15 +512,20 @@ public class ProviderAPI extends IntentService {
int progress = 0;
Bundle current_download = new Bundle();
- if (task != null && task.containsKey(ProviderItem.DANGER_ON) && task.containsKey(Provider.MAIN_URL)) {
- last_danger_on = task.getBoolean(ProviderItem.DANGER_ON);
- last_provider_main_url = task.getString(Provider.MAIN_URL);
+ if (task != null) {
+ last_danger_on = task.containsKey(ProviderItem.DANGER_ON) && task.getBoolean(ProviderItem.DANGER_ON);
+ last_provider_main_url = task.containsKey(Provider.MAIN_URL) ?
+ task.getString(Provider.MAIN_URL) :
+ "";
+ provider_ca_cert_fingerprint = task.containsKey(Provider.CA_CERT_FINGERPRINT) ?
+ task.getString(Provider.CA_CERT_FINGERPRINT) :
+ "";
CA_CERT_DOWNLOADED = PROVIDER_JSON_DOWNLOADED = EIP_SERVICE_JSON_DOWNLOADED = false;
go_ahead = true;
}
if (!PROVIDER_JSON_DOWNLOADED)
- current_download = getAndSetProviderJson(last_provider_main_url, last_danger_on);
+ current_download = getAndSetProviderJson(last_provider_main_url, last_danger_on, provider_ca_cert_fingerprint);
if (PROVIDER_JSON_DOWNLOADED || (current_download.containsKey(RESULT_KEY) && current_download.getBoolean(RESULT_KEY))) {
broadcastProgress(progress++);
PROVIDER_JSON_DOWNLOADED = true;
@@ -608,11 +614,15 @@ public class ProviderAPI extends IntentService {
return hexData.toString();
}
- private Bundle getAndSetProviderJson(String provider_main_url, boolean danger_on) {
+ private Bundle getAndSetProviderJson(String provider_main_url, boolean danger_on, String provider_ca_cert_fingerprint) {
Bundle result = new Bundle();
if (go_ahead) {
- String provider_dot_json_string = downloadWithCommercialCA(provider_main_url + "/provider.json", danger_on);
+ String provider_dot_json_string;
+ if(provider_ca_cert_fingerprint.isEmpty())
+ provider_dot_json_string = downloadWithCommercialCA(provider_main_url + "/provider.json", danger_on);
+ else
+ provider_dot_json_string = downloadWithCommercialCA(provider_main_url + "/provider.json", danger_on, provider_ca_cert_fingerprint);
try {
JSONObject provider_json = new JSONObject(provider_dot_json_string);
@@ -678,6 +688,29 @@ public class ProviderAPI extends IntentService {
return error_message;
}
+ private String downloadWithCommercialCA(String url_string, boolean danger_on, String ca_cert_fingerprint) {
+ String result = "";
+
+ int seconds_of_timeout = 2;
+ String[] pins = new String[] {ca_cert_fingerprint};
+ try {
+ URL url = new URL(url_string);
+ HttpsURLConnection connection = PinningHelper.getPinnedHttpsURLConnection(Dashboard.getContext(), pins, url);
+ connection.setConnectTimeout(seconds_of_timeout * 1000);
+ if (!LeapSRPSession.getToken().isEmpty())
+ connection.addRequestProperty(LeapSRPSession.AUTHORIZATION_HEADER, "Token token = " + LeapSRPSession.getToken());
+ result = new Scanner(connection.getInputStream()).useDelimiter("\\A").next();
+ } catch (IOException e) {
+ if(e instanceof SSLHandshakeException) {
+ result = danger_on ? downloadWithoutCA(url_string) :
+ formatErrorMessage(R.string.error_security_pinnedcertificate);
+ } else
+ result = formatErrorMessage(R.string.error_io_exception_user_message);
+ }
+
+ return result;
+ }
+
/**
* Tries to download the contents of the provided url using commercially validated CA certificate from chosen provider.
* <p/>
@@ -696,6 +729,7 @@ public class ProviderAPI extends IntentService {
try {
provider_url = new URL(string_url);
URLConnection url_connection = provider_url.openConnection();
+
url_connection.setConnectTimeout(seconds_of_timeout * 1000);
if (!LeapSRPSession.getToken().isEmpty())
url_connection.addRequestProperty(LeapSRPSession.AUTHORIZATION_HEADER, "Token token = " + LeapSRPSession.getToken());
diff --git a/app/src/main/java/se/leap/bitmaskclient/Provider.java b/app/src/main/java/se/leap/bitmaskclient/Provider.java
index ee06a586..a030927d 100644
--- a/app/src/main/java/se/leap/bitmaskclient/Provider.java
+++ b/app/src/main/java/se/leap/bitmaskclient/Provider.java
@@ -32,6 +32,7 @@ public final class Provider implements Parcelable {
private JSONObject definition; // Represents our Provider's provider.json
private URL main_url;
+ private String certificate_pin = "";
final public static String
API_URL = "api_uri",
@@ -62,8 +63,9 @@ public final class Provider implements Parcelable {
this.main_url = main_url;
}
- public Provider(File provider_file) {
-
+ public Provider(URL main_url, String certificate_pin) {
+ this.main_url = main_url;
+ this.certificate_pin = certificate_pin;
}
public static final Parcelable.Creator<Provider> CREATOR
@@ -81,11 +83,9 @@ public final class Provider implements Parcelable {
try {
main_url = new URL(in.readString());
String definition_string = in.readString();
- if (definition_string != null)
+ if (!definition_string.isEmpty())
definition = new JSONObject((definition_string));
- } catch (MalformedURLException e) {
- e.printStackTrace();
- } catch (JSONException e) {
+ } catch (MalformedURLException | JSONException e) {
e.printStackTrace();
}
}
@@ -106,6 +106,8 @@ public final class Provider implements Parcelable {
return main_url;
}
+ protected String certificatePin() { return certificate_pin; }
+
protected String getName() {
// Should we pass the locale in, or query the system here?
String lang = Locale.getDefault().getLanguage();
diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderManager.java b/app/src/main/java/se/leap/bitmaskclient/ProviderManager.java
index 40fe8b5a..220a71c8 100644
--- a/app/src/main/java/se/leap/bitmaskclient/ProviderManager.java
+++ b/app/src/main/java/se/leap/bitmaskclient/ProviderManager.java
@@ -49,11 +49,14 @@ public class ProviderManager implements AdapteeCollection<Provider> {
Set<Provider> providers = new HashSet<Provider>();
try {
for (String file : relative_file_paths) {
- String main_url = extractMainUrlFromInputStream(assets_manager.open(directory + "/" + file));
- providers.add(new Provider(new URL(main_url)));
+ InputStream provider_file = assets_manager.open(directory + "/" + file);
+ String main_url = extractMainUrlFromInputStream(provider_file);
+ String certificate_pin = extractCertificatePinFromInputStream(provider_file);
+ if(certificate_pin.isEmpty())
+ providers.add(new Provider(new URL(main_url)));
+ else
+ providers.add(new Provider(new URL(main_url), certificate_pin));
}
- } catch (MalformedURLException e) {
- e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
@@ -75,30 +78,43 @@ public class ProviderManager implements AdapteeCollection<Provider> {
String main_url = extractMainUrlFromInputStream(new FileInputStream(external_files_dir.getAbsolutePath() + "/" + file));
providers.add(new Provider(new URL(main_url)));
}
- } catch (MalformedURLException e) {
- e.printStackTrace();
- } catch (FileNotFoundException e) {
+ } catch (MalformedURLException | FileNotFoundException e) {
e.printStackTrace();
}
return providers;
}
- private String extractMainUrlFromInputStream(InputStream input_stream_file_contents) {
+ private String extractMainUrlFromInputStream(InputStream input_stream) {
String main_url = "";
- byte[] bytes = new byte[0];
+
+ JSONObject file_contents = inputStreamToJson(input_stream);
+ if(file_contents != null)
+ main_url = file_contents.optString(Provider.MAIN_URL);
+ return main_url;
+ }
+
+ private String extractCertificatePinFromInputStream(InputStream input_stream) {
+ String certificate_pin = "";
+
+ JSONObject file_contents = inputStreamToJson(input_stream);
+ if(file_contents != null)
+ certificate_pin = file_contents.optString(Provider.CA_CERT_FINGERPRINT);
+
+ return certificate_pin;
+ }
+
+ private JSONObject inputStreamToJson(InputStream input_stream) {
+ JSONObject json = null;
try {
- bytes = new byte[input_stream_file_contents.available()];
- if (input_stream_file_contents.read(bytes) > 0) {
- JSONObject file_contents = new JSONObject(new String(bytes));
- main_url = file_contents.getString(Provider.MAIN_URL);
- }
- } catch (IOException e) {
- e.printStackTrace();
- } catch (JSONException e) {
+ byte[] bytes = new byte[input_stream.available()];
+ if (input_stream.read(bytes) > 0)
+ json = new JSONObject(new String(bytes));
+ input_stream.reset();
+ } catch (IOException | JSONException e) {
e.printStackTrace();
}
- return main_url;
+ return json;
}
public Set<Provider> providers() {
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index 7ab5150e..82ca44e9 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -48,6 +48,7 @@
<string name="setup_error_close_button">Salir</string>
<string name="setup_error_text">Sucedió un error configurando Bitmask con tu proveedor elegido.\n\nPuedes volver a intentarlo, o elegir otro proveedor.</string>
<string name="server_unreachable_message">No se ha detectado red para hablar con el servidor, inténtalo de nuevo.</string>
+ <string name="error.security.pinnedcertificate">Error de seguridad, actualiza la aplicación o elige otro proveedor.</string>
<string name="malformed_url">No parece que sea un proveedor de Bitmask.</string>
<string name="certificate_error">No es un proveedor de Bitmak de confianza.</string>
<string name="service_is_down_error">El servicio está caído.</string>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index ac6191a9..bcfd3a2c 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -47,6 +47,7 @@
<string name="setup_error_close_button">Exit</string>
<string name="setup_error_text">There was an error configuring Bitmask with your chosen provider.\n\nYou may choose to reconfigure, or exit and configure a provider upon next launch.</string>
<string name="server_unreachable_message">Server is unreachable, please try again.</string>
+ <string name="error.security.pinnedcertificate">Security error, update the app or choose another provider.</string>
<string name="malformed_url">It doesn\'t seem to be a Bitmask provider.</string>
<string name="certificate_error">This is not a trusted Bitmask provider.</string>
<string name="service_is_down_error">Service is down.</string>
diff --git a/app/src/release/java/se/leap/bitmaskclient/ConfigurationWizard.java b/app/src/release/java/se/leap/bitmaskclient/ConfigurationWizard.java
index 19ba1ba8..68ff9e47 100644
--- a/app/src/release/java/se/leap/bitmaskclient/ConfigurationWizard.java
+++ b/app/src/release/java/se/leap/bitmaskclient/ConfigurationWizard.java
@@ -126,7 +126,7 @@ public class ConfigurationWizard extends Activity
if (fragment_manager.findFragmentByTag(ProviderDetailFragment.TAG) == null && setting_up_provider) {
if (selected_provider != null)
- onItemSelectedUi(selected_provider);
+ onItemSelectedUi();
if (progress > 0)
mProgressBar.setProgress(progress);
}
@@ -229,17 +229,17 @@ public class ConfigurationWizard extends Activity
void onItemSelected(int position) {
//TODO Code 2 pane view
selected_provider = adapter.getItem(position);
- onItemSelectedUi(selected_provider);
- onItemSelectedLogic(selected_provider);
+ onItemSelectedUi();
+ onItemSelectedLogic();
}
- private void onItemSelectedLogic(Provider selected_provider) {
- setUpProvider(selected_provider.mainUrl());
+ private void onItemSelectedLogic() {
+ setUpProvider();
}
- private void onItemSelectedUi(Provider provider) {
+ private void onItemSelectedUi() {
startProgressBar();
- adapter.hideAllBut(adapter.indexOf(provider));
+ adapter.hideAllBut(adapter.indexOf(selected_provider));
}
@Override
@@ -379,8 +379,8 @@ public class ConfigurationWizard extends Activity
private void autoSelectProvider(Provider provider) {
selected_provider = provider;
- onItemSelectedUi(selected_provider);
- onItemSelectedLogic(selected_provider);
+ onItemSelectedUi();
+ onItemSelectedLogic();
}
/**
@@ -389,10 +389,11 @@ public class ConfigurationWizard extends Activity
* @param provider_name
* @param provider_main_url
*/
- public void setUpProvider(URL provider_main_url) {
+ public void setUpProvider() {
Intent provider_API_command = new Intent(this, ProviderAPI.class);
Bundle parameters = new Bundle();
- parameters.putString(Provider.MAIN_URL, provider_main_url.toString());
+ parameters.putString(Provider.MAIN_URL, selected_provider.mainUrl().toString());
+ parameters.putString(Provider.CA_CERT_FINGERPRINT, selected_provider.certificatePin());
provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER);
provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters);
diff --git a/app/src/release/java/se/leap/bitmaskclient/ProviderAPI.java b/app/src/release/java/se/leap/bitmaskclient/ProviderAPI.java
index 890262ce..a96556bc 100644
--- a/app/src/release/java/se/leap/bitmaskclient/ProviderAPI.java
+++ b/app/src/release/java/se/leap/bitmaskclient/ProviderAPI.java
@@ -34,6 +34,7 @@ import javax.net.ssl.*;
import org.apache.http.client.*;
import org.json.*;
+import org.thoughtcrime.ssl.pinning.util.*;
import se.leap.bitmaskclient.ProviderListContent.ProviderItem;
import se.leap.bitmaskclient.eip.*;
@@ -87,6 +88,7 @@ public class ProviderAPI extends IntentService {
private static boolean go_ahead = true;
private static SharedPreferences preferences;
private static String provider_api_url;
+ private static String provider_ca_cert_fingerprint;
private Resources resources;
public static void stop() {
@@ -504,13 +506,19 @@ public class ProviderAPI extends IntentService {
Bundle current_download = new Bundle();
if (task != null && task.containsKey(Provider.MAIN_URL)) {
- last_provider_main_url = task.getString(Provider.MAIN_URL);
+ last_provider_main_url = task.containsKey(Provider.MAIN_URL) ?
+ task.getString(Provider.MAIN_URL) :
+ "";
+ provider_ca_cert_fingerprint = task.containsKey(Provider.CA_CERT_FINGERPRINT) ?
+ task.getString(Provider.CA_CERT_FINGERPRINT) :
+ "";
+
CA_CERT_DOWNLOADED = PROVIDER_JSON_DOWNLOADED = EIP_SERVICE_JSON_DOWNLOADED = false;
go_ahead = true;
}
if (!PROVIDER_JSON_DOWNLOADED)
- current_download = getAndSetProviderJson(last_provider_main_url);
+ current_download = getAndSetProviderJson(last_provider_main_url, provider_ca_cert_fingerprint);
if (PROVIDER_JSON_DOWNLOADED || (current_download.containsKey(RESULT_KEY) && current_download.getBoolean(RESULT_KEY))) {
broadcastProgress(progress++);
PROVIDER_JSON_DOWNLOADED = true;
@@ -602,11 +610,16 @@ public class ProviderAPI extends IntentService {
return hexData.toString();
}
- private Bundle getAndSetProviderJson(String provider_main_url) {
+ private Bundle getAndSetProviderJson(String provider_main_url, String provider_ca_cert_fingerprint) {
Bundle result = new Bundle();
if (go_ahead) {
- String provider_dot_json_string = downloadWithCommercialCA(provider_main_url + "/provider.json");
+ String provider_dot_json_string;
+
+ if(provider_ca_cert_fingerprint.isEmpty())
+ provider_dot_json_string = downloadWithCommercialCA(provider_main_url + "/provider.json");
+ else
+ provider_dot_json_string = downloadWithCommercialCA(provider_main_url + "/provider.json", provider_ca_cert_fingerprint);
try {
JSONObject provider_json = new JSONObject(provider_dot_json_string);
@@ -672,6 +685,28 @@ public class ProviderAPI extends IntentService {
return error_message;
}
+ private String downloadWithCommercialCA(String url_string, String ca_cert_fingerprint) {
+ String result = "";
+
+ int seconds_of_timeout = 2;
+ String[] pins = new String[] {ca_cert_fingerprint};
+ try {
+ URL url = new URL(url_string);
+ HttpsURLConnection connection = PinningHelper.getPinnedHttpsURLConnection(Dashboard.getContext(), pins, url);
+ connection.setConnectTimeout(seconds_of_timeout * 1000);
+ if (!LeapSRPSession.getToken().isEmpty())
+ connection.addRequestProperty(LeapSRPSession.AUTHORIZATION_HEADER, "Token token = " + LeapSRPSession.getToken());
+ result = new Scanner(connection.getInputStream()).useDelimiter("\\A").next();
+ } catch (IOException e) {
+ if(e instanceof SSLHandshakeException)
+ result = formatErrorMessage(R.string.error_security_pinnedcertificate);
+ else
+ result = formatErrorMessage(R.string.error_io_exception_user_message);
+ }
+
+ return result;
+ }
+
/**
* Tries to download the contents of the provided url using commercially validated CA certificate from chosen provider.
*