summaryrefslogtreecommitdiff
path: root/app/src
diff options
context:
space:
mode:
Diffstat (limited to 'app/src')
-rw-r--r--app/src/insecure/java/se/leap/bitmaskclient/ProviderApiManager.java1
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/LeapSRPSession.java14
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/MainActivity.java9
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/Provider.java4
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java16
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/ProviderManager.java47
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/StartActivity.java10
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/TLSCompatSocketFactory.java1
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java20
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java2
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/fragments/AlwaysOnDialog.java3
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/utils/ConfigHelper.java161
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/utils/FileHelper.java22
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/utils/InputStreamHelper.java21
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/utils/KeyStoreHelper.java78
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/utils/PreferenceHelper.java (renamed from app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java)235
-rw-r--r--app/src/production/java/se/leap/bitmaskclient/ProviderApiManager.java1
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/PreferenceHelperTest.java (renamed from app/src/test/java/se/leap/bitmaskclient/ConfigHelperTest.java)14
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/ProviderManagerTest.java189
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/ProviderTest.java2
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java22
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java39
22 files changed, 613 insertions, 298 deletions
diff --git a/app/src/insecure/java/se/leap/bitmaskclient/ProviderApiManager.java b/app/src/insecure/java/se/leap/bitmaskclient/ProviderApiManager.java
index 798b6433..7e33cd63 100644
--- a/app/src/insecure/java/se/leap/bitmaskclient/ProviderApiManager.java
+++ b/app/src/insecure/java/se/leap/bitmaskclient/ProviderApiManager.java
@@ -46,6 +46,7 @@ import javax.net.ssl.X509TrustManager;
import okhttp3.OkHttpClient;
import se.leap.bitmaskclient.eip.EIP;
+import se.leap.bitmaskclient.utils.ConfigHelper;
import static android.text.TextUtils.isEmpty;
import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_KEY;
diff --git a/app/src/main/java/se/leap/bitmaskclient/LeapSRPSession.java b/app/src/main/java/se/leap/bitmaskclient/LeapSRPSession.java
index 3a1fd6e0..d1f1ed21 100644
--- a/app/src/main/java/se/leap/bitmaskclient/LeapSRPSession.java
+++ b/app/src/main/java/se/leap/bitmaskclient/LeapSRPSession.java
@@ -17,12 +17,16 @@
package se.leap.bitmaskclient;
-import org.jboss.security.srp.*;
+import org.jboss.security.srp.SRPParameters;
-import java.io.*;
-import java.math.*;
-import java.security.*;
-import java.util.*;
+import java.io.UnsupportedEncodingException;
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Arrays;
+
+import se.leap.bitmaskclient.utils.ConfigHelper;
/**
* Implements all SRP algorithm logic.
diff --git a/app/src/main/java/se/leap/bitmaskclient/MainActivity.java b/app/src/main/java/se/leap/bitmaskclient/MainActivity.java
index d9917799..c194ea99 100644
--- a/app/src/main/java/se/leap/bitmaskclient/MainActivity.java
+++ b/app/src/main/java/se/leap/bitmaskclient/MainActivity.java
@@ -51,6 +51,7 @@ import se.leap.bitmaskclient.eip.EipCommand;
import se.leap.bitmaskclient.eip.EipStatus;
import se.leap.bitmaskclient.eip.VoidVpnService;
import se.leap.bitmaskclient.fragments.LogFragment;
+import se.leap.bitmaskclient.utils.ConfigHelper;
import static android.content.Intent.CATEGORY_DEFAULT;
import static se.leap.bitmaskclient.Constants.BROADCAST_EIP_EVENT;
@@ -76,6 +77,8 @@ import static se.leap.bitmaskclient.ProviderAPI.INCORRECTLY_DOWNLOADED_VPN_CERTI
import static se.leap.bitmaskclient.ProviderAPI.USER_MESSAGE;
import static se.leap.bitmaskclient.R.string.downloading_vpn_certificate_failed;
import static se.leap.bitmaskclient.R.string.vpn_certificate_user_message;
+import static se.leap.bitmaskclient.utils.PreferenceHelper.getSavedProviderFromSharedPreferences;
+import static se.leap.bitmaskclient.utils.PreferenceHelper.storeProviderInPreferences;
public class MainActivity extends AppCompatActivity implements Observer {
@@ -123,7 +126,7 @@ public class MainActivity extends AppCompatActivity implements Observer {
getSupportFragmentManager().findFragmentById(R.id.navigation_drawer);
preferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE);
- provider = ConfigHelper.getSavedProviderFromSharedPreferences(preferences);
+ provider = getSavedProviderFromSharedPreferences(preferences);
// Set up the drawer.
navigationDrawerFragment.setUp(
@@ -212,7 +215,7 @@ public class MainActivity extends AppCompatActivity implements Observer {
return;
}
- ConfigHelper.storeProviderInPreferences(preferences, provider);
+ storeProviderInPreferences(preferences, provider);
navigationDrawerFragment.refresh();
switch (requestCode) {
@@ -339,7 +342,7 @@ public class MainActivity extends AppCompatActivity implements Observer {
case CORRECTLY_DOWNLOADED_VPN_CERTIFICATE:
provider = resultData.getParcelable(PROVIDER_KEY);
- ConfigHelper.storeProviderInPreferences(preferences, provider);
+ storeProviderInPreferences(preferences, provider);
EipCommand.startVPN(this, true);
break;
case INCORRECTLY_DOWNLOADED_VPN_CERTIFICATE:
diff --git a/app/src/main/java/se/leap/bitmaskclient/Provider.java b/app/src/main/java/se/leap/bitmaskclient/Provider.java
index fd067bf9..5e5a3a62 100644
--- a/app/src/main/java/se/leap/bitmaskclient/Provider.java
+++ b/app/src/main/java/se/leap/bitmaskclient/Provider.java
@@ -140,7 +140,7 @@ public final class Provider implements Parcelable {
return definition;
}
- String getDefinitionString() {
+ public String getDefinitionString() {
return getDefinition().toString();
}
@@ -148,7 +148,7 @@ public final class Provider implements Parcelable {
return mainUrl.getDomain();
}
- String getMainUrlString() {
+ public String getMainUrlString() {
return getMainUrl().toString();
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java b/app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java
index 2cde431e..25b5a56c 100644
--- a/app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java
+++ b/app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java
@@ -52,8 +52,9 @@ import javax.net.ssl.SSLHandshakeException;
import okhttp3.OkHttpClient;
import se.leap.bitmaskclient.Constants.CREDENTIAL_ERRORS;
+import se.leap.bitmaskclient.utils.ConfigHelper;
-import static se.leap.bitmaskclient.ConfigHelper.getFingerprintFromCertificate;
+import static se.leap.bitmaskclient.utils.ConfigHelper.getFingerprintFromCertificate;
import static se.leap.bitmaskclient.Constants.BROADCAST_PROVIDER_API_EVENT;
import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_CODE;
import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_KEY;
@@ -103,6 +104,9 @@ import static se.leap.bitmaskclient.R.string.vpn_certificate_is_invalid;
import static se.leap.bitmaskclient.R.string.warning_corrupted_provider_cert;
import static se.leap.bitmaskclient.R.string.warning_corrupted_provider_details;
import static se.leap.bitmaskclient.R.string.warning_expired_provider_cert;
+import static se.leap.bitmaskclient.utils.ConfigHelper.parseRsaKeyFromString;
+import static se.leap.bitmaskclient.utils.PreferenceHelper.deleteProviderDetailsFromPreferences;
+import static se.leap.bitmaskclient.utils.PreferenceHelper.getFromPersistedProvider;
/**
* Implements the logic of the http api calls. The methods of this class needs to be called from
@@ -218,7 +222,7 @@ public abstract class ProviderApiManagerBase {
void resetProviderDetails(Provider provider) {
provider.reset();
- ConfigHelper.deleteProviderDetailsFromPreferences(preferences, provider.getDomain());
+ deleteProviderDetailsFromPreferences(preferences, provider.getDomain());
}
String formatErrorMessage(final int toastStringId) {
@@ -754,16 +758,16 @@ public abstract class ProviderApiManagerBase {
}
protected String getPersistedPrivateKey(String providerDomain) {
- return ConfigHelper.getFromPersistedProvider(PROVIDER_PRIVATE_KEY, providerDomain, preferences);
+ return getFromPersistedProvider(PROVIDER_PRIVATE_KEY, providerDomain, preferences);
}
protected String getPersistedVPNCertificate(String providerDomain) {
- return ConfigHelper.getFromPersistedProvider(PROVIDER_VPN_CERTIFICATE, providerDomain, preferences);
+ return getFromPersistedProvider(PROVIDER_VPN_CERTIFICATE, providerDomain, preferences);
}
protected JSONObject getPersistedProviderDefinition(String providerDomain) {
try {
- return new JSONObject(ConfigHelper.getFromPersistedProvider(Provider.KEY, providerDomain, preferences));
+ return new JSONObject(getFromPersistedProvider(Provider.KEY, providerDomain, preferences));
} catch (JSONException e) {
e.printStackTrace();
return new JSONObject();
@@ -849,7 +853,7 @@ public abstract class ProviderApiManagerBase {
}
}
- RSAPrivateKey key = ConfigHelper.parseRsaKeyFromString(keyString);
+ RSAPrivateKey key = parseRsaKeyFromString(keyString);
keyString = Base64.encodeToString(key.getEncoded(), Base64.DEFAULT);
provider.setPrivateKey( "-----BEGIN RSA PRIVATE KEY-----\n" + keyString + "-----END RSA PRIVATE KEY-----");
diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderManager.java b/app/src/main/java/se/leap/bitmaskclient/ProviderManager.java
index 97ba3b98..a36c2dec 100644
--- a/app/src/main/java/se/leap/bitmaskclient/ProviderManager.java
+++ b/app/src/main/java/se/leap/bitmaskclient/ProviderManager.java
@@ -1,6 +1,7 @@
package se.leap.bitmaskclient;
import android.content.res.AssetManager;
+import android.support.annotation.VisibleForTesting;
import com.pedrogomez.renderers.AdapteeCollection;
@@ -8,9 +9,7 @@ import org.json.JSONException;
import org.json.JSONObject;
import java.io.File;
-import java.io.FileInputStream;
import java.io.FileNotFoundException;
-import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
@@ -22,6 +21,11 @@ import java.util.Iterator;
import java.util.List;
import java.util.Set;
+import static se.leap.bitmaskclient.utils.FileHelper.createFile;
+import static se.leap.bitmaskclient.utils.FileHelper.persistFile;
+import static se.leap.bitmaskclient.utils.InputStreamHelper.getInputStreamFrom;
+import static se.leap.bitmaskclient.utils.InputStreamHelper.loadInputStreamAsString;
+
/**
* Created by parmegv on 4/12/14.
*/
@@ -37,6 +41,8 @@ public class ProviderManager implements AdapteeCollection<Provider> {
private static ProviderManager instance;
final private static String URLS = "urls";
+ final private static String EXT_JSON = ".json";
+ final private static String EXT_PEM = ".pem";
public static ProviderManager getInstance(AssetManager assetsManager, File externalFilesDir) {
if (instance == null)
@@ -45,6 +51,11 @@ public class ProviderManager implements AdapteeCollection<Provider> {
return instance;
}
+ @VisibleForTesting
+ static void reset() {
+ instance = null;
+ }
+
private ProviderManager(AssetManager assetManager, File externalFilesDir) {
this.assetsManager = assetManager;
addDefaultProviders(assetManager);
@@ -79,8 +90,8 @@ public class ProviderManager implements AdapteeCollection<Provider> {
String provider = file.substring(0, file.length() - ".url".length());
InputStream provider_file = assetsManager.open(directory + "/" + file);
mainUrl = extractMainUrlFromInputStream(provider_file);
- certificate = ConfigHelper.loadInputStreamAsString(assetsManager.open(provider + ".pem"));
- providerDefinition = ConfigHelper.loadInputStreamAsString(assetsManager.open(provider + ".json"));
+ certificate = loadInputStreamAsString(assetsManager.open(provider + EXT_PEM));
+ providerDefinition = loadInputStreamAsString(assetsManager.open(provider + EXT_JSON));
} catch (IOException e) {
e.printStackTrace();
}
@@ -107,7 +118,7 @@ public class ProviderManager implements AdapteeCollection<Provider> {
Set<Provider> providers = new HashSet<>();
try {
for (String file : files) {
- String mainUrl = extractMainUrlFromInputStream(ConfigHelper.getInputStreamFrom(externalFilesDir.getAbsolutePath() + "/" + file));
+ String mainUrl = extractMainUrlFromInputStream(getInputStreamFrom(externalFilesDir.getAbsolutePath() + "/" + file));
providers.add(new Provider(new URL(mainUrl)));
}
} catch (MalformedURLException | FileNotFoundException e) {
@@ -219,19 +230,33 @@ public class ProviderManager implements AdapteeCollection<Provider> {
defaultProviderURLs.clear();
}
- //FIXME: removed custom providers should be deleted here as well
void saveCustomProvidersToFile() {
try {
+ deleteLegacyCustomProviders();
+
for (Provider provider : customProviders) {
- File providerFile = new File(externalFilesDir, provider.getName() + ".json");
+ File providerFile = createFile(externalFilesDir, provider.getName() + EXT_JSON);
if (!providerFile.exists()) {
- FileWriter writer = new FileWriter(providerFile);
- writer.write(provider.toJson().toString());
- writer.close();
+ persistFile(providerFile, provider.toJson().toString());
}
}
- } catch (IOException e) {
+ } catch (IOException | SecurityException e) {
e.printStackTrace();
}
}
+
+ /**
+ * Deletes persisted custom providers from from internal storage that are not in customProviders list anymore
+ */
+ private void deleteLegacyCustomProviders() throws IOException, SecurityException {
+ Set<Provider> persistedCustomProviders = externalFilesDir != null && externalFilesDir.isDirectory() ?
+ providersFromFiles(externalFilesDir.list()) : new HashSet<Provider>();
+ persistedCustomProviders.removeAll(customProviders);
+ for (Provider providerToDelete : persistedCustomProviders) {
+ File providerFile = createFile(externalFilesDir, providerToDelete.getName() + EXT_JSON);
+ if (providerFile.exists()) {
+ providerFile.delete();
+ }
+ }
+ }
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/StartActivity.java b/app/src/main/java/se/leap/bitmaskclient/StartActivity.java
index 6bbdeb4f..33c13b90 100644
--- a/app/src/main/java/se/leap/bitmaskclient/StartActivity.java
+++ b/app/src/main/java/se/leap/bitmaskclient/StartActivity.java
@@ -15,6 +15,7 @@ import java.lang.annotation.RetentionPolicy;
import de.blinkt.openvpn.core.VpnStatus;
import se.leap.bitmaskclient.eip.EipCommand;
import se.leap.bitmaskclient.userstatus.User;
+import se.leap.bitmaskclient.utils.ConfigHelper;
import static se.leap.bitmaskclient.Constants.APP_ACTION_CONFIGURE_ALWAYS_ON_PROFILE;
import static se.leap.bitmaskclient.Constants.EIP_RESTART_ON_BOOT;
@@ -24,6 +25,9 @@ import static se.leap.bitmaskclient.Constants.PROVIDER_KEY;
import static se.leap.bitmaskclient.Constants.REQUEST_CODE_CONFIGURE_LEAP;
import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES;
import static se.leap.bitmaskclient.MainActivity.ACTION_SHOW_VPN_FRAGMENT;
+import static se.leap.bitmaskclient.utils.PreferenceHelper.getSavedProviderFromSharedPreferences;
+import static se.leap.bitmaskclient.utils.PreferenceHelper.providerInSharedPreferences;
+import static se.leap.bitmaskclient.utils.PreferenceHelper.storeProviderInPreferences;
/**
* Activity shown at startup. Evaluates if App is started for the first time or has been upgraded
@@ -154,9 +158,9 @@ public class StartActivity extends Activity{
}
private void prepareEIP() {
- boolean provider_exists = ConfigHelper.providerInSharedPreferences(preferences);
+ boolean provider_exists = providerInSharedPreferences(preferences);
if (provider_exists) {
- Provider provider = ConfigHelper.getSavedProviderFromSharedPreferences(preferences);
+ Provider provider = getSavedProviderFromSharedPreferences(preferences);
if(!provider.isConfigured()) {
configureLeapProvider();
} else {
@@ -186,7 +190,7 @@ public class StartActivity extends Activity{
if (requestCode == REQUEST_CODE_CONFIGURE_LEAP) {
if (resultCode == RESULT_OK && data != null && data.hasExtra(Provider.KEY)) {
Provider provider = data.getParcelableExtra(Provider.KEY);
- ConfigHelper.storeProviderInPreferences(preferences, provider);
+ storeProviderInPreferences(preferences, provider);
EipCommand.startVPN(this, false);
showMainActivity();
} else if (resultCode == RESULT_CANCELED) {
diff --git a/app/src/main/java/se/leap/bitmaskclient/TLSCompatSocketFactory.java b/app/src/main/java/se/leap/bitmaskclient/TLSCompatSocketFactory.java
index 76d38447..cca75bdf 100644
--- a/app/src/main/java/se/leap/bitmaskclient/TLSCompatSocketFactory.java
+++ b/app/src/main/java/se/leap/bitmaskclient/TLSCompatSocketFactory.java
@@ -22,6 +22,7 @@ import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import okhttp3.OkHttpClient;
+import se.leap.bitmaskclient.utils.ConfigHelper;
/**
* Created by cyberta on 24.10.17.
diff --git a/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java b/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java
index e7a5e460..a2aebd44 100644
--- a/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java
+++ b/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java
@@ -46,9 +46,8 @@ import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.CompoundButton;
import android.widget.ListView;
-import android.widget.Toast;
-import se.leap.bitmaskclient.ConfigHelper;
+import se.leap.bitmaskclient.utils.ConfigHelper;
import se.leap.bitmaskclient.DrawerSettingsAdapter;
import se.leap.bitmaskclient.DrawerSettingsAdapter.DrawerSettingsItem;
import se.leap.bitmaskclient.FragmentManagerEnhanced;
@@ -62,8 +61,6 @@ import se.leap.bitmaskclient.fragments.LogFragment;
import static android.content.Context.MODE_PRIVATE;
import static se.leap.bitmaskclient.BitmaskApp.getRefWatcher;
-import static se.leap.bitmaskclient.ConfigHelper.getSaveBattery;
-import static se.leap.bitmaskclient.ConfigHelper.getShowAlwaysOnDialog;
import static se.leap.bitmaskclient.Constants.PROVIDER_KEY;
import static se.leap.bitmaskclient.Constants.REQUEST_CODE_SWITCH_PROVIDER;
import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES;
@@ -77,6 +74,11 @@ import static se.leap.bitmaskclient.DrawerSettingsAdapter.SWITCH_PROVIDER;
import static se.leap.bitmaskclient.R.string.about_fragment_title;
import static se.leap.bitmaskclient.R.string.log_fragment_title;
import static se.leap.bitmaskclient.R.string.switch_provider_menu_option;
+import static se.leap.bitmaskclient.utils.PreferenceHelper.getProviderName;
+import static se.leap.bitmaskclient.utils.PreferenceHelper.getSaveBattery;
+import static se.leap.bitmaskclient.utils.PreferenceHelper.getSavedProviderFromSharedPreferences;
+import static se.leap.bitmaskclient.utils.PreferenceHelper.getShowAlwaysOnDialog;
+import static se.leap.bitmaskclient.utils.PreferenceHelper.saveBattery;
/**
* Fragment used for managing interactions for and presentation of a navigation drawer.
@@ -307,7 +309,7 @@ public class NavigationDrawerFragment extends Fragment {
DrawerSettingsItem item = settingsListAdapter.getDrawerItem(BATTERY_SAVER);
item.setChecked(true);
settingsListAdapter.notifyDataSetChanged();
- ConfigHelper.saveBattery(getContext(), item.isChecked());
+ saveBattery(getContext(), item.isChecked());
}
})
.setNegativeButton(activity.getString(android.R.string.no), new DialogInterface.OnClickListener() {
@@ -392,14 +394,14 @@ public class NavigationDrawerFragment extends Fragment {
private void onSwitchItemSelected(int elementType, boolean newStateIsChecked) {
switch (elementType) {
case BATTERY_SAVER:
- if (ConfigHelper.getSaveBattery(getContext()) == newStateIsChecked) {
+ if (getSaveBattery(getContext()) == newStateIsChecked) {
//initial ui setup, ignore
return;
}
if (newStateIsChecked) {
showExperimentalFeatureAlert();
} else {
- ConfigHelper.saveBattery(this.getContext(), false);
+ saveBattery(this.getContext(), false);
disableSwitch(BATTERY_SAVER);
}
break;
@@ -425,7 +427,7 @@ public class NavigationDrawerFragment extends Fragment {
fragment = new EipFragment();
fragmentTag = EipFragment.TAG;
Bundle arguments = new Bundle();
- Provider currentProvider = ConfigHelper.getSavedProviderFromSharedPreferences(preferences);
+ Provider currentProvider = getSavedProviderFromSharedPreferences(preferences);
arguments.putParcelable(PROVIDER_KEY, currentProvider);
fragment.setArguments(arguments);
} else {
@@ -483,7 +485,7 @@ public class NavigationDrawerFragment extends Fragment {
private void createListAdapterData() {
accountListAdapter.clear();
- String providerName = ConfigHelper.getProviderName(preferences);
+ String providerName = getProviderName(preferences);
if (providerName == null) {
//TODO: ADD A header to the ListView containing a useful message.
//TODO 2: disable switchProvider
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java b/app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java
index 03dd9d05..83904729 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java
@@ -22,7 +22,7 @@ import java.security.cert.X509Certificate;
import java.util.Calendar;
import java.util.Date;
-import se.leap.bitmaskclient.ConfigHelper;
+import se.leap.bitmaskclient.utils.ConfigHelper;
public class VpnCertificateValidator {
public final static String TAG = VpnCertificateValidator.class.getSimpleName();
diff --git a/app/src/main/java/se/leap/bitmaskclient/fragments/AlwaysOnDialog.java b/app/src/main/java/se/leap/bitmaskclient/fragments/AlwaysOnDialog.java
index 3558f378..e3d004f5 100644
--- a/app/src/main/java/se/leap/bitmaskclient/fragments/AlwaysOnDialog.java
+++ b/app/src/main/java/se/leap/bitmaskclient/fragments/AlwaysOnDialog.java
@@ -17,7 +17,8 @@ import butterknife.InjectView;
import se.leap.bitmaskclient.R;
import se.leap.bitmaskclient.views.IconTextView;
-import static se.leap.bitmaskclient.ConfigHelper.saveShowAlwaysOnDialog;
+import static se.leap.bitmaskclient.utils.PreferenceHelper.saveShowAlwaysOnDialog;
+
/**
* Created by cyberta on 25.02.18.
diff --git a/app/src/main/java/se/leap/bitmaskclient/utils/ConfigHelper.java b/app/src/main/java/se/leap/bitmaskclient/utils/ConfigHelper.java
new file mode 100644
index 00000000..bb9fb2d4
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/utils/ConfigHelper.java
@@ -0,0 +1,161 @@
+/**
+ * Copyright (c) 2013 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.utils;
+
+import android.support.annotation.NonNull;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.spongycastle.util.encoders.Base64;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.security.KeyFactory;
+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 se.leap.bitmaskclient.ProviderAPI;
+
+/**
+ * Stores constants, and implements auxiliary methods used across all Bitmask Android classes.
+ *
+ * @author parmegv
+ * @author MeanderingCode
+ */
+public class ConfigHelper {
+ final public static String NG_1024 =
+ "eeaf0ab9adb38dd69c33f80afa8fc5e86072618775ff3c0b9ea2314c9c256576d674df7496ea81d3383b4813d692c6e0e0d5d8e250b98be48e495c1d6089dad15dc7d7b46154d6b6ce8ef4ad69b15d4982559b297bcf1885c529f566660e57ec68edbc3c05726cc02fd4cbf4976eaa9afd5138fe8376435b9fc61d2fc0eb06e3";
+ final public static BigInteger G = new BigInteger("2");
+
+ public static boolean checkErroneousDownload(String downloadedString) {
+ try {
+ if (downloadedString == null || downloadedString.isEmpty() || new JSONObject(downloadedString).has(ProviderAPI.ERRORS) || new JSONObject(downloadedString).has(ProviderAPI.BACKEND_ERROR_KEY)) {
+ return true;
+ } else {
+ return false;
+ }
+ } catch (NullPointerException | JSONException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Treat the input as the MSB representation of a number,
+ * and lop off leading zero elements. For efficiency, the
+ * input is simply returned if no leading zeroes are found.
+ *
+ * @param in array to be trimmed
+ */
+ public static byte[] trim(byte[] in) {
+ if (in.length == 0 || in[0] != 0)
+ return in;
+
+ int len = in.length;
+ int i = 1;
+ while (in[i] == 0 && i < len)
+ ++i;
+ byte[] ret = new byte[len - i];
+ System.arraycopy(in, i, ret, 0, len - i);
+ return ret;
+ }
+
+ public static X509Certificate parseX509CertificateFromString(String certificateString) {
+ java.security.cert.Certificate certificate = null;
+ CertificateFactory cf;
+ try {
+ cf = CertificateFactory.getInstance("X.509");
+
+ certificateString = certificateString.replaceFirst("-----BEGIN CERTIFICATE-----", "").replaceFirst("-----END CERTIFICATE-----", "").trim();
+ byte[] cert_bytes = Base64.decode(certificateString);
+ InputStream caInput = new ByteArrayInputStream(cert_bytes);
+ try {
+ certificate = cf.generateCertificate(caInput);
+ System.out.println("ca=" + ((X509Certificate) certificate).getSubjectDN());
+ } finally {
+ caInput.close();
+ }
+ } catch (NullPointerException | CertificateException | IOException | IllegalArgumentException e) {
+ return null;
+ }
+ return (X509Certificate) certificate;
+ }
+
+ public static RSAPrivateKey parseRsaKeyFromString(String rsaKeyString) {
+ RSAPrivateKey key;
+ 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));
+ key = (RSAPrivateKey) kf.generatePrivate(keySpec);
+ } catch (InvalidKeySpecException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ return null;
+ } catch (NoSuchAlgorithmException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ return null;
+ } catch (NoSuchProviderException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ return null;
+ } catch (NullPointerException e) {
+ e.printStackTrace();
+ return null;
+ }
+
+ return key;
+ }
+
+ private static String byteArrayToHex(byte[] input) {
+ int readBytes = input.length;
+ StringBuffer hexData = new StringBuffer();
+ int onebyte;
+ for (int i = 0; i < readBytes; i++) {
+ onebyte = ((0x000000ff & input[i]) | 0xffffff00);
+ hexData.append(Integer.toHexString(onebyte).substring(6));
+ }
+ return hexData.toString();
+ }
+
+ /**
+ * Calculates the hexadecimal representation of a sha256/sha1 fingerprint of a certificate
+ *
+ * @param certificate
+ * @param encoding
+ * @return
+ * @throws NoSuchAlgorithmException
+ * @throws CertificateEncodingException
+ */
+ @NonNull
+ public static String getFingerprintFromCertificate(X509Certificate certificate, String encoding) throws NoSuchAlgorithmException, CertificateEncodingException /*, UnsupportedEncodingException*/ {
+ byte[] byteArray = MessageDigest.getInstance(encoding).digest(certificate.getEncoded());
+ return byteArrayToHex(byteArray);
+ }
+
+
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/utils/FileHelper.java b/app/src/main/java/se/leap/bitmaskclient/utils/FileHelper.java
new file mode 100644
index 00000000..1c3e1ebb
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/utils/FileHelper.java
@@ -0,0 +1,22 @@
+package se.leap.bitmaskclient.utils;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+
+/**
+ * Created by cyberta on 18.03.18.
+ */
+
+public class FileHelper {
+ public static File createFile(File dir, String fileName) {
+ return new File(dir, fileName);
+ }
+
+ public static void persistFile(File file, String content) throws IOException {
+ FileWriter writer = new FileWriter(file);
+ writer.write(content);
+ writer.close();
+ }
+
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/utils/InputStreamHelper.java b/app/src/main/java/se/leap/bitmaskclient/utils/InputStreamHelper.java
new file mode 100644
index 00000000..87996615
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/utils/InputStreamHelper.java
@@ -0,0 +1,21 @@
+package se.leap.bitmaskclient.utils;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+
+/**
+ * Created by cyberta on 18.03.18.
+ */
+
+public class InputStreamHelper {
+ //allows us to mock FileInputStream
+ public static InputStream getInputStreamFrom(String filePath) throws FileNotFoundException {
+ return new FileInputStream(filePath);
+ }
+
+ public static String loadInputStreamAsString(InputStream is) {
+ java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
+ return s.hasNext() ? s.next() : "";
+ }
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/utils/KeyStoreHelper.java b/app/src/main/java/se/leap/bitmaskclient/utils/KeyStoreHelper.java
new file mode 100644
index 00000000..48d4cbad
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/utils/KeyStoreHelper.java
@@ -0,0 +1,78 @@
+package se.leap.bitmaskclient.utils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+
+/**
+ * Created by cyberta on 18.03.18.
+ */
+
+public class KeyStoreHelper {
+ private static KeyStore trustedKeystore;
+
+ /**
+ * Adds a new X509 certificate given its input stream and its provider name
+ *
+ * @param provider used to store the certificate in the keystore
+ * @param inputStream from which X509 certificate must be generated.
+ */
+ public static void addTrustedCertificate(String provider, InputStream inputStream) {
+ CertificateFactory cf;
+ try {
+ cf = CertificateFactory.getInstance("X.509");
+ X509Certificate cert =
+ (X509Certificate) cf.generateCertificate(inputStream);
+ trustedKeystore.setCertificateEntry(provider, cert);
+ } catch (CertificateException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (KeyStoreException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Adds a new X509 certificate given in its string from and using its provider name
+ *
+ * @param provider used to store the certificate in the keystore
+ * @param certificate
+ */
+ public static void addTrustedCertificate(String provider, String certificate) {
+
+ try {
+ X509Certificate cert = ConfigHelper.parseX509CertificateFromString(certificate);
+ if (trustedKeystore == null) {
+ trustedKeystore = KeyStore.getInstance("BKS");
+ trustedKeystore.load(null);
+ }
+ trustedKeystore.setCertificateEntry(provider, cert);
+ } catch (KeyStoreException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (NoSuchAlgorithmException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (CertificateException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * @return class wide keystore
+ */
+ public static KeyStore getKeystore() {
+ return trustedKeystore;
+ }
+
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java b/app/src/main/java/se/leap/bitmaskclient/utils/PreferenceHelper.java
index aaff9ebc..92f025b2 100644
--- a/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java
+++ b/app/src/main/java/se/leap/bitmaskclient/utils/PreferenceHelper.java
@@ -1,63 +1,23 @@
-/**
- * Copyright (c) 2013 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;
+package se.leap.bitmaskclient.utils;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
-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.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.math.BigInteger;
import java.net.MalformedURLException;
import java.net.URL;
-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 java.util.ArrayList;
-import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
-import java.util.Set;
-import static android.R.attr.name;
+import se.leap.bitmaskclient.Provider;
+
import static se.leap.bitmaskclient.Constants.ALWAYS_ON_SHOW_DIALOG;
import static se.leap.bitmaskclient.Constants.DEFAULT_SHARED_PREFS_BATTERY_SAVER;
import static se.leap.bitmaskclient.Constants.PREFERENCES_APP_VERSION;
@@ -68,195 +28,10 @@ import static se.leap.bitmaskclient.Constants.PROVIDER_VPN_CERTIFICATE;
import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES;
/**
- * Stores constants, and implements auxiliary methods used across all Bitmask Android classes.
- *
- * @author parmegv
- * @author MeanderingCode
+ * Created by cyberta on 18.03.18.
*/
-public class ConfigHelper {
- private static final String TAG = ConfigHelper.class.getName();
- private static KeyStore keystore_trusted;
-
- final public static String NG_1024 =
- "eeaf0ab9adb38dd69c33f80afa8fc5e86072618775ff3c0b9ea2314c9c256576d674df7496ea81d3383b4813d692c6e0e0d5d8e250b98be48e495c1d6089dad15dc7d7b46154d6b6ce8ef4ad69b15d4982559b297bcf1885c529f566660e57ec68edbc3c05726cc02fd4cbf4976eaa9afd5138fe8376435b9fc61d2fc0eb06e3";
- final public static BigInteger G = new BigInteger("2");
-
- public static boolean checkErroneousDownload(String downloadedString) {
- try {
- if (downloadedString == null || downloadedString.isEmpty() || new JSONObject(downloadedString).has(ProviderAPI.ERRORS) || new JSONObject(downloadedString).has(ProviderAPI.BACKEND_ERROR_KEY)) {
- return true;
- } else {
- return false;
- }
- } catch (NullPointerException | JSONException e) {
- return false;
- }
- }
-
- /**
- * Treat the input as the MSB representation of a number,
- * and lop off leading zero elements. For efficiency, the
- * input is simply returned if no leading zeroes are found.
- *
- * @param in array to be trimmed
- */
- public static byte[] trim(byte[] in) {
- if (in.length == 0 || in[0] != 0)
- return in;
-
- int len = in.length;
- int i = 1;
- while (in[i] == 0 && i < len)
- ++i;
- byte[] ret = new byte[len - i];
- System.arraycopy(in, i, ret, 0, len - i);
- return ret;
- }
-
- public static X509Certificate parseX509CertificateFromString(String certificateString) {
- java.security.cert.Certificate certificate = null;
- CertificateFactory cf;
- try {
- cf = CertificateFactory.getInstance("X.509");
-
- certificateString = certificateString.replaceFirst("-----BEGIN CERTIFICATE-----", "").replaceFirst("-----END CERTIFICATE-----", "").trim();
- byte[] cert_bytes = Base64.decode(certificateString);
- InputStream caInput = new ByteArrayInputStream(cert_bytes);
- try {
- certificate = cf.generateCertificate(caInput);
- System.out.println("ca=" + ((X509Certificate) certificate).getSubjectDN());
- } finally {
- caInput.close();
- }
- } catch (NullPointerException | CertificateException | IOException | IllegalArgumentException e) {
- return null;
- }
- return (X509Certificate) certificate;
- }
-
- public static String loadInputStreamAsString(java.io.InputStream is) {
- java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
- return s.hasNext() ? s.next() : "";
- }
-
- //allows us to mock FileInputStream
- public static InputStream getInputStreamFrom(String filePath) throws FileNotFoundException {
- return new FileInputStream(filePath);
- }
-
- protected static RSAPrivateKey parseRsaKeyFromString(String rsaKeyString) {
- RSAPrivateKey key;
- 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));
- key = (RSAPrivateKey) kf.generatePrivate(keySpec);
- } catch (InvalidKeySpecException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- return null;
- } catch (NoSuchAlgorithmException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- return null;
- } catch (NoSuchProviderException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- return null;
- } catch (NullPointerException e) {
- e.printStackTrace();
- return null;
- }
-
- return key;
- }
-
- private static String byteArrayToHex(byte[] input) {
- int readBytes = input.length;
- StringBuffer hexData = new StringBuffer();
- int onebyte;
- for (int i = 0; i < readBytes; i++) {
- onebyte = ((0x000000ff & input[i]) | 0xffffff00);
- hexData.append(Integer.toHexString(onebyte).substring(6));
- }
- return hexData.toString();
- }
-
- /**
- * Calculates the hexadecimal representation of a sha256/sha1 fingerprint of a certificate
- *
- * @param certificate
- * @param encoding
- * @return
- * @throws NoSuchAlgorithmException
- * @throws CertificateEncodingException
- */
- @NonNull
- public static String getFingerprintFromCertificate(X509Certificate certificate, String encoding) throws NoSuchAlgorithmException, CertificateEncodingException /*, UnsupportedEncodingException*/ {
- byte[] byteArray = MessageDigest.getInstance(encoding).digest(certificate.getEncoded());
- return byteArrayToHex(byteArray);
- }
-
- /**
- * Adds a new X509 certificate given its input stream and its provider name
- *
- * @param provider used to store the certificate in the keystore
- * @param inputStream from which X509 certificate must be generated.
- */
- public static void addTrustedCertificate(String provider, InputStream inputStream) {
- CertificateFactory cf;
- try {
- cf = CertificateFactory.getInstance("X.509");
- X509Certificate cert =
- (X509Certificate) cf.generateCertificate(inputStream);
- keystore_trusted.setCertificateEntry(provider, cert);
- } catch (CertificateException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (KeyStoreException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
-
- /**
- * Adds a new X509 certificate given in its string from and using its provider name
- *
- * @param provider used to store the certificate in the keystore
- * @param certificate
- */
- public static void addTrustedCertificate(String provider, String certificate) {
-
- try {
- X509Certificate cert = ConfigHelper.parseX509CertificateFromString(certificate);
- if (keystore_trusted == null) {
- keystore_trusted = KeyStore.getInstance("BKS");
- keystore_trusted.load(null);
- }
- keystore_trusted.setCertificateEntry(provider, cert);
- } catch (KeyStoreException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (NoSuchAlgorithmException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (CertificateException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
-
- /**
- * @return class wide keystore
- */
- public static KeyStore getKeystore() {
- return keystore_trusted;
- }
-
+public class PreferenceHelper {
public static boolean providerInSharedPreferences(@NonNull SharedPreferences preferences) {
return preferences.getBoolean(PROVIDER_CONFIGURED, false);
}
diff --git a/app/src/production/java/se/leap/bitmaskclient/ProviderApiManager.java b/app/src/production/java/se/leap/bitmaskclient/ProviderApiManager.java
index 885d69db..b1afa6d3 100644
--- a/app/src/production/java/se/leap/bitmaskclient/ProviderApiManager.java
+++ b/app/src/production/java/se/leap/bitmaskclient/ProviderApiManager.java
@@ -31,6 +31,7 @@ import java.util.List;
import okhttp3.OkHttpClient;
import se.leap.bitmaskclient.eip.EIP;
+import se.leap.bitmaskclient.utils.ConfigHelper;
import static android.text.TextUtils.isEmpty;
import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_KEY;
diff --git a/app/src/test/java/se/leap/bitmaskclient/ConfigHelperTest.java b/app/src/test/java/se/leap/bitmaskclient/PreferenceHelperTest.java
index 2c7848bc..d49fa08c 100644
--- a/app/src/test/java/se/leap/bitmaskclient/ConfigHelperTest.java
+++ b/app/src/test/java/se/leap/bitmaskclient/PreferenceHelperTest.java
@@ -6,6 +6,8 @@ import org.junit.Before;
import org.junit.Test;
import se.leap.bitmaskclient.testutils.MockSharedPreferences;
+import se.leap.bitmaskclient.utils.ConfigHelper;
+import se.leap.bitmaskclient.utils.PreferenceHelper;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -14,11 +16,13 @@ import static se.leap.bitmaskclient.Constants.PROVIDER_EIP_DEFINITION;
import static se.leap.bitmaskclient.Constants.PROVIDER_PRIVATE_KEY;
import static se.leap.bitmaskclient.Constants.PROVIDER_VPN_CERTIFICATE;
import static se.leap.bitmaskclient.testutils.TestSetupHelper.getInputAsString;
+import static se.leap.bitmaskclient.utils.PreferenceHelper.getSavedProviderFromSharedPreferences;
+import static se.leap.bitmaskclient.utils.PreferenceHelper.providerInSharedPreferences;
/**
* Created by cyberta on 17.01.18.
*/
-public class ConfigHelperTest {
+public class PreferenceHelperTest {
private SharedPreferences mockPreferences;
@@ -30,18 +34,18 @@ public class ConfigHelperTest {
@Test
public void providerInSharedPreferences_notInPreferences_returnsFalse() throws Exception {
- assertFalse(ConfigHelper.providerInSharedPreferences(mockPreferences));
+ assertFalse(providerInSharedPreferences(mockPreferences));
}
@Test
public void providerInSharedPreferences_inPreferences_returnsTrue() throws Exception {
mockPreferences.edit().putBoolean(PROVIDER_CONFIGURED, true).apply();
- assertTrue(ConfigHelper.providerInSharedPreferences(mockPreferences));
+ assertTrue(providerInSharedPreferences(mockPreferences));
}
@Test
public void getSavedProviderFromSharedPreferences_notInPreferences_returnsDefaultProvider() throws Exception {
- Provider provider = ConfigHelper.getSavedProviderFromSharedPreferences(mockPreferences);
+ Provider provider = getSavedProviderFromSharedPreferences(mockPreferences);
assertFalse(provider.isConfigured());
}
@@ -55,7 +59,7 @@ public class ConfigHelperTest {
.putString(PROVIDER_VPN_CERTIFICATE, getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.vpn_cert.pem")))
.putString(PROVIDER_PRIVATE_KEY, getInputAsString(getClass().getClassLoader().getResourceAsStream("private_rsa_key.pem")))
.apply();
- Provider provider = ConfigHelper.getSavedProviderFromSharedPreferences(mockPreferences);
+ Provider provider = getSavedProviderFromSharedPreferences(mockPreferences);
assertTrue(provider.isConfigured());
}
diff --git a/app/src/test/java/se/leap/bitmaskclient/ProviderManagerTest.java b/app/src/test/java/se/leap/bitmaskclient/ProviderManagerTest.java
new file mode 100644
index 00000000..1914f989
--- /dev/null
+++ b/app/src/test/java/se/leap/bitmaskclient/ProviderManagerTest.java
@@ -0,0 +1,189 @@
+package se.leap.bitmaskclient;
+
+import android.content.res.AssetManager;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.ArrayList;
+
+import se.leap.bitmaskclient.utils.ConfigHelper;
+import se.leap.bitmaskclient.utils.FileHelper;
+import se.leap.bitmaskclient.utils.InputStreamHelper;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.powermock.api.mockito.PowerMockito.verifyStatic;
+import static se.leap.bitmaskclient.testutils.MockHelper.mockFileHelper;
+import static se.leap.bitmaskclient.testutils.MockHelper.mockInputStreamHelper;
+
+/**
+ * Created by cyberta on 20.02.18.
+ */
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({ConfigHelper.class, FileHelper.class, InputStreamHelper.class})
+public class ProviderManagerTest {
+
+ @Mock
+ private AssetManager assetManager;
+ @Mock
+ private File file;
+ private ProviderManager providerManager;
+
+ @Before
+ public void setup() throws Exception {
+ //mock assetManager methods
+ //--------------------------
+ when(assetManager.open(anyString())).thenAnswer(new Answer<InputStream>() {
+ @Override
+ public InputStream answer(InvocationOnMock invocation) throws Throwable {
+ String filename = "preconfigured/" + invocation.getArguments()[0];
+ return getClass().getClassLoader().getResourceAsStream(filename);
+ }
+ });
+ when(assetManager.list(anyString())).thenAnswer(new Answer<String[]>() {
+ @Override
+ public String[] answer(InvocationOnMock invocation) throws Throwable {
+ String path = (String) invocation.getArguments()[0];
+ if ("urls".equals(path)) {
+ String[] preconfiguredUrls = new String[3];
+ preconfiguredUrls[0] = "calyx.net.url";
+ preconfiguredUrls[1] = "demo.bitmask.net.url";
+ preconfiguredUrls[2] = "riseup.net.url";
+ return preconfiguredUrls;
+ } else
+ throw new IllegalArgumentException("You need to implement the expected path manually!");
+ }
+ });
+
+ //mock File methods
+ //------------------
+ when(file.isDirectory()).thenReturn(true);
+
+ ArrayList<String> mockedCustomProviderList = new ArrayList<>();
+ mockedCustomProviderList.add("leapcolombia.json");
+ String[] mockedCustomProviderArray = new String[mockedCustomProviderList.size()];
+ mockedCustomProviderArray = mockedCustomProviderList.toArray(mockedCustomProviderArray);
+ when(file.list()).thenReturn(mockedCustomProviderArray);
+
+ when(file.getAbsolutePath()).thenReturn("externalDir");
+ when(file.getPath()).thenReturn("externalDir");
+ mockFileHelper(file);
+
+ // mock inputStream
+ //-----------------------------------
+ mockInputStreamHelper();
+
+ }
+
+ @After
+ public void tearDown() {
+ ProviderManager.reset();
+ }
+
+ @Test
+ public void testSize_has5ProvidersWithCurrentTestSetup() {
+ providerManager = ProviderManager.getInstance(assetManager, file);
+ assertEquals("3 preconfigured, 1 custom provider, 1 dummy provider", 5, providerManager.size());
+ }
+
+ @Test
+ public void testAdd_newCustomProviderThatIsNotPartOfDefaultNorCustomList_returnTrue() throws Exception {
+ providerManager = ProviderManager.getInstance(assetManager, file);
+ Provider customProvider = new Provider("https://anewprovider.org");
+ assertTrue("custom provider added: ", providerManager.add(customProvider));
+ assertEquals("3 preconfigured, 2 custom providers, 1 dummy provider", 6, providerManager.providers().size());
+ }
+
+ @Test
+ public void testAdd_newCustomProviderThatIsNotPartOfDefaultButOfCustomList_returnFalse() throws Exception {
+ providerManager = ProviderManager.getInstance(assetManager, file);
+ Provider customProvider = new Provider("https://leapcolombia.org");
+ assertFalse("custom provider added: ", providerManager.add(customProvider));
+ assertEquals("3 preconfigured, 1 custom provider, 1 dummy provider", 5, providerManager.providers().size());
+ }
+
+ @Test
+ public void testAdd_newCustomProviderThatIsPartOfDefaultButNotOfCustomList_returnFalse() throws Exception {
+ providerManager = ProviderManager.getInstance(assetManager, file);
+ Provider customProvider = new Provider("https://demo.bitmask.net");
+ assertFalse("custom provider added: ", providerManager.add(customProvider));
+ assertEquals("3 preconfigured, 1 custom provider, 1 dummy provider", 5, providerManager.providers().size());
+ }
+
+ @Test
+ public void testRemove_ProviderIsPartOfDefaultButNotCustomList_returnsFalse() throws Exception {
+ providerManager = ProviderManager.getInstance(assetManager, file);
+ Provider customProvider = new Provider("https://demo.bitmask.net");
+ assertFalse("custom provider not removed: ", providerManager.remove(customProvider));
+ assertEquals("3 preconfigured, 1 custom provider, 1 dummy provider", 5, providerManager.providers().size());
+ }
+
+ @Test
+ public void testRemove_ProviderIsNotPartOfDefaultButOfCustomList_returnsTrue() throws Exception {
+ providerManager = ProviderManager.getInstance(assetManager, file);
+ Provider customProvider = new Provider("https://leapcolombia.org");
+ assertTrue("custom provider not removed: ", providerManager.remove(customProvider));
+ assertEquals("3 preconfigured, 0 custom providers, 1 dummy provider", 4, providerManager.providers().size());
+ }
+
+ @Test
+ public void testRemove_ProviderIsNotPartOfDefaultNorOfCustomList_returnsFalse() throws Exception {
+ providerManager = ProviderManager.getInstance(assetManager, file);
+ Provider customProvider = new Provider("https://anotherprovider.org");
+ assertFalse("custom provider not removed: ", providerManager.remove(customProvider));
+ assertEquals("3 preconfigured, 1 custom providers, 1 dummy provider", 5, providerManager.providers().size());
+ }
+
+ @Test
+ public void testClear_ProvidersListHasOnlyDummyProvider() throws Exception {
+ providerManager = ProviderManager.getInstance(assetManager, file);
+ providerManager.clear();
+ assertEquals("1 providers", 1, providerManager.providers().size());
+ assertEquals("provider is dummy element", "https://example.net", providerManager.get(0).getMainUrlString());
+ }
+
+ @Test
+ public void testSaveCustomProvidersToFile_CustomProviderDeleted_deletesFromDir() throws Exception {
+ when(file.exists()).thenReturn(true);
+ providerManager = ProviderManager.getInstance(assetManager, file);
+ //leapcolombia is mocked custom provider from setup
+ Provider customProvider = new Provider("https://leapcolombia.org");
+ providerManager.remove(customProvider);
+ providerManager.saveCustomProvidersToFile();
+ verify(file, times(1)).delete();
+ }
+
+
+ @Test
+ public void testSaveCustomProvidersToFile_newCustomProviders_persistNew() throws Exception {
+ when(file.list()).thenReturn(new String[0]);
+ when(file.exists()).thenReturn(false);
+ providerManager = ProviderManager.getInstance(assetManager, file);
+ Provider customProvider = new Provider("https://anotherprovider.org");
+ Provider secondCustomProvider = new Provider("https://yetanotherprovider.org");
+ providerManager.add(customProvider);
+ providerManager.add(secondCustomProvider);
+ providerManager.saveCustomProvidersToFile();
+
+ verifyStatic(FileHelper.class, times(2));
+ FileHelper.persistFile(any(File.class), anyString());
+ }
+
+
+} \ No newline at end of file
diff --git a/app/src/test/java/se/leap/bitmaskclient/ProviderTest.java b/app/src/test/java/se/leap/bitmaskclient/ProviderTest.java
index 495d5b3f..a141edec 100644
--- a/app/src/test/java/se/leap/bitmaskclient/ProviderTest.java
+++ b/app/src/test/java/se/leap/bitmaskclient/ProviderTest.java
@@ -1,9 +1,7 @@
package se.leap.bitmaskclient;
-import org.json.JSONException;
import org.junit.Test;
-import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
diff --git a/app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java b/app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java
index 4842d170..7283968b 100644
--- a/app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java
+++ b/app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java
@@ -38,13 +38,15 @@ import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateEncodingException;
-import se.leap.bitmaskclient.ConfigHelper;
+import se.leap.bitmaskclient.testutils.MockHelper;
+import se.leap.bitmaskclient.utils.ConfigHelper;
import se.leap.bitmaskclient.Provider;
import se.leap.bitmaskclient.ProviderAPI;
import se.leap.bitmaskclient.ProviderApiConnector;
import se.leap.bitmaskclient.ProviderApiManager;
import se.leap.bitmaskclient.ProviderApiManagerBase;
import se.leap.bitmaskclient.testutils.MockSharedPreferences;
+import se.leap.bitmaskclient.utils.PreferenceHelper;
import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_KEY;
import static se.leap.bitmaskclient.Constants.PROVIDER_KEY;
@@ -58,6 +60,7 @@ import static se.leap.bitmaskclient.testutils.MockHelper.mockClientGenerator;
import static se.leap.bitmaskclient.testutils.MockHelper.mockConfigHelper;
import static se.leap.bitmaskclient.testutils.MockHelper.mockFingerprintForCertificate;
import static se.leap.bitmaskclient.testutils.MockHelper.mockIntent;
+import static se.leap.bitmaskclient.testutils.MockHelper.mockPreferenceHelper;
import static se.leap.bitmaskclient.testutils.MockHelper.mockProviderApiConnector;
import static se.leap.bitmaskclient.testutils.MockHelper.mockResources;
import static se.leap.bitmaskclient.testutils.MockHelper.mockResultReceiver;
@@ -72,7 +75,7 @@ import static se.leap.bitmaskclient.testutils.TestSetupHelper.getProvider;
*/
@RunWith(PowerMockRunner.class)
-@PrepareForTest({ProviderApiManager.class, TextUtils.class, ConfigHelper.class, ProviderApiConnector.class})
+@PrepareForTest({ProviderApiManager.class, TextUtils.class, ConfigHelper.class, ProviderApiConnector.class, PreferenceHelper.class})
public class ProviderApiManagerTest {
private SharedPreferences mockPreferences;
@@ -172,8 +175,8 @@ public class ProviderApiManagerTest {
@Test
public void test_handleIntentSetupProvider_happyPath_storedProviderAndCAFromPreviousSetup() throws IOException, CertificateEncodingException, NoSuchAlgorithmException, JSONException {
Provider provider = new Provider("https://riseup.net");
- mockConfigHelper("a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494", getConfiguredProvider());
-
+ mockPreferenceHelper(getConfiguredProvider());
+ mockConfigHelper("a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494");
mockProviderApiConnector(NO_ERROR);
mockPreferences.edit().putString(Provider.KEY + ".riseup.net", getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.json"))).apply();
mockPreferences.edit().putString(Provider.CA_CERT + ".riseup.net", getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.pem"))).apply();
@@ -238,7 +241,8 @@ public class ProviderApiManagerTest {
@Test
public void test_handleIntentSetupProvider_storedProviderAndCAFromPreviousSetup_failedPinning() throws IOException, CertificateEncodingException, NoSuchAlgorithmException, JSONException {
Provider provider = new Provider("https://riseup.net");
- mockConfigHelper("a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29495", getConfiguredProvider());
+ mockPreferenceHelper(getConfiguredProvider());
+ mockConfigHelper("a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29495");
mockProviderApiConnector(NO_ERROR);
mockPreferences.edit().putString(Provider.KEY + ".riseup.net", getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.json"))).apply();
@@ -308,8 +312,8 @@ public class ProviderApiManagerTest {
@Test
public void test_handleIntentSetupProvider_preseededProviderAndCA_ValidCertificateButUpdatedCertificateOnServerSide() throws IOException, CertificateEncodingException, NoSuchAlgorithmException, JSONException {
Provider provider = getConfiguredProvider();
-
- mockConfigHelper("a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494", getConfiguredProvider());
+ mockPreferenceHelper(provider);
+ mockConfigHelper("a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494");
mockProviderApiConnector(ERROR_CASE_UPDATED_CERTIFICATE);
providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback());
@@ -331,8 +335,8 @@ public class ProviderApiManagerTest {
@Test
public void test_handleIntentSetupProvider_storedProviderAndCAFromPreviousSetup_ValidCertificateButUpdatedCertificateOnServerSide() throws IOException, CertificateEncodingException, NoSuchAlgorithmException, JSONException {
Provider provider = new Provider("https://riseup.net");
-
- mockConfigHelper("a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494", getConfiguredProvider());
+ mockPreferenceHelper(getConfiguredProvider());
+ mockConfigHelper("a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494");
mockProviderApiConnector(ERROR_CASE_UPDATED_CERTIFICATE);
mockPreferences.edit().putString(Provider.KEY + ".riseup.net", getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.json"))).apply();
mockPreferences.edit().putString(Provider.CA_CERT + ".riseup.net", getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.pem"))).apply();
diff --git a/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java b/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java
index d85b050f..d68296a8 100644
--- a/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java
+++ b/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java
@@ -14,6 +14,7 @@ import org.json.JSONObject;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
+import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
@@ -29,12 +30,15 @@ import java.util.Map;
import java.util.Set;
import okhttp3.OkHttpClient;
-import se.leap.bitmaskclient.ConfigHelper;
import se.leap.bitmaskclient.OkHttpClientGenerator;
import se.leap.bitmaskclient.Provider;
import se.leap.bitmaskclient.R;
import se.leap.bitmaskclient.testutils.BackendMockResponses.BackendMockProvider;
import se.leap.bitmaskclient.testutils.matchers.BundleMatcher;
+import se.leap.bitmaskclient.utils.ConfigHelper;
+import se.leap.bitmaskclient.utils.FileHelper;
+import se.leap.bitmaskclient.utils.InputStreamHelper;
+import se.leap.bitmaskclient.utils.PreferenceHelper;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
@@ -49,6 +53,8 @@ import static org.mockito.Mockito.when;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
import static se.leap.bitmaskclient.Constants.PROVIDER_PRIVATE_KEY;
import static se.leap.bitmaskclient.Constants.PROVIDER_VPN_CERTIFICATE;
+import static se.leap.bitmaskclient.utils.FileHelper.createFile;
+import static se.leap.bitmaskclient.utils.PreferenceHelper.getFromPersistedProvider;
/**
* Created by cyberta on 29.01.18.
@@ -343,22 +349,36 @@ public class MockHelper {
return resultReceiver;
}
- public static void mockConfigHelperForFileInputStream() throws FileNotFoundException {
- mockStatic(ConfigHelper.class);
- when(ConfigHelper.loadInputStreamAsString(any(InputStream.class))).thenCallRealMethod();
- when(ConfigHelper.getInputStreamFrom(anyString())).thenAnswer(new Answer<InputStream>() {
+ public static void mockInputStreamHelper() throws FileNotFoundException {
+ mockStatic(InputStreamHelper.class);
+ when(InputStreamHelper.loadInputStreamAsString(any(InputStream.class))).thenCallRealMethod();
+ when(InputStreamHelper.getInputStreamFrom(anyString())).thenAnswer(new Answer<InputStream>() {
@Override
public InputStream answer(InvocationOnMock invocation) throws Throwable {
String filename = (String) invocation.getArguments()[0];
return getClass().getClassLoader().getResourceAsStream(filename);
}
});
+
+ }
+
+ public static void mockFileHelper(final File mockedFile) throws FileNotFoundException {
+ mockStatic(FileHelper.class);
+ when(createFile(any(File.class), anyString())).thenReturn(mockedFile);
}
- public static void mockConfigHelper(String mockedFingerprint, final Provider providerFromPrefs) throws CertificateEncodingException, NoSuchAlgorithmException {
+ public static void mockConfigHelper(String mockedFingerprint) throws CertificateEncodingException, NoSuchAlgorithmException {
// FIXME use MockSharedPreferences instead of provider
mockStatic(ConfigHelper.class);
- when(ConfigHelper.getFromPersistedProvider(anyString(), anyString(), any(SharedPreferences.class))).thenAnswer(new Answer<String>() {
+ when(ConfigHelper.getFingerprintFromCertificate(any(X509Certificate.class), anyString())).thenReturn(mockedFingerprint);
+ when(ConfigHelper.checkErroneousDownload(anyString())).thenCallRealMethod();
+ when(ConfigHelper.parseX509CertificateFromString(anyString())).thenCallRealMethod();
+ }
+
+ public static void mockPreferenceHelper(final Provider providerFromPrefs) {
+ // FIXME use MockSharedPreferences instead of provider
+ mockStatic(PreferenceHelper.class);
+ when(getFromPersistedProvider(anyString(), anyString(), any(SharedPreferences.class))).thenAnswer(new Answer<String>() {
@Override
public String answer(InvocationOnMock invocation) throws Throwable {
String key = (String) invocation.getArguments()[0];
@@ -375,11 +395,8 @@ public class MockHelper {
return null;
}
});
- when(ConfigHelper.getFingerprintFromCertificate(any(X509Certificate.class), anyString())).thenReturn(mockedFingerprint);
- when(ConfigHelper.checkErroneousDownload(anyString())).thenCallRealMethod();
- when(ConfigHelper.parseX509CertificateFromString(anyString())).thenCallRealMethod();
- when(ConfigHelper.loadInputStreamAsString(any(InputStream.class))).thenCallRealMethod();
}
+
public static void mockFingerprintForCertificate(String mockedFingerprint) throws CertificateEncodingException, NoSuchAlgorithmException {
mockStatic(ConfigHelper.class);
when(ConfigHelper.getFingerprintFromCertificate(any(X509Certificate.class), anyString())).thenReturn(mockedFingerprint);