Skip to content

Commit

Permalink
Rework logic to delete shared prefs when key is corrupt
Browse files Browse the repository at this point in the history
Sometimes encrypted shared preferences can become unusable on android when backed up data gets migrated between different devices (and possibly in other scenarios).

We tried to work around this by catching one particular exception and calling PlatformRemoveAll() to try and delete the shared prefs so we could create a new set, however this logic was flawed since the error occurs when getting an instance of the encrypted shared preferences, which the PlatformRemoveAll attempts to do itself (so it would fail to get the thing to remove the thing).

This changes up the logic a bit and directly clears the shared preference that the encrypted one is stored in, without first trying to get an instance of the corrupt shared prefs.

It then also tries to directly create a new instance afterwards, and return that, in an attempt to make this failsafe / reset operation transparent to the original call to get or set a secure storage key/value.
  • Loading branch information
Redth committed Jul 26, 2024
1 parent fe62f1d commit 65d3f94
Showing 1 changed file with 51 additions and 17 deletions.
68 changes: 51 additions & 17 deletions src/Essentials/src/SecureStorage/SecureStorage.android.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Android.Content;
using AndroidX.Security.Crypto;
using Java.Security;
using Javax.Crypto;
using Xamarin.Google.Crypto.Tink.Shaded.Protobuf;

namespace Microsoft.Maui.Storage
Expand Down Expand Up @@ -80,31 +81,64 @@ void PlatformRemoveAll()
editor?.Clear()?.Apply();
}

static void DeleteSharedPreferences()
{
// Get the alias used to store the encrypted values
var alias = $"{ApplicationModel.AppInfo.Current.PackageName}.microsoft.maui.essentials.preferences";
// Open an editor to the preferences we can clear
var editPreferences = Application.Context.GetSharedPreferences(alias, FileCreationMode.Private).Edit();
// Commit is synchronous here so we can be sure it's done before trying to create the encrypted preferences again
editPreferences?.Clear()?.Commit();
}

ISharedPreferences GetEncryptedSharedPreferences()
{
try
{
var context = Application.Context;

var prefsMainKey = new MasterKey.Builder(context, Alias)
.SetKeyScheme(MasterKey.KeyScheme.Aes256Gcm)
.Build();

return EncryptedSharedPreferences.Create(
context,
Alias,
prefsMainKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.Aes256Siv,
EncryptedSharedPreferences.PrefValueEncryptionScheme.Aes256Gcm);
return CreateEncryptedSharedPreferences();
}
catch (InvalidProtocolBufferException)
catch (System.Exception ex)
when (ex is InvalidProtocolBufferException or Android.Security.KeyStoreException or KeyStoreException or BadPaddingException)
{
// TODO: Use Logger here?
System.Diagnostics.Debug.WriteLine(
"Unable get encrypted shared preferences, which is likely due to an app uninstall. Removing all keys and returning null.");
PlatformRemoveAll();
// If we encounter any of these exceptions, it's likely due to a corrupt key or bad migration between devices
// There isn't much to do at this point except try to delete the shared preferences so we can recreate them
try
{
System.Diagnostics.Debug.WriteLine(
"Unable get encrypted shared preferences, which is likely due to corrupt encryption key or bad app cache backup/restore. Removing all keys and returning null.");
System.Diagnostics.Debug.WriteLine(ex);

// Delete the shared preferences
DeleteSharedPreferences();

// Try to return a new instance now that we've deleted the old
return CreateEncryptedSharedPreferences();
}
catch (System.Exception ex2)
{
// If we still can't create things, we'll have to give up and return null
// TODO: Use Logger here?
System.Diagnostics.Debug.WriteLine("Still unable to create encrypted shared preferences after attempting to deleting them. Returning null.");
System.Diagnostics.Debug.WriteLine(ex2);
}
return null;
}
}

ISharedPreferences CreateEncryptedSharedPreferences()
{
var context = Application.Context;

var prefsMainKey = new MasterKey.Builder(context, Alias)
.SetKeyScheme(MasterKey.KeyScheme.Aes256Gcm)
.Build();

return EncryptedSharedPreferences.Create(
context,
Alias,
prefsMainKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.Aes256Siv,
EncryptedSharedPreferences.PrefValueEncryptionScheme.Aes256Gcm);
}
}
}

0 comments on commit 65d3f94

Please sign in to comment.