GH-221: Add iOS SecAccessible properties (#223)

* Address #221, use AfterFirstUnlockThisDeviceOnly to match other platforms. Although allow a platform specific override to set accessible state.

* Add tests

* Added iOS specific prop to set SecAccessible

* Default to AfterFirstUnlock on iOS SecureStorage
This commit is contained in:
James Montemagno 2018-05-04 11:58:53 -07:00 коммит произвёл Jonathan Dick
Родитель 0c30bcfb94
Коммит 9295ce38c2
2 изменённых файлов: 39 добавлений и 8 удалений

Просмотреть файл

@ -23,6 +23,13 @@ namespace DeviceTests
// TODO: we don't know how to write iOS apps, it appears, so just skip for now
if (DeviceInfo.DeviceType == DeviceType.Virtual)
return;
// Try the new platform specific api
await SecureStorage.SetAsync(key, data, Security.SecAccessible.AfterFirstUnlock);
var b = await SecureStorage.GetAsync(key, Security.SecAccessible.AfterFirstUnlock);
Assert.Equal(data, b);
#endif
#if __ANDROID__

Просмотреть файл

@ -8,39 +8,62 @@ namespace Xamarin.Essentials
{
public static partial class SecureStorage
{
static Task<string> PlatformGetAsync(string key)
public static SecAccessible DefaultAccessible { get; set; } =
SecAccessible.AfterFirstUnlock;
public static Task<string> GetAsync(string key, SecAccessible accessible)
{
var kc = new KeyChain();
if (string.IsNullOrWhiteSpace(key))
throw new ArgumentNullException(nameof(key));
var kc = new KeyChain(accessible);
return Task.FromResult(kc.ValueForKey(key, Alias));
}
static Task PlatformSetAsync(string key, string data)
public static Task SetAsync(string key, string value, SecAccessible accessible)
{
var kc = new KeyChain();
kc.SetValueForKey(data, key, Alias);
if (string.IsNullOrWhiteSpace(key))
throw new ArgumentNullException(nameof(key));
if (value == null)
throw new ArgumentNullException(nameof(value));
var kc = new KeyChain(accessible);
kc.SetValueForKey(value, key, Alias);
return Task.CompletedTask;
}
static Task<string> PlatformGetAsync(string key) =>
GetAsync(key, DefaultAccessible);
static Task PlatformSetAsync(string key, string data) =>
SetAsync(key, data, DefaultAccessible);
}
class KeyChain
{
static SecRecord ExistingRecordForKey(string key, string service)
SecAccessible accessible;
internal KeyChain(SecAccessible accessible) =>
this.accessible = accessible;
SecRecord ExistingRecordForKey(string key, string service)
{
return new SecRecord(SecKind.GenericPassword)
{
Account = key,
Service = service,
Label = key,
Accessible = accessible
};
}
internal string ValueForKey(string key, string service)
{
var record = ExistingRecordForKey(key, service);
SecStatusCode resultCode;
var match = SecKeyChain.QueryAsRecord(record, out resultCode);
var match = SecKeyChain.QueryAsRecord(record, out var resultCode);
if (resultCode == SecStatusCode.Success)
return NSString.FromData(match.ValueData, NSStringEncoding.UTF8);
@ -75,6 +98,7 @@ namespace Xamarin.Essentials
Account = key,
Service = service,
Label = key,
Accessible = accessible,
ValueData = NSData.FromString(value, NSStringEncoding.UTF8),
};
}