Permalink
Browse files

GH-304: Added `Remove` and `RemoveAll` methods to SecureStorage (#330)

* [secure-storage] Added `Remove` and `RemoveAll`

* [secure-storage] Added tests for Remove/RemoveAll

* [secure-storage] Remove unnecessary method

We no longer consider the `SecAccessible` value when _retrieving_  KeyChain records anymore, so it’s unnecessary to pass this value into the Get call, and unnecessary to have a platform specific overload with this parameter for getting a record.

* [secure-storage] updated docs

* [secure-storage] update sample with remove methods

* [secure-storage] Fix GetAsync call for iOS

We don’t use the overload with SecAccessible any longer.

* Fix android remove, we must do a md5hash of the key as that is how it was stored :)

* [secure-storage] Fix iOS PlatformGet
  • Loading branch information...
Redth committed Jun 28, 2018
1 parent 7e3e6a7 commit 26c14c78f77ff7e8f3e4640e45bbb84224bf41d2
@@ -19,7 +19,7 @@ public async Task Saves_And_Loads(string key, string data, bool emulatePreApi23)
// Try the new platform specific api
await SecureStorage.SetAsync(key, data, Security.SecAccessible.AfterFirstUnlock);

var b = await SecureStorage.GetAsync(key, Security.SecAccessible.AfterFirstUnlock);
var b = await SecureStorage.GetAsync(key);

Assert.Equal(data, b);
#endif
@@ -47,5 +47,43 @@ public async Task Non_Existent_Key_Returns_Null(bool emulatePreApi23)

Assert.Null(v);
}

[Theory]
[InlineData("KEY_TO_REMOVE1", true)]
[InlineData("KEY_TO_REMOVE2", false)]
public async Task Remove_Key(string key, bool emulatePreApi23)
{
#if __ANDROID__
SecureStorage.AlwaysUseAsymmetricKeyStorage = emulatePreApi23;
#endif
await SecureStorage.SetAsync(key, "Irrelevant Data");

SecureStorage.Remove(key);

var v = await SecureStorage.GetAsync(key);

Assert.Null(v);
}

[Theory]
[InlineData(true, new[] { "KEYS_TO_REMOVEA1", "KEYS_TO_REMOVEA2" })]
[InlineData(false, new[] { "KEYS_TO_REMOVEB1", "KEYS_TO_REMOVEB2" })]
public async Task Remove_All_Keys(bool emulatePreApi23, string[] keys)
{
#if __ANDROID__
SecureStorage.AlwaysUseAsymmetricKeyStorage = emulatePreApi23;
#endif

// Set a couple keys
foreach (var key in keys)
await SecureStorage.SetAsync(key, "Irrelevant Data");

// Remove them all
SecureStorage.RemoveAll();

// Make sure they are all removed
foreach (var key in keys)
Assert.Null(await SecureStorage.GetAsync(key));
}
}
}
@@ -21,6 +21,9 @@

<Button Text="Load" Command="{Binding LoadCommand}" IsEnabled="{Binding IsNotBusy}" />
<Button Text="Save" Command="{Binding SaveCommand}" IsEnabled="{Binding IsNotBusy}" />
<Button Text="Remove" Command="{Binding RemoveCommand}" IsEnabled="{Binding IsNotBusy}" />

<Button Text="Remove All" Command="{Binding RemoveAllCommand}" IsEnabled="{Binding IsNotBusy}" Margin="0,10,0,0" />
</StackLayout>
</ScrollView>
</StackLayout>
@@ -14,6 +14,8 @@ public SecureStorageViewModel()
{
LoadCommand = new Command(OnLoad);
SaveCommand = new Command(OnSave);
RemoveCommand = new Command(OnRemove);
RemoveAllCommand = new Command(OnRemoveAll);
}

public string Key
@@ -32,6 +34,10 @@ public string SecuredValue

public ICommand SaveCommand { get; }

public ICommand RemoveCommand { get; }

public ICommand RemoveAllCommand { get; }

async void OnLoad()
{
if (IsBusy)
@@ -66,5 +72,39 @@ async void OnSave()
}
IsBusy = false;
}

async void OnRemove()
{
if (IsBusy)
return;
IsBusy = true;

try
{
SecureStorage.Remove(Key);
}
catch (Exception ex)
{
await DisplayAlertAsync(ex.Message);
}
IsBusy = false;
}

async void OnRemoveAll()
{
if (IsBusy)
return;
IsBusy = true;

try
{
SecureStorage.RemoveAll();
}
catch (Exception ex)
{
await DisplayAlertAsync(ex.Message);
}
IsBusy = false;
}
}
}
@@ -51,6 +51,42 @@ static Task PlatformSetAsync(string key, string data)
return Task.CompletedTask;
}

static bool PlatformRemove(string key)
{
var context = Platform.AppContext;

key = Utils.Md5Hash(key);

using (var prefs = context.GetSharedPreferences(Alias, FileCreationMode.Private))
{
if (prefs.Contains(key))
{
using (var prefsEditor = prefs.Edit())
{
prefsEditor.Remove(key);
prefsEditor.Commit();
return true;
}
}
}

return false;
}

static void PlatformRemoveAll()
{
var context = Platform.AppContext;

using (var prefs = context.GetSharedPreferences(Alias, FileCreationMode.Private))
using (var prefsEditor = prefs.Edit())
{
foreach (var key in prefs.All.Keys)
prefsEditor.Remove(key);

prefsEditor.Commit();
}
}

internal static bool AlwaysUseAsymmetricKeyStorage { get; set; } = false;
}

@@ -11,16 +11,6 @@ public static partial class SecureStorage
public static SecAccessible DefaultAccessible { get; set; } =
SecAccessible.AfterFirstUnlock;

public static Task<string> GetAsync(string key, SecAccessible accessible)
{
if (string.IsNullOrWhiteSpace(key))
throw new ArgumentNullException(nameof(key));

var kc = new KeyChain(accessible);

return Task.FromResult(kc.ValueForKey(key, Alias));
}

public static Task SetAsync(string key, string value, SecAccessible accessible)
{
if (string.IsNullOrWhiteSpace(key))
@@ -35,11 +25,33 @@ public static Task SetAsync(string key, string value, SecAccessible accessible)
return Task.CompletedTask;
}

static Task<string> PlatformGetAsync(string key) =>
GetAsync(key, DefaultAccessible);
static Task<string> PlatformGetAsync(string key)
{
if (string.IsNullOrWhiteSpace(key))
throw new ArgumentNullException(nameof(key));

var kc = new KeyChain(DefaultAccessible);
var value = kc.ValueForKey(key, Alias);

return Task.FromResult(value);
}

static Task PlatformSetAsync(string key, string data) =>
SetAsync(key, data, DefaultAccessible);

static bool PlatformRemove(string key)
{
var kc = new KeyChain(DefaultAccessible);

return kc.Remove(key, Alias);
}

static void PlatformRemoveAll()
{
var kc = new KeyChain(DefaultAccessible);

kc.RemoveAll(Alias);
}
}

class KeyChain
@@ -89,6 +101,30 @@ internal void SetValueForKey(string value, string key, string service)
throw new Exception($"Error adding record: {result}");
}

internal bool Remove(string key, string service)
{
var record = ExistingRecordForKey(key, service);
var match = SecKeyChain.QueryAsRecord(record, out var resultCode);

if (resultCode == SecStatusCode.Success)
{
RemoveRecord(record);
return true;
}

return false;
}

internal void RemoveAll(string service)
{
var query = new SecRecord(SecKind.GenericPassword)
{
Service = service
};

SecKeyChain.Remove(query);
}

SecRecord CreateRecordForNewKeyValue(string key, string value, string service)
{
return new SecRecord(SecKind.GenericPassword)
@@ -9,5 +9,11 @@ public partial class SecureStorage

static Task PlatformSetAsync(string key, string data) =>
throw new NotImplementedInReferenceAssemblyException();

static bool PlatformRemove(string key) =>
throw new NotImplementedInReferenceAssemblyException();

static void PlatformRemoveAll() =>
throw new NotImplementedInReferenceAssemblyException();
}
}
@@ -25,5 +25,11 @@ public static Task SetAsync(string key, string value)

return PlatformSetAsync(key, value);
}

public static bool Remove(string key)
=> PlatformRemove(key);

public static void RemoveAll()
=> PlatformRemoveAll();
}
}
@@ -41,6 +41,26 @@ static async Task PlatformSetAsync(string key, string data)
settings.Values[key] = encBytes;
}

static bool PlatformRemove(string key)
{
var settings = GetSettings(Alias);

if (settings.Values.ContainsKey(key))
{
settings.Values.Remove(key);
return true;
}

return false;
}

static void PlatformRemoveAll()
{
var settings = GetSettings(Alias);

settings.Values.Clear();
}

static ApplicationDataContainer GetSettings(string name)
{
var localSettings = ApplicationData.Current.LocalSettings;
@@ -375,6 +375,8 @@
</Type>
<Type Name="Xamarin.Essentials.SecureStorage" Id="T:Xamarin.Essentials.SecureStorage">
<Member Id="M:Xamarin.Essentials.SecureStorage.GetAsync(System.String)" />
<Member Id="M:Xamarin.Essentials.SecureStorage.Remove(System.String)" />
<Member Id="M:Xamarin.Essentials.SecureStorage.RemoveAll" />
<Member Id="M:Xamarin.Essentials.SecureStorage.SetAsync(System.String,System.String)" />
</Type>
<Type Name="Xamarin.Essentials.SensorSpeed" Id="T:Xamarin.Essentials.SensorSpeed">
@@ -372,7 +372,8 @@
</Type>
<Type Name="Xamarin.Essentials.SecureStorage" Id="T:Xamarin.Essentials.SecureStorage">
<Member Id="M:Xamarin.Essentials.SecureStorage.GetAsync(System.String)" />
<Member Id="M:Xamarin.Essentials.SecureStorage.GetAsync(System.String,Security.SecAccessible)" />
<Member Id="M:Xamarin.Essentials.SecureStorage.Remove(System.String)" />
<Member Id="M:Xamarin.Essentials.SecureStorage.RemoveAll" />
<Member Id="M:Xamarin.Essentials.SecureStorage.SetAsync(System.String,System.String)" />
<Member Id="M:Xamarin.Essentials.SecureStorage.SetAsync(System.String,System.String,Security.SecAccessible)" />
<Member Id="P:Xamarin.Essentials.SecureStorage.DefaultAccessible" />
@@ -371,6 +371,8 @@
</Type>
<Type Name="Xamarin.Essentials.SecureStorage" Id="T:Xamarin.Essentials.SecureStorage">
<Member Id="M:Xamarin.Essentials.SecureStorage.GetAsync(System.String)" />
<Member Id="M:Xamarin.Essentials.SecureStorage.Remove(System.String)" />
<Member Id="M:Xamarin.Essentials.SecureStorage.RemoveAll" />
<Member Id="M:Xamarin.Essentials.SecureStorage.SetAsync(System.String,System.String)" />
</Type>
<Type Name="Xamarin.Essentials.SensorSpeed" Id="T:Xamarin.Essentials.SensorSpeed">
@@ -371,6 +371,8 @@
</Type>
<Type Name="Xamarin.Essentials.SecureStorage" Id="T:Xamarin.Essentials.SecureStorage">
<Member Id="M:Xamarin.Essentials.SecureStorage.GetAsync(System.String)" />
<Member Id="M:Xamarin.Essentials.SecureStorage.Remove(System.String)" />
<Member Id="M:Xamarin.Essentials.SecureStorage.RemoveAll" />
<Member Id="M:Xamarin.Essentials.SecureStorage.SetAsync(System.String,System.String)" />
</Type>
<Type Name="Xamarin.Essentials.SensorSpeed" Id="T:Xamarin.Essentials.SensorSpeed">
Oops, something went wrong.

0 comments on commit 26c14c7

Please sign in to comment.