summaryrefslogtreecommitdiff
path: root/app/src/main/java/se/leap/bitmaskclient/base/utils
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java/se/leap/bitmaskclient/base/utils')
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/utils/BuildConfigHelper.java98
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/utils/CertificateHelper.java64
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/utils/ConfigHelper.java136
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/utils/FileHelper.java41
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/utils/HandlerProvider.java38
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/utils/InputStreamHelper.java24
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java7
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/utils/RSAHelper.java72
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/utils/TimezoneHelper.java47
9 files changed, 398 insertions, 129 deletions
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/BuildConfigHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/BuildConfigHelper.java
new file mode 100644
index 00000000..e1f65b5e
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/BuildConfigHelper.java
@@ -0,0 +1,98 @@
+package se.leap.bitmaskclient.base.utils;
+
+import static se.leap.bitmaskclient.base.models.Constants.DEFAULT_BITMASK;
+
+import androidx.annotation.VisibleForTesting;
+
+import de.blinkt.openvpn.core.NativeUtils;
+import se.leap.bitmaskclient.BuildConfig;
+
+// ObfsVpnHelper class allows us to mock BuildConfig fields related to the pre-shipped circumvention settings
+public class BuildConfigHelper {
+
+ public interface BuildConfigHelperInterface {
+ boolean useObfsVpn();
+ boolean hasObfuscationPinningDefaults();
+ String obfsvpnIP();
+ String obfsvpnPort();
+ String obfsvpnCert();
+ boolean useKcp();
+ boolean isDefaultBitmask();
+ }
+
+ public static class DefaultBuildConfigHelper implements BuildConfigHelperInterface {
+ @Override
+ public boolean useObfsVpn() {
+ return BuildConfig.use_obfsvpn;
+ }
+
+ @Override
+ public boolean hasObfuscationPinningDefaults() {
+ return BuildConfig.obfsvpn_ip != null &&
+ BuildConfig.obfsvpn_port != null &&
+ BuildConfig.obfsvpn_cert != null &&
+ !BuildConfig.obfsvpn_ip.isEmpty() &&
+ !BuildConfig.obfsvpn_port.isEmpty() &&
+ !BuildConfig.obfsvpn_cert.isEmpty();
+ }
+
+ @Override
+ public String obfsvpnIP() {
+ return BuildConfig.obfsvpn_ip;
+ }
+
+ @Override
+ public String obfsvpnPort() {
+ return BuildConfig.obfsvpn_port;
+ }
+
+ @Override
+ public String obfsvpnCert() {
+ return BuildConfig.obfsvpn_cert;
+ }
+
+ @Override
+ public boolean useKcp() {
+ return BuildConfig.obfsvpn_use_kcp;
+ }
+
+ @Override
+ public boolean isDefaultBitmask() {
+ return BuildConfig.FLAVOR_branding.equals(DEFAULT_BITMASK);
+ }
+ }
+
+ private static BuildConfigHelperInterface instance = new DefaultBuildConfigHelper();
+
+ @VisibleForTesting
+ public BuildConfigHelper(BuildConfigHelperInterface helperInterface) {
+ if (!NativeUtils.isUnitTest()) {
+ throw new IllegalStateException("ObfsVpnHelper injected with ObfsVpnHelperInterface outside of an unit test");
+ }
+ instance = helperInterface;
+ }
+
+ public static boolean useObfsVpn() {
+ return instance.useObfsVpn();
+ }
+
+ public static boolean hasObfuscationPinningDefaults() {
+ return instance.hasObfuscationPinningDefaults();
+ }
+ public static String obfsvpnIP() {
+ return instance.obfsvpnIP();
+ }
+ public static String obfsvpnPort() {
+ return instance.obfsvpnPort();
+ }
+ public static String obfsvpnCert() {
+ return instance.obfsvpnCert();
+ }
+ public static boolean useKcp() {
+ return instance.useKcp();
+ }
+
+ public static boolean isDefaultBitmask() {
+ return instance.isDefaultBitmask();
+ }
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/CertificateHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/CertificateHelper.java
new file mode 100644
index 00000000..11202734
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/CertificateHelper.java
@@ -0,0 +1,64 @@
+package se.leap.bitmaskclient.base.utils;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+
+import de.blinkt.openvpn.core.NativeUtils;
+
+public class CertificateHelper {
+
+ public interface CertificateHelperInterface {
+ String getFingerprintFromCertificate(X509Certificate certificate, String encoding) throws NoSuchAlgorithmException, CertificateEncodingException;
+
+ }
+
+ public static class DefaultCertificateHelper implements CertificateHelperInterface {
+
+ public 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
+ */
+ @Override
+ public String getFingerprintFromCertificate(X509Certificate certificate, String encoding) throws NoSuchAlgorithmException, CertificateEncodingException {
+ byte[] byteArray = MessageDigest.getInstance(encoding).digest(certificate.getEncoded());
+ return byteArrayToHex(byteArray);
+ }
+ }
+
+ private static CertificateHelperInterface instance = new DefaultCertificateHelper();
+
+ @VisibleForTesting
+ public CertificateHelper(CertificateHelperInterface helperInterface) {
+ if (!NativeUtils.isUnitTest()) {
+ throw new IllegalStateException("CertificateHelper injected with CertificateHelperInterface outside of an unit test");
+ }
+ instance = helperInterface;
+ }
+
+ @NonNull
+ public static String getFingerprintFromCertificate(X509Certificate certificate, String encoding) throws NoSuchAlgorithmException, CertificateEncodingException {
+ return instance.getFingerprintFromCertificate(certificate, encoding);
+ }
+
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/ConfigHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/ConfigHelper.java
index 9289738a..cd5d1fca 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/utils/ConfigHelper.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/ConfigHelper.java
@@ -16,8 +16,6 @@
*/
package se.leap.bitmaskclient.base.utils;
-import static se.leap.bitmaskclient.base.models.Constants.DEFAULT_BITMASK;
-
import android.app.PendingIntent;
import android.content.Context;
import android.content.res.Resources;
@@ -36,22 +34,14 @@ 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 java.util.ArrayList;
-import java.util.Calendar;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import de.blinkt.openvpn.core.NativeUtils;
import okhttp3.internal.publicsuffix.PublicSuffixDatabase;
import se.leap.bitmaskclient.BuildConfig;
import se.leap.bitmaskclient.R;
@@ -69,8 +59,6 @@ public class ConfigHelper {
final public static String NG_1024 =
"eeaf0ab9adb38dd69c33f80afa8fc5e86072618775ff3c0b9ea2314c9c256576d674df7496ea81d3383b4813d692c6e0e0d5d8e250b98be48e495c1d6089dad15dc7d7b46154d6b6ce8ef4ad69b15d4982559b297bcf1885c529f566660e57ec68edbc3c05726cc02fd4cbf4976eaa9afd5138fe8376435b9fc61d2fc0eb06e3";
final public static BigInteger G = new BigInteger("2");
- final public static Pattern IPv4_PATTERN = Pattern.compile("^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$");
- final public static Pattern PEM_CERTIFICATE_PATTERN = Pattern.compile("((-----BEGIN CERTIFICATE-----)([A-Za-z0-9+/=\\n]+)(-----END CERTIFICATE-----)+)");
public static boolean checkErroneousDownload(String downloadedString) {
try {
@@ -109,8 +97,8 @@ public class ConfigHelper {
CertificateFactory cf;
try {
cf = CertificateFactory.getInstance("X.509");
-
- Matcher matcher = PEM_CERTIFICATE_PATTERN.matcher(certificateString);
+ Pattern pattern = Pattern.compile("((-----BEGIN CERTIFICATE-----)([A-Za-z0-9+/=\\n]+)(-----END CERTIFICATE-----)+)");
+ Matcher matcher = pattern.matcher(certificateString);
while (matcher.find()) {
String certificate = matcher.group(3);
if (certificate == null) continue;
@@ -131,65 +119,6 @@ public class ConfigHelper {
return null;
}
- public static class RSAHelper {
- public static RSAPrivateKey parseRsaKeyFromString(String rsaKeyString) {
- RSAPrivateKey key;
- try {
- KeyFactory kf;
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
- kf = KeyFactory.getInstance("RSA", "BC");
- } else {
- kf = KeyFactory.getInstance("RSA");
- }
- 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 (NullPointerException e) {
- e.printStackTrace();
- return null;
- } catch (NoSuchProviderException 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);
- }
-
public static void ensureNotOnMainThread(@NonNull Context context) throws IllegalStateException{
Looper looper = Looper.myLooper();
if (looper != null && looper == context.getMainLooper()) {
@@ -198,35 +127,18 @@ public class ConfigHelper {
}
}
- public static boolean isDefaultBitmask() {
- return BuildConfig.FLAVOR_branding.equals(DEFAULT_BITMASK);
- }
-
public static boolean preferAnonymousUsage() {
return BuildConfig.priotize_anonymous_usage;
}
- public static int getCurrentTimezone() {
- return Calendar.getInstance().get(Calendar.ZONE_OFFSET) / 3600000;
- }
-
- public static int timezoneDistance(int local_timezone, int remoteTimezone) {
- // Distance along the numberline of Prime Meridian centric, assumes UTC-11 through UTC+12
- int dist = Math.abs(local_timezone - remoteTimezone);
- // Farther than 12 timezones and it's shorter around the "back"
- if (dist > 12)
- dist = 12 - (dist - 12); // Well i'll be. Absolute values make equations do funny things.
- return dist;
- }
-
/**
*
* @param remoteTimezone
* @return a value between 0.1 and 1.0
*/
public static double getConnectionQualityFromTimezoneDistance(int remoteTimezone) {
- int localTimeZone = ConfigHelper.getCurrentTimezone();
- int distance = ConfigHelper.timezoneDistance(localTimeZone, remoteTimezone);
+ int localTimeZone = TimezoneHelper.getCurrentTimezone();
+ int distance = TimezoneHelper.timezoneDistance(localTimeZone, remoteTimezone);
return Math.max(distance / 12.0, 0.1);
}
@@ -274,7 +186,7 @@ public class ConfigHelper {
if (ipv4 == null) {
return false;
}
- Matcher matcher = IPv4_PATTERN.matcher(ipv4);
+ Matcher matcher = Pattern.compile("^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$").matcher(ipv4);
return matcher.matches();
}
@@ -287,35 +199,6 @@ public class ConfigHelper {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R;
}
- // ObfsVpnHelper class allows us to mock BuildConfig.use_obfsvpn while
- // not mocking the whole ConfigHelper class
- public static class ObfsVpnHelper {
- public static boolean useObfsVpn() {
- return BuildConfig.use_obfsvpn;
- }
-
- public static boolean hasObfuscationPinningDefaults() {
- return BuildConfig.obfsvpn_ip != null &&
- BuildConfig.obfsvpn_port != null &&
- BuildConfig.obfsvpn_cert != null &&
- !BuildConfig.obfsvpn_ip.isEmpty() &&
- !BuildConfig.obfsvpn_port.isEmpty() &&
- !BuildConfig.obfsvpn_cert.isEmpty();
- }
- public static String obfsvpnIP() {
- return BuildConfig.obfsvpn_ip;
- }
- public static String obfsvpnPort() {
- return BuildConfig.obfsvpn_port;
- }
- public static String obfsvpnCert() {
- return BuildConfig.obfsvpn_cert;
- }
- public static boolean useKcp() {
- return BuildConfig.obfsvpn_use_kcp;
- }
- }
-
public static int getPendingIntentFlags() {
int flags = PendingIntent.FLAG_CANCEL_CURRENT;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
@@ -323,4 +206,11 @@ public class ConfigHelper {
}
return flags;
}
+
+ public static int getTorTimeout() {
+ if (NativeUtils.isUnitTest()) {
+ return 1;
+ }
+ return 180;
+ }
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/FileHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/FileHelper.java
index eb1c255c..f1d86876 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/utils/FileHelper.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/FileHelper.java
@@ -2,6 +2,8 @@ package se.leap.bitmaskclient.base.utils;
import android.content.Context;
+import androidx.annotation.VisibleForTesting;
+
import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
@@ -9,19 +11,50 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
+import de.blinkt.openvpn.core.NativeUtils;
+
/**
* Created by cyberta on 18.03.18.
*/
public class FileHelper {
+
+ public interface FileHelperInterface {
+ File createFile(File dir, String fileName);
+ void persistFile(File file, String content) throws IOException;
+ }
+
+ public static class DefaultFileHelper implements FileHelperInterface {
+ @Override
+ public File createFile(File dir, String fileName) {
+ return new File(dir, fileName);
+ }
+
+ @Override
+ public void persistFile(File file, String content) throws IOException {
+ FileWriter writer = new FileWriter(file);
+ writer.write(content);
+ writer.close();
+ }
+ }
+
+ private static FileHelperInterface instance = new DefaultFileHelper();
+
+ @VisibleForTesting
+ public FileHelper(FileHelperInterface helperInterface) {
+ if (!NativeUtils.isUnitTest()) {
+ throw new IllegalStateException("FileHelper injected with FileHelperInterface outside of an unit test");
+ }
+
+ instance = helperInterface;
+ }
+
public static File createFile(File dir, String fileName) {
- return new File(dir, fileName);
+ return instance.createFile(dir, fileName);
}
public static void persistFile(File file, String content) throws IOException {
- FileWriter writer = new FileWriter(file);
- writer.write(content);
- writer.close();
+ instance.persistFile(file, content);
}
public static String readPublicKey(Context context) {
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/HandlerProvider.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/HandlerProvider.java
new file mode 100644
index 00000000..d9198ab7
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/HandlerProvider.java
@@ -0,0 +1,38 @@
+package se.leap.bitmaskclient.base.utils;
+
+import android.os.Handler;
+import android.os.Looper;
+
+public class HandlerProvider {
+
+
+ public interface HandlerInterface {
+ void postDelayed(Runnable r, long delay);
+ }
+
+
+ private static HandlerInterface instance;
+
+ public HandlerProvider(HandlerInterface handlerInterface) {
+ instance = handlerInterface;
+ }
+ public static HandlerInterface get() {
+ if (instance == null) {
+ instance = new DefaultHandler();
+ }
+ return instance;
+ }
+
+ public static class DefaultHandler implements HandlerInterface {
+ Handler handler;
+
+ public DefaultHandler() {
+ this.handler = new Handler(Looper.getMainLooper());
+ }
+ @Override
+ public void postDelayed(Runnable r, long delay) {
+ this.handler.postDelayed(r, delay);
+ }
+ }
+}
+
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/InputStreamHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/InputStreamHelper.java
index 8e6273a7..6dfe0861 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/utils/InputStreamHelper.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/InputStreamHelper.java
@@ -8,14 +8,36 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
+import de.blinkt.openvpn.core.NativeUtils;
+
/**
* Created by cyberta on 18.03.18.
*/
public class InputStreamHelper {
+ public interface InputStreamHelperInterface {
+ InputStream getInputStreamFrom(String filePath) throws FileNotFoundException;
+
+ }
+
+ private static InputStreamHelperInterface instance = new DefaultInputStreamHelper();
+
+ private static class DefaultInputStreamHelper implements InputStreamHelperInterface {
+ @Override
+ public InputStream getInputStreamFrom(String filePath) throws FileNotFoundException {
+ return new FileInputStream(filePath);
+ }
+ }
+
+ public InputStreamHelper(InputStreamHelperInterface helperInterface) {
+ if (!NativeUtils.isUnitTest()) {
+ throw new IllegalStateException("InputStreamHelper injected with InputStreamHelperInterface outside of an unit test");
+ }
+ instance = helperInterface;
+ }
//allows us to mock FileInputStream
public static InputStream getInputStreamFrom(String filePath) throws FileNotFoundException {
- return new FileInputStream(filePath);
+ return instance.getInputStreamFrom(filePath);
}
public static String loadInputStreamAsString(InputStream is) {
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 b35a04cd..2420a797 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
@@ -40,6 +40,7 @@ import static se.leap.bitmaskclient.base.models.Constants.USE_BRIDGES;
import static se.leap.bitmaskclient.base.models.Constants.USE_IPv6_FIREWALL;
import static se.leap.bitmaskclient.base.models.Constants.USE_OBFUSCATION_PINNING;
import static se.leap.bitmaskclient.base.models.Constants.USE_SNOWFLAKE;
+import static se.leap.bitmaskclient.base.models.Constants.USE_SYSTEM_PROXY;
import android.content.Context;
import android.content.SharedPreferences;
@@ -460,12 +461,16 @@ public class PreferenceHelper {
return getBoolean(ALLOW_EXPERIMENTAL_TRANSPORTS, false);
}
+ public static boolean useSystemProxy() {
+ return getBoolean(USE_SYSTEM_PROXY, true);
+ }
+
public static void setUseObfuscationPinning(Boolean pinning) {
putBoolean(USE_OBFUSCATION_PINNING, pinning);
}
public static boolean useObfuscationPinning() {
- return ConfigHelper.ObfsVpnHelper.useObfsVpn() &&
+ return BuildConfigHelper.useObfsVpn() &&
getUseBridges() &&
getBoolean(USE_OBFUSCATION_PINNING, false) &&
!TextUtils.isEmpty(getObfuscationPinningIP()) &&
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/RSAHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/RSAHelper.java
new file mode 100644
index 00000000..2872139a
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/RSAHelper.java
@@ -0,0 +1,72 @@
+package se.leap.bitmaskclient.base.utils;
+
+import android.os.Build;
+
+import androidx.annotation.VisibleForTesting;
+
+import org.spongycastle.util.encoders.Base64;
+
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+
+import de.blinkt.openvpn.core.NativeUtils;
+
+public class RSAHelper {
+
+ public interface RSAHelperInterface {
+ RSAPrivateKey parseRsaKeyFromString(String rsaKeyString);
+ }
+
+ public static class DefaultRSAHelper implements RSAHelperInterface {
+
+ @Override
+ public RSAPrivateKey parseRsaKeyFromString(String rsaKeyString) {
+ RSAPrivateKey key;
+ try {
+ KeyFactory kf;
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
+ kf = KeyFactory.getInstance("RSA", "BC");
+ } else {
+ kf = KeyFactory.getInstance("RSA");
+ }
+ 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 (NullPointerException e) {
+ e.printStackTrace();
+ return null;
+ } catch (NoSuchProviderException e) {
+ e.printStackTrace();
+ return null;
+ }
+
+ return key;
+ }
+ }
+
+ private static RSAHelperInterface instance = new DefaultRSAHelper();
+
+ @VisibleForTesting
+ public RSAHelper(RSAHelperInterface helperInterface) {
+ if (!NativeUtils.isUnitTest()) {
+ throw new IllegalStateException("RSAHelper injected with RSAHelperInterface outside of an unit test");
+ }
+ instance = helperInterface;
+ }
+
+ public static RSAPrivateKey parseRsaKeyFromString(String rsaKeyString) {
+ return instance.parseRsaKeyFromString(rsaKeyString);
+ }
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/TimezoneHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/TimezoneHelper.java
new file mode 100644
index 00000000..63b12fd3
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/TimezoneHelper.java
@@ -0,0 +1,47 @@
+package se.leap.bitmaskclient.base.utils;
+
+import androidx.annotation.VisibleForTesting;
+
+import java.util.Calendar;
+
+import de.blinkt.openvpn.core.NativeUtils;
+
+public class TimezoneHelper {
+
+ public interface TimezoneInterface {
+ int getCurrentTimezone();
+ }
+
+ private static TimezoneInterface instance = new DefaultTimezoneHelper();
+
+ @VisibleForTesting
+ public TimezoneHelper(TimezoneInterface timezoneInterface) {
+ if (!NativeUtils.isUnitTest()) {
+ throw new IllegalStateException("TimezoneHelper injected with timezoneInterface outside of an unit test");
+ }
+ instance = timezoneInterface;
+ }
+
+ public static TimezoneInterface get() {
+ return instance;
+ }
+
+ public static int timezoneDistance(int localTimezone, int remoteTimezone) { // Distance along the numberline of Prime Meridian centric, assumes UTC-11 through UTC+12
+ int dist = Math.abs(localTimezone - remoteTimezone);
+ // Farther than 12 timezones and it's shorter around the "back"
+ if (dist > 12)
+ dist = 12 - (dist - 12); // Well i'll be. Absolute values make equations do funny things.
+ return dist;
+ }
+
+ public static int getCurrentTimezone() {
+ return get().getCurrentTimezone();
+ }
+
+ private static class DefaultTimezoneHelper implements TimezoneInterface {
+ @Override
+ public int getCurrentTimezone() {
+ return Calendar.getInstance().get(Calendar.ZONE_OFFSET) / 3600000;
+ }
+ }
+}