forked from xamarin/Essentials
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSecureStorage.ios.cs
150 lines (124 loc) · 4.46 KB
/
SecureStorage.ios.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
using System;
using System.Threading.Tasks;
using Foundation;
using Security;
namespace Xamarin.Essentials
{
public static partial class SecureStorage
{
public static SecAccessible DefaultAccessible { get; set; } =
SecAccessible.AfterFirstUnlock;
public static Task SetAsync(string key, string value, SecAccessible accessible)
{
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)
{
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
{
SecAccessible accessible;
internal KeyChain(SecAccessible accessible) =>
this.accessible = accessible;
SecRecord ExistingRecordForKey(string key, string service)
{
return new SecRecord(SecKind.GenericPassword)
{
Account = key,
Service = service
};
}
internal string ValueForKey(string key, string service)
{
using (var record = ExistingRecordForKey(key, service))
using (var match = SecKeyChain.QueryAsRecord(record, out var resultCode))
{
if (resultCode == SecStatusCode.Success)
return NSString.FromData(match.ValueData, NSStringEncoding.UTF8);
else
return null;
}
}
internal void SetValueForKey(string value, string key, string service)
{
using (var record = ExistingRecordForKey(key, service))
{
if (string.IsNullOrEmpty(value))
{
if (!string.IsNullOrEmpty(ValueForKey(key, service)))
RemoveRecord(record);
return;
}
// if the key already exists, remove it
if (!string.IsNullOrEmpty(ValueForKey(key, service)))
RemoveRecord(record);
}
using (var newRecord = CreateRecordForNewKeyValue(key, value, service))
{
var result = SecKeyChain.Add(newRecord);
if (result != SecStatusCode.Success)
throw new Exception($"Error adding record: {result}");
}
}
internal bool Remove(string key, string service)
{
using (var record = ExistingRecordForKey(key, service))
using (var match = SecKeyChain.QueryAsRecord(record, out var resultCode))
{
if (resultCode == SecStatusCode.Success)
{
RemoveRecord(record);
return true;
}
}
return false;
}
internal void RemoveAll(string service)
{
using (var query = new SecRecord(SecKind.GenericPassword) { Service = service })
{
SecKeyChain.Remove(query);
}
}
SecRecord CreateRecordForNewKeyValue(string key, string value, string service)
{
return new SecRecord(SecKind.GenericPassword)
{
Account = key,
Service = service,
Label = key,
Accessible = accessible,
ValueData = NSData.FromString(value, NSStringEncoding.UTF8),
};
}
bool RemoveRecord(SecRecord record)
{
var result = SecKeyChain.Remove(record);
if (result != SecStatusCode.Success && result != SecStatusCode.ItemNotFound)
throw new Exception($"Error removing record: {result}");
return true;
}
}
}