diff options
author | cyBerta <cyberta@riseup.net> | 2023-05-24 13:18:02 +0200 |
---|---|---|
committer | cyBerta <cyberta@riseup.net> | 2023-07-13 10:53:49 +0200 |
commit | 69164dc81246035deea243181f4134d7018f572f (patch) | |
tree | b6b48ad4eba38e66cdebbe9a6832785cf027e9b7 | |
parent | 86bf641b9558e9b9532efbb70391e41b37f620e9 (diff) |
migrate to encrypted shared preferences
6 files changed, 88 insertions, 23 deletions
diff --git a/app/build.gradle b/app/build.gradle index 07cf4ca5..367f69c8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,9 +4,9 @@ import java.util.regex.Pattern apply plugin: 'com.android.application' android { - compileSdkVersion 31 + compileSdkVersion 33 ndkVersion "21.4.7075529" - buildToolsVersion '31.0.0' + buildToolsVersion '33.0.2' compileOptions { targetCompatibility 1.8 @@ -25,7 +25,7 @@ android { versionCode 169000 versionName "1.1.8" minSdkVersion 21 - targetSdkVersion 31 + targetSdkVersion 33 vectorDrawables.useSupportLibrary = true buildConfigField 'boolean', 'openvpn3', 'false' @@ -429,6 +429,7 @@ dependencies { implementation 'com.squareup.okhttp3:okhttp:4.10.0' implementation 'com.squareup.okhttp3:okhttp-dnsoverhttps:4.10.0' implementation 'org.conscrypt:conscrypt-android:2.5.2' + implementation 'androidx.security:security-crypto:1.1.0-alpha06' implementation 'androidx.legacy:legacy-support-core-utils:1.0.0' implementation 'androidx.annotation:annotation:1.4.0' implementation 'androidx.legacy:legacy-support-v4:1.0.0' diff --git a/app/src/main/java/se/leap/bitmaskclient/base/StartActivity.java b/app/src/main/java/se/leap/bitmaskclient/base/StartActivity.java index 6426c3d6..b622f5ca 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/StartActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/StartActivity.java @@ -38,6 +38,7 @@ import android.util.Log; import androidx.annotation.IntDef; import androidx.annotation.Nullable; +import androidx.security.crypto.EncryptedSharedPreferences; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -81,7 +82,7 @@ public class StartActivity extends Activity{ @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - preferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); + preferences = PreferenceHelper.getSharedPreferences(this); Log.d(TAG, "Started"); @@ -197,6 +198,11 @@ public class StartActivity extends Activity{ } } + if (hasNewFeature(FeatureVersionCode.ENCRYPTED_SHARED_PREFS)) { + PreferenceHelper.migrateToEncryptedPrefs(this); + preferences = PreferenceHelper.getSharedPreferences(this); + } + // always check if manual gateway selection feature switch has been disabled if (!BuildConfig.allow_manual_gateway_selection && PreferenceHelper.getPreferredCity(this) != null) { PreferenceHelper.setPreferredCity(this, null); 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 57467974..70bf3943 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 @@ -27,6 +27,7 @@ public interface Constants { ///////////////////////////////////////////// String SHARED_PREFERENCES = "LEAPPreferences"; + String SHARED_ENCRYPTED_PREFERENCES = "LEAPEncryptedPreferences"; String PREFERENCES_APP_VERSION = "bitmask version"; String ALWAYS_ON_SHOW_DIALOG = "DIALOG.ALWAYS_ON_SHOW_DIALOG"; String CLEARLOG = "clearlogconnect"; diff --git a/app/src/main/java/se/leap/bitmaskclient/base/models/FeatureVersionCode.java b/app/src/main/java/se/leap/bitmaskclient/base/models/FeatureVersionCode.java index f7251acb..c2eb9694 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/models/FeatureVersionCode.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/models/FeatureVersionCode.java @@ -5,4 +5,6 @@ public interface FeatureVersionCode { int GEOIP_SERVICE = 148; int CALYX_PROVIDER_LILYPAD_UPDATE = 165000; int RISEUP_PROVIDER_LILYPAD_UPDATE = 165000; + + int ENCRYPTED_SHARED_PREFS = 170000; } 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 9cba221a..eadd37bc 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 @@ -27,6 +27,7 @@ import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_MOTD_LAST_UPD import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_PRIVATE_KEY; import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_VPN_CERTIFICATE; import static se.leap.bitmaskclient.base.models.Constants.RESTART_ON_UPDATE; +import static se.leap.bitmaskclient.base.models.Constants.SHARED_ENCRYPTED_PREFERENCES; import static se.leap.bitmaskclient.base.models.Constants.SHARED_PREFERENCES; import static se.leap.bitmaskclient.base.models.Constants.SHOW_EXPERIMENTAL; import static se.leap.bitmaskclient.base.models.Constants.USE_BRIDGES; @@ -37,16 +38,21 @@ import static se.leap.bitmaskclient.base.models.Constants.USE_SNOWFLAKE; import android.content.Context; import android.content.SharedPreferences; import android.text.TextUtils; +import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.WorkerThread; +import androidx.security.crypto.EncryptedSharedPreferences; import org.json.JSONException; import org.json.JSONObject; +import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; +import java.security.GeneralSecurityException; import java.util.HashSet; +import java.util.Map; import java.util.Set; import de.blinkt.openvpn.VpnProfile; @@ -60,6 +66,23 @@ import se.leap.bitmaskclient.tor.TorStatusObservable; public class PreferenceHelper { + private static final String TAG = PreferenceHelper.class.getSimpleName(); + + public static SharedPreferences getSharedPreferences(Context context) { + try { + return EncryptedSharedPreferences.create( + SHARED_ENCRYPTED_PREFERENCES, + "leap_secret_shared_prefs", + context, + EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, + EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM + ); + } catch (GeneralSecurityException | IOException e) { + e.printStackTrace(); + } + return context.getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); + } + public static Provider getSavedProviderFromSharedPreferences(@NonNull SharedPreferences preferences) { Provider provider = new Provider(); try { @@ -97,7 +120,7 @@ public class PreferenceHelper { } public static void persistProviderAsync(Context context, Provider provider) { - SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); + SharedPreferences preferences = getSharedPreferences(context); storeProviderInPreferences(preferences, provider, true); } @@ -151,7 +174,7 @@ public class PreferenceHelper { * Sets the profile that is connected (to connect if the service restarts) */ public static void setLastUsedVpnProfile(Context context, VpnProfile connectedProfile) { - SharedPreferences prefs = context.getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); + SharedPreferences prefs = getSharedPreferences(context); SharedPreferences.Editor prefsedit = prefs.edit(); prefsedit.putString(LAST_USED_PROFILE, connectedProfile.toJson()); prefsedit.apply(); @@ -161,7 +184,7 @@ public class PreferenceHelper { * Returns the profile that was last connected (to connect if the service restarts) */ public static VpnProfile getLastConnectedVpnProfile(Context context) { - SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); + SharedPreferences preferences = getSharedPreferences(context); String lastConnectedProfileJson = preferences.getString(LAST_USED_PROFILE, null); return VpnProfile.fromJson(lastConnectedProfileJson); } @@ -204,6 +227,8 @@ public class PreferenceHelper { apply(); } + // used in fatweb flavor + @SuppressWarnings("unused") public static void setLastAppUpdateCheck(Context context) { putLong(context, LAST_UPDATE_CHECK, System.currentTimeMillis()); } @@ -419,7 +444,7 @@ public class PreferenceHelper { if (context == null) { return null; } - SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); + SharedPreferences preferences = getSharedPreferences(context); return preferences.getStringSet(EXCLUDED_APPS, new HashSet<>()); } @@ -427,7 +452,7 @@ public class PreferenceHelper { if (context == null) { return defValue; } - SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); + SharedPreferences preferences = getSharedPreferences(context); return preferences.getLong(key, defValue); } @@ -435,7 +460,7 @@ public class PreferenceHelper { if (context == null) { return; } - SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); + SharedPreferences preferences = getSharedPreferences(context); preferences.edit().putLong(key, value).apply(); } @@ -443,7 +468,7 @@ public class PreferenceHelper { if (context == null) { return defValue; } - SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); + SharedPreferences preferences = getSharedPreferences(context); return preferences.getString(key, defValue); } @@ -452,7 +477,7 @@ public class PreferenceHelper { if (context == null) { return; } - SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); + SharedPreferences preferences = getSharedPreferences(context); preferences.edit().putString(key, value).commit(); } @@ -460,7 +485,7 @@ public class PreferenceHelper { if (context == null) { return; } - SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); + SharedPreferences preferences = getSharedPreferences(context); preferences.edit().putString(key, value).apply(); } @@ -468,7 +493,7 @@ public class PreferenceHelper { if (context == null) { return; } - SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); + SharedPreferences preferences = getSharedPreferences(context); preferences.edit().putStringSet(key, value).apply(); } @@ -477,7 +502,7 @@ public class PreferenceHelper { return false; } - SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); + SharedPreferences preferences = getSharedPreferences(context); return preferences.getBoolean(key, defValue); } @@ -486,7 +511,7 @@ public class PreferenceHelper { return; } - SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); + SharedPreferences preferences = getSharedPreferences(context); preferences.edit().putBoolean(key, value).apply(); } @@ -495,7 +520,41 @@ public class PreferenceHelper { return false; } - SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); + SharedPreferences preferences = getSharedPreferences(context); return preferences.contains(key); } + + public static void migrateToEncryptedPrefs(Context context) { + SharedPreferences encryptedPrefs = getSharedPreferences(context); + if (!(encryptedPrefs instanceof EncryptedSharedPreferences)) { + Log.e(TAG, "Failed to migrate shared preferences"); + return; + } + SharedPreferences.Editor encryptedEditor = encryptedPrefs.edit(); + SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); + Map<String,?> keys = preferences.getAll(); + + for(Map.Entry<String,?> entry : keys.entrySet()){ + try { + Object value = entry.getValue(); + if (value instanceof String) { + encryptedEditor.putString(entry.getKey(), (String) value); + } else if (value instanceof Boolean) { + encryptedEditor.putBoolean(entry.getKey(), (Boolean) value); + } else if (value instanceof Integer) { + encryptedEditor.putInt(entry.getKey(), (Integer) value); + } else if (value instanceof Set<?>) { + encryptedEditor.putStringSet(entry.getKey(), (Set<String>) value); + } else if (value instanceof Long) { + encryptedEditor.putLong(entry.getKey(), (Long) value); + } else if (value instanceof Float) { + encryptedEditor.putFloat(entry.getKey(), (Float) value); + } + } catch (ClassCastException e) { + e.printStackTrace(); + } + } + encryptedEditor.commit(); + preferences.edit().clear().apply(); + } } diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java index 86ce577b..0e7e1f4a 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java @@ -19,7 +19,6 @@ package se.leap.bitmaskclient.providersetup; import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; import android.net.ConnectivityManager; import android.net.NetworkCapabilities; import android.net.NetworkInfo; @@ -31,11 +30,9 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager; import java.util.concurrent.TimeoutException; +import se.leap.bitmaskclient.base.utils.PreferenceHelper; import se.leap.bitmaskclient.providersetup.connectivity.OkHttpClientGenerator; import se.leap.bitmaskclient.tor.TorServiceCommand; -import se.leap.bitmaskclient.tor.TorServiceConnection; - -import static se.leap.bitmaskclient.base.models.Constants.SHARED_PREFERENCES; /** * Implements HTTP api methods (encapsulated in {{@link ProviderApiManager}}) @@ -180,9 +177,8 @@ public class ProviderAPI extends JobIntentService implements ProviderApiManagerB private ProviderApiManager initApiManager() { - SharedPreferences preferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); OkHttpClientGenerator clientGenerator = new OkHttpClientGenerator(getResources()); - return new ProviderApiManager(preferences, getResources(), clientGenerator, this); + return new ProviderApiManager(PreferenceHelper.getSharedPreferences(this), getResources(), clientGenerator, this); } } |