bitwarden-android/src/iOS/Services/KeyChainStorageService.cs
2016-05-02 02:52:09 -04:00

91 lines
2.5 KiB
C#

using System;
using System.Runtime.CompilerServices;
using Bit.App.Abstractions;
using Foundation;
using Security;
namespace Bit.iOS.Services
{
public class KeyChainStorageService : ISecureStorageService
{
public void Store(string key, byte[] dataBytes)
{
using(var data = NSData.FromArray(dataBytes))
using(var newRecord = GetKeyRecord(key, data))
{
Delete(key);
CheckError(SecKeyChain.Add(newRecord));
}
}
public byte[] Retrieve(string key)
{
SecStatusCode resultCode;
using(var existingRecord = GetKeyRecord(key))
using(var record = SecKeyChain.QueryAsRecord(existingRecord, out resultCode))
{
if(resultCode == SecStatusCode.ItemNotFound)
{
return null;
}
CheckError(resultCode);
return record.Generic.ToArray();
}
}
public void Delete(string key)
{
using(var record = GetExistingRecord(key))
{
if(record != null)
{
CheckError(SecKeyChain.Remove(record));
}
}
}
public bool Contains(string key)
{
using(var existingRecord = GetExistingRecord(key))
{
return existingRecord != null;
}
}
private static void CheckError(SecStatusCode resultCode, [CallerMemberName] string caller = null)
{
if(resultCode != SecStatusCode.Success)
{
throw new Exception(string.Format("Failed to execute {0}. Result code: {1}", caller, resultCode));
}
}
private static SecRecord GetKeyRecord(string key, NSData data = null)
{
var record = new SecRecord(SecKind.GenericPassword)
{
Service = NSBundle.MainBundle.BundleIdentifier,
Account = key
};
if(data != null)
{
record.Generic = data;
}
return record;
}
private static SecRecord GetExistingRecord(string key)
{
var existingRecord = GetKeyRecord(key);
SecStatusCode resultCode;
SecKeyChain.QueryAsRecord(existingRecord, out resultCode);
return resultCode == SecStatusCode.Success ? existingRecord : null;
}
}
}