summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorcyBerta <cyberta@riseup.net>2024-11-07 19:13:53 +0100
committercyberta <cyberta@riseup.net>2024-12-11 00:09:34 +0000
commitbbf98bb94648bb9c7a8aebe866a7bc61a79c14ca (patch)
tree0147212802abbc01c64b4c36c26ff1464a3719a2 /app
parentab4c3397c501e059849d16f05fa211d5ebd3c4db (diff)
update bitmask-core API, store v5 provider details
Diffstat (limited to 'app')
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/models/Constants.java4
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/models/Provider.java108
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java43
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java75
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerV3.java52
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerV5.java66
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java31
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/providersetup/ProviderManagerTest.java2
8 files changed, 254 insertions, 127 deletions
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/models/Constants.java b/app/src/main/java/se/leap/bitmaskclient/base/models/Constants.java
index b49061c9..5a5d1d6e 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/models/Constants.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/models/Constants.java
@@ -126,6 +126,10 @@ public interface Constants {
String PROVIDER_MOTD_HASHES = "Constants.PROVIDER_MOTD_HASHES";
String PROVIDER_MOTD_LAST_SEEN = "Constants.PROVIDER_MOTD_LAST_SEEN";
String PROVIDER_MOTD_LAST_UPDATED = "Constants.PROVIDER_MOTD_LAST_UPDATED";
+ String PROVIDER_MODELS_PROVIDER = "Constants.PROVIDER_MODELS_PROVIDER";
+ String PROVIDER_MODELS_EIPSERVICE = "Constants.PROVIDER_MDOELS_EIPSERVICE";
+ String PROVIDER_MODELS_GATEWAYS = "Constants.PROVIDER_MODELS_GATEWAYS";
+ String PROVIDER_MODELS_BRIDGES = "Constants.PROVIDER_MODELS_BRIDGES";
////////////////////////////////////////////////
// PRESHIPPED PROVIDER CONFIG
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/models/Provider.java b/app/src/main/java/se/leap/bitmaskclient/base/models/Provider.java
index 51183e5e..cdec9e7a 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/models/Provider.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/models/Provider.java
@@ -39,12 +39,14 @@ import android.os.Parcelable;
import androidx.annotation.NonNull;
import com.google.gson.Gson;
+import com.google.gson.JsonSyntaxException;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.net.MalformedURLException;
+import java.net.URISyntaxException;
import java.net.URL;
import java.security.PrivateKey;
import java.util.ArrayList;
@@ -145,7 +147,7 @@ public final class Provider implements Parcelable {
setGeoipUrl(geoipUrl);
}
- public static Provider createCustomProvider(String mainUrl, String domain) {
+ public static Provider createCustomProvider(String mainUrl, String domain, Introducer introducer) {
Provider p = new Provider(mainUrl);
p.domain = domain;
return p;
@@ -189,29 +191,93 @@ public final class Provider implements Parcelable {
}
};
- public void setBridges(ModelsBridge[] bridges) {
- this.modelsBridges = bridges;
+ public void setBridges(String bridgesJson) {
+ if (bridgesJson == null) {
+ this.modelsBridges = null;
+ return;
+ }
+ try {
+ this.modelsBridges = JSON.createGson().create().fromJson(bridgesJson, ModelsBridge[].class);
+ } catch (JsonSyntaxException e) {
+ e.printStackTrace();
+ }
}
public ModelsBridge[] getBridges() {
return this.modelsBridges;
}
- public void setGateways(ModelsGateway[] gateways) {
- this.modelsGateways = gateways;
+
+ public String getBridgesJson() {
+ return getJsonString(modelsBridges);
+ }
+
+ public void setGateways(String gatewaysJson) {
+ if (gatewaysJson == null) {
+ this.modelsGateways = null;
+ return;
+ }
+ try {
+ this.modelsGateways = JSON.createGson().create().fromJson(gatewaysJson, ModelsGateway[].class);
+ } catch (JsonSyntaxException e) {
+ e.printStackTrace();
+ }
}
public ModelsGateway[] getGateways() {
return modelsGateways;
}
- public void setService(ModelsEIPService service) {
- this.modelsEIPService = service;
+ public String getGatewaysJson() {
+ return getJsonString(modelsGateways);
}
+ public void setService(String serviceJson) {
+ if (serviceJson == null) {
+ this.modelsEIPService = null;
+ return;
+ }
+ try {
+ this.modelsEIPService = JSON.createGson().create().fromJson(serviceJson, ModelsEIPService.class);
+ } catch (JsonSyntaxException e) {
+ e.printStackTrace();
+ }
+ }
public ModelsEIPService getService() {
return this.modelsEIPService;
}
+ public String getServiceJson() {
+ return getJsonString(modelsEIPService);
+ }
+
+ public void setModelsProvider(String json) {
+ if (json == null) {
+ this.modelsProvider = null;
+ return;
+ }
+ try {
+ this.modelsProvider = JSON.createGson().create().fromJson(json, ModelsProvider.class);
+ } catch (JsonSyntaxException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public String getModelsProviderJson() {
+ return getJsonString(modelsProvider);
+ }
+
+ private String getJsonString(Object model) {
+ if (model == null) {
+ return null;
+ }
+ try {
+ return JSON.createGson().create().toJson(model);
+ } catch (JsonSyntaxException e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
public boolean isConfigured() {
if (apiVersion < 5) {
return !mainUrl.isEmpty() &&
@@ -583,23 +649,10 @@ public final class Provider implements Parcelable {
this.shouldUpdateVpnCertificate = in.readInt() == 0;
this.introducer = in.readParcelable(Introducer.class.getClassLoader());
if (this.apiVersion == 5) {
- Gson gson = JSON.createGson().create();
- tmpString = in.readString();
- if (!tmpString.isEmpty()) {
- this.setModelsProvider(gson.fromJson(tmpString, ModelsProvider.class));
- }
- tmpString = in.readString();
- if (!tmpString.isEmpty()) {
- this.setService(gson.fromJson(tmpString, ModelsEIPService.class));
- }
- tmpString = in.readString();
- if (!tmpString.isEmpty()) {
- this.setBridges(gson.fromJson(tmpString, ModelsBridge[].class));
- }
- tmpString = in.readString();
- if (!tmpString.isEmpty()) {
- this.setGateways(gson.fromJson(tmpString, ModelsGateway[].class));
- }
+ this.setModelsProvider(in.readString());
+ this.setService(in.readString());
+ this.setBridges(in.readString());
+ this.setGateways(in.readString());
}
} catch (MalformedURLException | JSONException e) {
e.printStackTrace();
@@ -904,6 +957,10 @@ public final class Provider implements Parcelable {
return introducer;
}
+ public void setIntroducer(String introducerUrl) throws URISyntaxException {
+ this.introducer = Introducer.fromUrl(introducerUrl);
+ }
+
/**
* resets everything except the main url, the providerIp and the geoip
* service url (currently preseeded)
@@ -926,7 +983,4 @@ public final class Provider implements Parcelable {
lastEipServiceUpdate = 0L;
}
- public void setModelsProvider(ModelsProvider p) {
- this.modelsProvider = p;
- }
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java
index bb342f0f..5cc3ff70 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java
@@ -31,6 +31,10 @@ import static se.leap.bitmaskclient.base.models.Constants.PREFERRED_CITY;
import static se.leap.bitmaskclient.base.models.Constants.PREFER_UDP;
import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_CONFIGURED;
import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_EIP_DEFINITION;
+import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_MODELS_BRIDGES;
+import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_MODELS_EIPSERVICE;
+import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_MODELS_GATEWAYS;
+import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_MODELS_PROVIDER;
import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_MOTD;
import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_MOTD_HASHES;
import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_MOTD_LAST_SEEN;
@@ -60,6 +64,8 @@ import androidx.annotation.WorkerThread;
import androidx.security.crypto.EncryptedSharedPreferences;
import androidx.security.crypto.MasterKey;
+import com.google.gson.Gson;
+
import org.json.JSONException;
import org.json.JSONObject;
@@ -74,7 +80,10 @@ import java.util.Set;
import de.blinkt.openvpn.VpnProfile;
import de.blinkt.openvpn.core.NativeUtils;
+import io.swagger.client.JSON;
+import mobile.BitmaskMobile;
import se.leap.bitmaskclient.BuildConfig;
+import se.leap.bitmaskclient.base.models.Introducer;
import se.leap.bitmaskclient.base.models.Provider;
import se.leap.bitmaskclient.tor.TorStatusObservable;
@@ -156,7 +165,14 @@ public class PreferenceHelper {
provider.setLastMotdSeen(preferences.getLong(PROVIDER_MOTD_LAST_SEEN, 0L));
provider.setLastMotdUpdate(preferences.getLong(PROVIDER_MOTD_LAST_UPDATED, 0L));
provider.setMotdLastSeenHashes(preferences.getStringSet(PROVIDER_MOTD_HASHES, new HashSet<>()));
- } catch (MalformedURLException | JSONException e) {
+ provider.setModelsProvider(preferences.getString(PROVIDER_MODELS_PROVIDER, null));
+ provider.setService(preferences.getString(PROVIDER_MODELS_EIPSERVICE, null));
+ provider.setBridges(preferences.getString(PROVIDER_MODELS_BRIDGES, null));
+ provider.setGateways(preferences.getString(PROVIDER_MODELS_GATEWAYS, null));
+ BitmaskMobile bm = new BitmaskMobile(new SharedPreferenceStore());
+ provider.setIntroducer(bm.getIntroducerURLByDomain(provider.getDomain()));
+
+ } catch (Exception e) {
e.printStackTrace();
}
}
@@ -201,12 +217,23 @@ public class PreferenceHelper {
public static HashMap<String, Provider> getCustomProviders() {
Set<String> providerDomains = getCustomProviderDomains();
HashMap<String, Provider> customProviders = new HashMap<>();
- for (String domain : providerDomains) {
- String mainURL = preferences.getString(Provider.MAIN_URL + "." + domain, null);
- if (mainURL != null) {
- customProviders.put(mainURL, Provider.createCustomProvider(mainURL, domain));
+ if (providerDomains.size() > 0) {
+ BitmaskMobile bm = new BitmaskMobile(new PreferenceHelper.SharedPreferenceStore());
+ for (String domain : providerDomains) {
+ String mainURL = preferences.getString(Provider.MAIN_URL + "." + domain, null);
+ if (mainURL != null) {
+ Introducer introducer = null;
+ try {
+ introducer = Introducer.fromUrl(bm.getIntroducerURLByDomain(domain));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ customProviders.put(mainURL, Provider.createCustomProvider(mainURL, domain, introducer));
+ }
}
+
}
+
return customProviders;
}
@@ -254,7 +281,11 @@ public class PreferenceHelper {
putString(PROVIDER_MOTD, provider.getMotdJsonString()).
putStringSet(PROVIDER_MOTD_HASHES, provider.getMotdLastSeenHashes()).
putLong(PROVIDER_MOTD_LAST_SEEN, provider.getLastMotdSeen()).
- putLong(PROVIDER_MOTD_LAST_UPDATED, provider.getLastMotdUpdate());
+ putLong(PROVIDER_MOTD_LAST_UPDATED, provider.getLastMotdUpdate()).
+ putString(PROVIDER_MODELS_GATEWAYS, provider.getGatewaysJson()).
+ putString(PROVIDER_MODELS_BRIDGES, provider.getBridgesJson()).
+ putString(PROVIDER_MODELS_EIPSERVICE, provider.getServiceJson()).
+ putString(PROVIDER_MODELS_PROVIDER, provider.getModelsProviderJson());
if (async) {
editor.apply();
} else {
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java
index 4093f25c..85af48b0 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java
@@ -19,6 +19,10 @@ package se.leap.bitmaskclient.providersetup;
import static se.leap.bitmaskclient.R.string.vpn_certificate_is_invalid;
import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_RESULT_KEY;
+import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_MODELS_BRIDGES;
+import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_MODELS_EIPSERVICE;
+import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_MODELS_GATEWAYS;
+import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_MODELS_PROVIDER;
import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_MOTD;
import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_MOTD_HASHES;
import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_MOTD_LAST_SEEN;
@@ -31,7 +35,6 @@ import static se.leap.bitmaskclient.base.models.Provider.PROVIDER_API_IP;
import static se.leap.bitmaskclient.base.models.Provider.PROVIDER_IP;
import static se.leap.bitmaskclient.base.utils.CertificateHelper.getFingerprintFromCertificate;
import static se.leap.bitmaskclient.base.utils.ConfigHelper.getDomainFromMainURL;
-import static se.leap.bitmaskclient.base.utils.PreferenceHelper.deleteProviderDetailsFromPreferences;
import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getFromPersistedProvider;
import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getLongFromPersistedProvider;
import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getStringSetFromPersistedProvider;
@@ -46,6 +49,8 @@ import android.content.res.Resources;
import android.os.Bundle;
import android.util.Base64;
+import com.google.gson.JsonSyntaxException;
+
import org.json.JSONException;
import org.json.JSONObject;
@@ -59,10 +64,12 @@ import java.util.ArrayList;
import java.util.Set;
import java.util.concurrent.TimeoutException;
+import io.swagger.client.JSON;
+import io.swagger.client.model.ModelsBridge;
+import io.swagger.client.model.ModelsProvider;
import se.leap.bitmaskclient.base.models.Provider;
import se.leap.bitmaskclient.base.utils.ConfigHelper;
import se.leap.bitmaskclient.base.utils.PreferenceHelper;
-import se.leap.bitmaskclient.base.utils.PrivateKeyHelper;
/**
* Implements the logic of the http api calls. The methods of this class needs to be called from
@@ -99,7 +106,6 @@ public abstract class ProviderApiManagerBase {
void resetProviderDetails(Provider provider) {
provider.reset();
- deleteProviderDetailsFromPreferences(provider.getDomain());
}
protected boolean isValidJson(String jsonString) {
@@ -157,6 +163,10 @@ public abstract class ProviderApiManagerBase {
provider.setMotdLastSeenHashes(getPersistedMotdHashes(providerDomain));
provider.setLastMotdUpdate(getPersistedMotdLastUpdate(providerDomain));
provider.setMotdJson(getPersistedMotd(providerDomain));
+ provider.setModelsProvider(getFromPersistedProvider(PROVIDER_MODELS_PROVIDER, providerDomain));
+ provider.setService(getFromPersistedProvider(PROVIDER_MODELS_EIPSERVICE, providerDomain));
+ provider.setGateways(getFromPersistedProvider(PROVIDER_MODELS_GATEWAYS, providerDomain));
+ provider.setBridges(getFromPersistedProvider(PROVIDER_MODELS_BRIDGES, providerDomain));
}
}
@@ -203,6 +213,24 @@ public abstract class ProviderApiManagerBase {
}
}
+ protected ModelsProvider getPersistedModelsProvider(String providerDomain) {
+ try {
+ String json = getFromPersistedProvider(PROVIDER_MODELS_PROVIDER, providerDomain);
+ return json != null ? JSON.createGson().create().fromJson(json, ModelsProvider.class) : null;
+ } catch (JsonSyntaxException e) {
+ return null;
+ }
+ }
+
+ protected ModelsBridge[] getPersistedModelsBridge(String providerDomain) {
+ try {
+ String json = getFromPersistedProvider(PROVIDER_MODELS_BRIDGES, providerDomain);
+ return json != null ? JSON.createGson().create().fromJson(json, ModelsBridge[].class) : null;
+ } catch (JsonSyntaxException e) {
+ return null;
+ }
+ }
+
protected long getPersistedMotdLastSeen(String providerDomain) {
return getLongFromPersistedProvider(PROVIDER_MOTD_LAST_SEEN, providerDomain);
}
@@ -220,43 +248,4 @@ public abstract class ProviderApiManagerBase {
return PreferenceHelper.hasKey(Provider.KEY + "." + domain) && PreferenceHelper.hasKey(CA_CERT + "." + domain);
}
- protected Bundle loadCertificate(Provider provider, String certString) {
- Bundle result = new Bundle();
- if (certString == null) {
- eventSender.setErrorResult(result, vpn_certificate_is_invalid, null);
- return result;
- }
-
- try {
- // API returns concatenated cert & key. Split them for OpenVPN options
- String certificateString = null, keyString = null;
- String[] certAndKey = certString.split("(?<=-\n)");
- for (int i = 0; i < certAndKey.length - 1; i++) {
- if (certAndKey[i].contains("KEY")) {
- keyString = certAndKey[i++] + certAndKey[i];
- } else if (certAndKey[i].contains("CERTIFICATE")) {
- certificateString = certAndKey[i++] + certAndKey[i];
- }
- }
-
- PrivateKey key = parsePrivateKeyFromString(keyString);
- keyString = Base64.encodeToString(key.getEncoded(), Base64.DEFAULT);
-
- if (key instanceof RSAPrivateKey) {
- provider.setPrivateKeyString(RSA_KEY_BEGIN + keyString + RSA_KEY_END);
- } else {
- provider.setPrivateKeyString(ED_25519_KEY_BEGIN + keyString + ED_25519_KEY_END);
- }
-
- ArrayList<X509Certificate> certificates = ConfigHelper.parseX509CertificatesFromString(certificateString);
- certificates.get(0).checkValidity();
- certificateString = Base64.encodeToString(certificates.get(0).getEncoded(), Base64.DEFAULT);
- provider.setVpnCertificate( "-----BEGIN CERTIFICATE-----\n" + certificateString + "-----END CERTIFICATE-----");
- result.putBoolean(BROADCAST_RESULT_KEY, true);
- } catch (CertificateException | NullPointerException e) {
- e.printStackTrace();
- eventSender.setErrorResult(result, vpn_certificate_is_invalid, null);
- }
- return result;
- }
-}
+ }
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerV3.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerV3.java
index f827d85d..0c6878c6 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerV3.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerV3.java
@@ -28,6 +28,7 @@ import static se.leap.bitmaskclient.R.string.server_unreachable_message;
import static se.leap.bitmaskclient.R.string.service_is_down_error;
import static se.leap.bitmaskclient.R.string.setup_error_text;
import static se.leap.bitmaskclient.R.string.setup_error_text_custom;
+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;
@@ -38,6 +39,11 @@ import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_VPN_CERTIFICA
import static se.leap.bitmaskclient.base.utils.BuildConfigHelper.isDefaultBitmask;
import static se.leap.bitmaskclient.base.utils.CertificateHelper.getFingerprintFromCertificate;
import static se.leap.bitmaskclient.base.utils.ConfigHelper.getProviderFormattedString;
+import static se.leap.bitmaskclient.base.utils.PrivateKeyHelper.ED_25519_KEY_BEGIN;
+import static se.leap.bitmaskclient.base.utils.PrivateKeyHelper.ED_25519_KEY_END;
+import static se.leap.bitmaskclient.base.utils.PrivateKeyHelper.RSA_KEY_BEGIN;
+import static se.leap.bitmaskclient.base.utils.PrivateKeyHelper.RSA_KEY_END;
+import static se.leap.bitmaskclient.base.utils.PrivateKeyHelper.parsePrivateKeyFromString;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.CORRECTLY_DOWNLOADED_EIP_SERVICE;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.CORRECTLY_DOWNLOADED_GEOIP_JSON;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.CORRECTLY_DOWNLOADED_VPN_CERTIFICATE;
@@ -70,6 +76,7 @@ import static se.leap.bitmaskclient.tor.TorStatusObservable.getProxyPort;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.ResultReceiver;
+import android.util.Base64;
import android.util.Log;
import android.util.Pair;
@@ -86,12 +93,16 @@ import java.net.URL;
import java.net.UnknownHostException;
import java.net.UnknownServiceException;
import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate;
+import java.security.interfaces.RSAPrivateKey;
import java.util.ArrayList;
import java.util.List;
+import java.util.StringTokenizer;
import java.util.concurrent.TimeoutException;
import javax.net.ssl.SSLHandshakeException;
@@ -354,6 +365,47 @@ public class ProviderApiManagerV3 extends ProviderApiManagerBase implements IPro
return loadCertificate(provider, certString);
}
+ private Bundle loadCertificate(Provider provider, String certString) {
+ Bundle result = new Bundle();
+ if (certString == null) {
+ eventSender.setErrorResult(result, vpn_certificate_is_invalid, null);
+ return result;
+ }
+
+ try {
+ // API returns concatenated cert & key. Split them for OpenVPN options
+ String certificateString = null, keyString = null;
+ String[] certAndKey = certString.split("(?<=-\n)");
+
+ for (int i = 0; i < certAndKey.length - 1; i++) {
+ if (certAndKey[i].contains("KEY")) {
+ keyString += certAndKey[i++] + certAndKey[i];
+ } else if (certAndKey[i].contains("CERTIFICATE")) {
+ certificateString += certAndKey[i++] + certAndKey[i];
+ }
+ }
+
+ PrivateKey key = parsePrivateKeyFromString(keyString);
+ keyString = Base64.encodeToString(key.getEncoded(), Base64.DEFAULT);
+
+ if (key instanceof RSAPrivateKey) {
+ provider.setPrivateKeyString(RSA_KEY_BEGIN + keyString + RSA_KEY_END);
+ } else {
+ provider.setPrivateKeyString(ED_25519_KEY_BEGIN + keyString + ED_25519_KEY_END);
+ }
+
+ ArrayList<X509Certificate> certificates = ConfigHelper.parseX509CertificatesFromString(certificateString);
+ certificates.get(0).checkValidity();
+ certificateString = Base64.encodeToString(certificates.get(0).getEncoded(), Base64.DEFAULT);
+ provider.setVpnCertificate( "-----BEGIN CERTIFICATE-----\n" + certificateString + "-----END CERTIFICATE-----");
+ result.putBoolean(BROADCAST_RESULT_KEY, true);
+ } catch (CertificateException | NullPointerException e) {
+ e.printStackTrace();
+ eventSender.setErrorResult(result, vpn_certificate_is_invalid, null);
+ }
+ return result;
+ }
+
/**
* Fetches the geo ip Json, containing a list of gateways sorted by distance from the users current location.
* Fetching is only allowed if the cache timeout of 1 h was reached, a valid geoip service URL exists and the
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerV5.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerV5.java
index 9981feb1..5b822be7 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerV5.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerV5.java
@@ -2,6 +2,7 @@ package se.leap.bitmaskclient.providersetup;
import static android.text.TextUtils.isEmpty;
import static se.leap.bitmaskclient.R.string.malformed_url;
+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_expired_provider_cert;
import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_RESULT_KEY;
@@ -32,23 +33,22 @@ import android.util.Log;
import androidx.annotation.Nullable;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import de.blinkt.openvpn.core.VpnStatus;
-import io.swagger.client.JSON;
-import io.swagger.client.model.ModelsBridge;
-import io.swagger.client.model.ModelsEIPService;
-import io.swagger.client.model.ModelsGateway;
-import io.swagger.client.model.ModelsProvider;
import mobile.BitmaskMobile;
import se.leap.bitmaskclient.BuildConfig;
import se.leap.bitmaskclient.R;
import se.leap.bitmaskclient.base.models.Provider;
import se.leap.bitmaskclient.base.models.ProviderObservable;
import se.leap.bitmaskclient.base.utils.ConfigHelper;
+import se.leap.bitmaskclient.base.utils.CredentialsParser;
import se.leap.bitmaskclient.base.utils.PreferenceHelper;
import se.leap.bitmaskclient.eip.EipStatus;
import se.leap.bitmaskclient.tor.TorStatusObservable;
@@ -124,8 +124,7 @@ public class ProviderApiManagerV5 extends ProviderApiManagerBase implements IPro
try {
String serviceJson = bm.getService();
- ModelsEIPService service = JSON.createGson().create().fromJson(serviceJson, ModelsEIPService.class);
- provider.setService(service);
+ provider.setService(serviceJson);
} catch (Exception e) {
return eventSender.setErrorResult(currentDownload, R.string.config_error_found, null);
}
@@ -133,23 +132,14 @@ public class ProviderApiManagerV5 extends ProviderApiManagerBase implements IPro
if (PreferenceHelper.getUseBridges()) {
try {
String bridgesJson = bm.getAllBridges("", "", "", "");
- if (bridgesJson.isEmpty()) {
- //TODO send no bridges error event
- }
- ModelsBridge[] bridges = JSON.createGson().create().fromJson(bridgesJson, ModelsBridge[].class);
- provider.setBridges(bridges);
+ provider.setBridges(bridgesJson);
} catch (Exception e) {
// TODO: send failed to fetch bridges event
}
} else {
try {
String gatewaysJson = bm.getAllGateways("", "", "");
- if (gatewaysJson.isEmpty()) {
- //TODO send no bridges error event
- }
- ModelsGateway[] gateways = JSON.createGson().create().fromJson(gatewaysJson, ModelsGateway[].class);
-
- provider.setGateways(gateways);
+ provider.setGateways(gatewaysJson);
} catch (Exception e) {
// TODO: send
return eventSender.setErrorResult(currentDownload, R.string.config_error_found, null);
@@ -171,29 +161,20 @@ public class ProviderApiManagerV5 extends ProviderApiManagerBase implements IPro
return currentDownload;
}
- getPersistedProviderUpdates(provider);
- currentDownload = validateProviderDetails(provider);
-
//provider certificate invalid
if (currentDownload.containsKey(ERRORS)) {
currentDownload.putParcelable(PROVIDER_KEY, provider);
return currentDownload;
}
- //no provider json or certificate available
- if (currentDownload.containsKey(BROADCAST_RESULT_KEY) && !currentDownload.getBoolean(BROADCAST_RESULT_KEY)) {
- resetProviderDetails(provider);
- }
-
- if (currentDownload.containsKey(PROVIDER_KEY)) {
- provider = currentDownload.getParcelable(PROVIDER_KEY);
- }
BitmaskMobile bm;
try {
bm = new BitmaskMobile(provider.getMainUrl(), new PreferenceHelper.SharedPreferenceStore());
+ bm.setDebug(BuildConfig.DEBUG);
if (TorStatusObservable.isRunning() && TorStatusObservable.getSocksProxyPort() != -1) {
bm.setSocksProxy(SOCKS_PROXY_SCHEME + PROXY_HOST + ":" + TorStatusObservable.getSocksProxyPort());
}
+ // TODO bm.setIntroducer();
} catch (IllegalStateException e) {
// TODO: improve error message
return eventSender.setErrorResult(currentDownload, R.string.config_error_found, null);
@@ -204,8 +185,7 @@ public class ProviderApiManagerV5 extends ProviderApiManagerBase implements IPro
try {
String providerJson = bm.getProvider();
Log.d(TAG, "provider Json reponse: " + providerJson);
- ModelsProvider p = JSON.createGson().create().fromJson(providerJson, ModelsProvider.class);
- provider.setModelsProvider(p);
+ provider.setModelsProvider(providerJson);
ProviderSetupObservable.updateProgress(DOWNLOADED_PROVIDER_JSON);
} catch (Exception e) {
Log.w(TAG, "failed fo fetch provider.json: " + e.getMessage());
@@ -215,8 +195,7 @@ public class ProviderApiManagerV5 extends ProviderApiManagerBase implements IPro
try {
String serviceJson = bm.getService();
Log.d(TAG, "service Json reponse: " + serviceJson);
- ModelsEIPService service = JSON.createGson().create().fromJson(serviceJson, ModelsEIPService.class);
- provider.setService(service);
+ provider.setService(serviceJson);
ProviderSetupObservable.updateProgress(DOWNLOADED_EIP_SERVICE_JSON);
} catch (Exception e) {
Log.w(TAG, "failed to fetch service.json: " + e.getMessage());
@@ -228,8 +207,7 @@ public class ProviderApiManagerV5 extends ProviderApiManagerBase implements IPro
// TODO: check if provider supports this API endpoint?
String gatewaysJson = bm.getAllGateways("", "", "");
Log.d(TAG, "gateways Json reponse: " + gatewaysJson);
- ModelsGateway[] gateways = JSON.createGson().create().fromJson(gatewaysJson, ModelsGateway[].class);
- provider.setGateways(gateways);
+ provider.setGateways(gatewaysJson);
} catch (Exception e) {
Log.w(TAG, "failed to fetch gateways: " + e.getMessage());
e.printStackTrace();
@@ -240,8 +218,7 @@ public class ProviderApiManagerV5 extends ProviderApiManagerBase implements IPro
// TODO: check if provider supports this API endpoint?
String bridgesJson = bm.getAllBridges("", "", "", "");
Log.d(TAG, "bridges Json reponse: " + bridgesJson);
- ModelsBridge[] bridges = JSON.createGson().create().fromJson(bridgesJson, ModelsBridge[].class);
- provider.setBridges(bridges);
+ provider.setBridges(bridgesJson);
} catch (Exception e) {
Log.w(TAG, "failed to fetch bridges: " + e.getMessage());
e.printStackTrace();
@@ -250,7 +227,7 @@ public class ProviderApiManagerV5 extends ProviderApiManagerBase implements IPro
try {
String cert = bm.getOpenVPNCert();
- currentDownload = loadCertificate(provider, cert);
+ currentDownload = loadCredentials(provider, cert);
} catch (Exception e) {
return eventSender.setErrorResult(currentDownload, R.string.error_json_exception_user_message, null);
}
@@ -258,6 +235,19 @@ public class ProviderApiManagerV5 extends ProviderApiManagerBase implements IPro
return currentDownload;
}
+ private Bundle loadCredentials(Provider provider, String credentials) {
+ Bundle result = new Bundle();
+
+ try {
+ CredentialsParser.parseXml(credentials, provider);
+ } catch (XmlPullParserException | IOException e) {
+ return eventSender.setErrorResult(result, vpn_certificate_is_invalid, null);
+ }
+
+ result.putBoolean(BROADCAST_RESULT_KEY, true);
+ return result;
+ }
+
@Nullable
private void configureBaseCountryCode(BitmaskMobile bm, Bundle parameters) {
String cc = parameters.getString(COUNTRYCODE, null);
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java
index 2b322586..8b4b7ad8 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java
@@ -228,23 +228,19 @@ public class ConfigureProviderFragment extends BaseSetupFragment implements Prop
switch (resultCode) {
case PROVIDER_OK:
setupActivityCallback.onProviderSelected(provider);
- if (provider.allowsAnonymous()) {
- ProviderAPICommand.execute(this.getContext(), DOWNLOAD_VPN_CERTIFICATE, provider);
+ if (provider.getApiVersion() < 5) {
+ if (provider.allowsAnonymous()) {
+ ProviderAPICommand.execute(this.getContext(), DOWNLOAD_VPN_CERTIFICATE, provider);
+ } else {
+ // TODO: implement error message that this client only supports anonymous usage
+ }
} else {
- // TODO: implement error message that this client only supports anonymous usage
+ sendSuccess(resumeSetup);
}
break;
case CORRECTLY_DOWNLOADED_VPN_CERTIFICATE:
setupActivityCallback.onProviderSelected(provider);
- handler.postDelayed(() -> {
- if (!ProviderSetupObservable.isCanceled()) {
- try {
- setupActivityCallback.onConfigurationSuccess();
- } catch (NullPointerException npe) {
- // callback disappeared in the meanwhile
- }
- }
- }, resumeSetup ? 0 : 750);
+ sendSuccess(resumeSetup);
break;
case PROVIDER_NOK:
case INCORRECTLY_DOWNLOADED_VPN_CERTIFICATE:
@@ -258,4 +254,15 @@ public class ConfigureProviderFragment extends BaseSetupFragment implements Prop
}
}
+ private void sendSuccess(boolean resumeSetup) {
+ handler.postDelayed(() -> {
+ if (!ProviderSetupObservable.isCanceled()) {
+ try {
+ setupActivityCallback.onConfigurationSuccess();
+ } catch (NullPointerException npe) {
+ // callback disappeared in the meanwhile
+ }
+ }
+ }, resumeSetup ? 0 : 750);
+ }
} \ No newline at end of file
diff --git a/app/src/test/java/se/leap/bitmaskclient/providersetup/ProviderManagerTest.java b/app/src/test/java/se/leap/bitmaskclient/providersetup/ProviderManagerTest.java
index d76e87c2..7cca9c2f 100644
--- a/app/src/test/java/se/leap/bitmaskclient/providersetup/ProviderManagerTest.java
+++ b/app/src/test/java/se/leap/bitmaskclient/providersetup/ProviderManagerTest.java
@@ -67,7 +67,7 @@ public class ProviderManagerTest {
preferenceHelper = new PreferenceHelper(mockSharedPrefs);
HashSet<Provider> customProviders = new HashSet<>();
- customProviders.add(Provider.createCustomProvider("https://leapcolombia.org", "leapcolombia.org"));
+ customProviders.add(Provider.createCustomProvider("https://leapcolombia.org", "leapcolombia.org", null));
PreferenceHelper.setCustomProviders(customProviders);
}