react-native-nitro-storage 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +414 -256
- package/android/src/main/cpp/AndroidStorageAdapterCpp.cpp +98 -11
- package/android/src/main/cpp/AndroidStorageAdapterCpp.hpp +15 -0
- package/android/src/main/java/com/nitrostorage/AndroidStorageAdapter.kt +130 -33
- package/android/src/main/java/com/nitrostorage/NitroStoragePackage.kt +2 -2
- package/cpp/bindings/HybridStorage.cpp +121 -12
- package/cpp/bindings/HybridStorage.hpp +10 -0
- package/cpp/core/NativeStorageAdapter.hpp +15 -0
- package/ios/IOSStorageAdapterCpp.hpp +19 -0
- package/ios/IOSStorageAdapterCpp.mm +233 -32
- package/lib/commonjs/Storage.types.js +23 -1
- package/lib/commonjs/Storage.types.js.map +1 -1
- package/lib/commonjs/index.js +173 -32
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.web.js +289 -49
- package/lib/commonjs/index.web.js.map +1 -1
- package/lib/commonjs/internal.js +10 -0
- package/lib/commonjs/internal.js.map +1 -1
- package/lib/module/Storage.types.js +22 -0
- package/lib/module/Storage.types.js.map +1 -1
- package/lib/module/index.js +163 -35
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.web.js +278 -51
- package/lib/module/index.web.js.map +1 -1
- package/lib/module/internal.js +8 -0
- package/lib/module/internal.js.map +1 -1
- package/lib/typescript/Storage.nitro.d.ts +10 -0
- package/lib/typescript/Storage.nitro.d.ts.map +1 -1
- package/lib/typescript/Storage.types.d.ts +20 -0
- package/lib/typescript/Storage.types.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +30 -7
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/index.web.d.ts +40 -7
- package/lib/typescript/index.web.d.ts.map +1 -1
- package/lib/typescript/internal.d.ts +2 -0
- package/lib/typescript/internal.d.ts.map +1 -1
- package/nitrogen/generated/shared/c++/HybridStorageSpec.cpp +10 -0
- package/nitrogen/generated/shared/c++/HybridStorageSpec.hpp +10 -0
- package/package.json +4 -1
- package/src/Storage.nitro.ts +11 -2
- package/src/Storage.types.ts +22 -0
- package/src/index.ts +270 -71
- package/src/index.web.ts +431 -90
- package/src/internal.ts +14 -4
- package/src/migration.ts +1 -1
|
@@ -13,6 +13,9 @@ public:
|
|
|
13
13
|
virtual void setDisk(const std::string& key, const std::string& value) = 0;
|
|
14
14
|
virtual std::optional<std::string> getDisk(const std::string& key) = 0;
|
|
15
15
|
virtual void deleteDisk(const std::string& key) = 0;
|
|
16
|
+
virtual bool hasDisk(const std::string& key) = 0;
|
|
17
|
+
virtual std::vector<std::string> getAllKeysDisk() = 0;
|
|
18
|
+
virtual size_t sizeDisk() = 0;
|
|
16
19
|
virtual void setDiskBatch(const std::vector<std::string>& keys, const std::vector<std::string>& values) = 0;
|
|
17
20
|
virtual std::vector<std::optional<std::string>> getDiskBatch(const std::vector<std::string>& keys) = 0;
|
|
18
21
|
virtual void deleteDiskBatch(const std::vector<std::string>& keys) = 0;
|
|
@@ -20,12 +23,24 @@ public:
|
|
|
20
23
|
virtual void setSecure(const std::string& key, const std::string& value) = 0;
|
|
21
24
|
virtual std::optional<std::string> getSecure(const std::string& key) = 0;
|
|
22
25
|
virtual void deleteSecure(const std::string& key) = 0;
|
|
26
|
+
virtual bool hasSecure(const std::string& key) = 0;
|
|
27
|
+
virtual std::vector<std::string> getAllKeysSecure() = 0;
|
|
28
|
+
virtual size_t sizeSecure() = 0;
|
|
23
29
|
virtual void setSecureBatch(const std::vector<std::string>& keys, const std::vector<std::string>& values) = 0;
|
|
24
30
|
virtual std::vector<std::optional<std::string>> getSecureBatch(const std::vector<std::string>& keys) = 0;
|
|
25
31
|
virtual void deleteSecureBatch(const std::vector<std::string>& keys) = 0;
|
|
26
32
|
|
|
27
33
|
virtual void clearDisk() = 0;
|
|
28
34
|
virtual void clearSecure() = 0;
|
|
35
|
+
|
|
36
|
+
virtual void setSecureAccessControl(int level) = 0;
|
|
37
|
+
virtual void setKeychainAccessGroup(const std::string& group) = 0;
|
|
38
|
+
|
|
39
|
+
virtual void setSecureBiometric(const std::string& key, const std::string& value) = 0;
|
|
40
|
+
virtual std::optional<std::string> getSecureBiometric(const std::string& key) = 0;
|
|
41
|
+
virtual void deleteSecureBiometric(const std::string& key) = 0;
|
|
42
|
+
virtual bool hasSecureBiometric(const std::string& key) = 0;
|
|
43
|
+
virtual void clearSecureBiometric() = 0;
|
|
29
44
|
};
|
|
30
45
|
|
|
31
46
|
} // namespace NitroStorage
|
|
@@ -12,6 +12,9 @@ public:
|
|
|
12
12
|
void setDisk(const std::string& key, const std::string& value) override;
|
|
13
13
|
std::optional<std::string> getDisk(const std::string& key) override;
|
|
14
14
|
void deleteDisk(const std::string& key) override;
|
|
15
|
+
bool hasDisk(const std::string& key) override;
|
|
16
|
+
std::vector<std::string> getAllKeysDisk() override;
|
|
17
|
+
size_t sizeDisk() override;
|
|
15
18
|
void setDiskBatch(const std::vector<std::string>& keys, const std::vector<std::string>& values) override;
|
|
16
19
|
std::vector<std::optional<std::string>> getDiskBatch(const std::vector<std::string>& keys) override;
|
|
17
20
|
void deleteDiskBatch(const std::vector<std::string>& keys) override;
|
|
@@ -19,12 +22,28 @@ public:
|
|
|
19
22
|
void setSecure(const std::string& key, const std::string& value) override;
|
|
20
23
|
std::optional<std::string> getSecure(const std::string& key) override;
|
|
21
24
|
void deleteSecure(const std::string& key) override;
|
|
25
|
+
bool hasSecure(const std::string& key) override;
|
|
26
|
+
std::vector<std::string> getAllKeysSecure() override;
|
|
27
|
+
size_t sizeSecure() override;
|
|
22
28
|
void setSecureBatch(const std::vector<std::string>& keys, const std::vector<std::string>& values) override;
|
|
23
29
|
std::vector<std::optional<std::string>> getSecureBatch(const std::vector<std::string>& keys) override;
|
|
24
30
|
void deleteSecureBatch(const std::vector<std::string>& keys) override;
|
|
25
31
|
|
|
26
32
|
void clearDisk() override;
|
|
27
33
|
void clearSecure() override;
|
|
34
|
+
|
|
35
|
+
void setSecureAccessControl(int level) override;
|
|
36
|
+
void setKeychainAccessGroup(const std::string& group) override;
|
|
37
|
+
|
|
38
|
+
void setSecureBiometric(const std::string& key, const std::string& value) override;
|
|
39
|
+
std::optional<std::string> getSecureBiometric(const std::string& key) override;
|
|
40
|
+
void deleteSecureBiometric(const std::string& key) override;
|
|
41
|
+
bool hasSecureBiometric(const std::string& key) override;
|
|
42
|
+
void clearSecureBiometric() override;
|
|
43
|
+
|
|
44
|
+
private:
|
|
45
|
+
int accessControlLevel_ = 0;
|
|
46
|
+
std::string keychainAccessGroup_;
|
|
28
47
|
};
|
|
29
48
|
|
|
30
49
|
} // namespace NitroStorage
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
#import "IOSStorageAdapterCpp.hpp"
|
|
2
2
|
#import <Foundation/Foundation.h>
|
|
3
3
|
#import <Security/Security.h>
|
|
4
|
+
#import <LocalAuthentication/LocalAuthentication.h>
|
|
5
|
+
#include <algorithm>
|
|
4
6
|
|
|
5
7
|
namespace NitroStorage {
|
|
6
8
|
|
|
7
9
|
static NSString* const kKeychainService = @"com.nitrostorage.keychain";
|
|
10
|
+
static NSString* const kBiometricKeychainService = @"com.nitrostorage.biometric";
|
|
8
11
|
static NSString* const kDiskSuiteName = @"com.nitrostorage.disk";
|
|
9
12
|
|
|
10
13
|
static NSUserDefaults* NitroDiskDefaults() {
|
|
@@ -12,9 +15,21 @@ static NSUserDefaults* NitroDiskDefaults() {
|
|
|
12
15
|
return defaults ?: [NSUserDefaults standardUserDefaults];
|
|
13
16
|
}
|
|
14
17
|
|
|
18
|
+
static CFStringRef accessControlAttr(int level) {
|
|
19
|
+
switch (level) {
|
|
20
|
+
case 1: return kSecAttrAccessibleAfterFirstUnlock;
|
|
21
|
+
case 2: return kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly;
|
|
22
|
+
case 3: return kSecAttrAccessibleWhenUnlockedThisDeviceOnly;
|
|
23
|
+
case 4: return kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly;
|
|
24
|
+
default: return kSecAttrAccessibleWhenUnlocked;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
15
28
|
IOSStorageAdapterCpp::IOSStorageAdapterCpp() {}
|
|
16
29
|
IOSStorageAdapterCpp::~IOSStorageAdapterCpp() {}
|
|
17
30
|
|
|
31
|
+
// --- Disk ---
|
|
32
|
+
|
|
18
33
|
void IOSStorageAdapterCpp::setDisk(const std::string& key, const std::string& value) {
|
|
19
34
|
NSString* nsKey = [NSString stringWithUTF8String:key.c_str()];
|
|
20
35
|
NSString* nsValue = [NSString stringWithUTF8String:value.c_str()];
|
|
@@ -48,6 +63,25 @@ void IOSStorageAdapterCpp::deleteDisk(const std::string& key) {
|
|
|
48
63
|
[[NSUserDefaults standardUserDefaults] removeObjectForKey:nsKey];
|
|
49
64
|
}
|
|
50
65
|
|
|
66
|
+
bool IOSStorageAdapterCpp::hasDisk(const std::string& key) {
|
|
67
|
+
NSString* nsKey = [NSString stringWithUTF8String:key.c_str()];
|
|
68
|
+
return [NitroDiskDefaults() objectForKey:nsKey] != nil;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
std::vector<std::string> IOSStorageAdapterCpp::getAllKeysDisk() {
|
|
72
|
+
NSDictionary<NSString*, id>* entries = [NitroDiskDefaults() dictionaryRepresentation];
|
|
73
|
+
std::vector<std::string> keys;
|
|
74
|
+
keys.reserve(entries.count);
|
|
75
|
+
for (NSString* key in entries) {
|
|
76
|
+
keys.push_back(std::string([key UTF8String]));
|
|
77
|
+
}
|
|
78
|
+
return keys;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
size_t IOSStorageAdapterCpp::sizeDisk() {
|
|
82
|
+
return [NitroDiskDefaults() dictionaryRepresentation].count;
|
|
83
|
+
}
|
|
84
|
+
|
|
51
85
|
void IOSStorageAdapterCpp::setDiskBatch(
|
|
52
86
|
const std::vector<std::string>& keys,
|
|
53
87
|
const std::vector<std::string>& values
|
|
@@ -78,15 +112,63 @@ void IOSStorageAdapterCpp::deleteDiskBatch(const std::vector<std::string>& keys)
|
|
|
78
112
|
}
|
|
79
113
|
}
|
|
80
114
|
|
|
115
|
+
void IOSStorageAdapterCpp::clearDisk() {
|
|
116
|
+
NSUserDefaults* defaults = NitroDiskDefaults();
|
|
117
|
+
NSDictionary<NSString*, id>* entries = [defaults dictionaryRepresentation];
|
|
118
|
+
for (NSString* key in entries) {
|
|
119
|
+
[defaults removeObjectForKey:key];
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// --- Secure (Keychain) ---
|
|
124
|
+
|
|
125
|
+
static NSMutableDictionary* baseKeychainQuery(NSString* key, NSString* service, NSString* accessGroup) {
|
|
126
|
+
NSMutableDictionary* query = [@{
|
|
127
|
+
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
|
|
128
|
+
(__bridge id)kSecAttrService: service,
|
|
129
|
+
(__bridge id)kSecAttrAccount: key
|
|
130
|
+
} mutableCopy];
|
|
131
|
+
if (accessGroup && accessGroup.length > 0) {
|
|
132
|
+
query[(__bridge id)kSecAttrAccessGroup] = accessGroup;
|
|
133
|
+
}
|
|
134
|
+
return query;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
static NSMutableDictionary* allAccountsQuery(NSString* service, NSString* accessGroup) {
|
|
138
|
+
NSMutableDictionary* query = [@{
|
|
139
|
+
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
|
|
140
|
+
(__bridge id)kSecAttrService: service,
|
|
141
|
+
(__bridge id)kSecReturnAttributes: @YES,
|
|
142
|
+
(__bridge id)kSecMatchLimit: (__bridge id)kSecMatchLimitAll
|
|
143
|
+
} mutableCopy];
|
|
144
|
+
if (accessGroup && accessGroup.length > 0) {
|
|
145
|
+
query[(__bridge id)kSecAttrAccessGroup] = accessGroup;
|
|
146
|
+
}
|
|
147
|
+
return query;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
static std::vector<std::string> keychainAccountsForService(NSString* service, NSString* accessGroup) {
|
|
151
|
+
NSMutableDictionary* query = allAccountsQuery(service, accessGroup);
|
|
152
|
+
CFTypeRef result = NULL;
|
|
153
|
+
std::vector<std::string> keys;
|
|
154
|
+
if (SecItemCopyMatching((__bridge CFDictionaryRef)query, &result) == errSecSuccess && result) {
|
|
155
|
+
NSArray* items = (__bridge_transfer NSArray*)result;
|
|
156
|
+
keys.reserve(items.count);
|
|
157
|
+
for (NSDictionary* item in items) {
|
|
158
|
+
NSString* account = item[(__bridge id)kSecAttrAccount];
|
|
159
|
+
if (account) {
|
|
160
|
+
keys.push_back(std::string([account UTF8String]));
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return keys;
|
|
165
|
+
}
|
|
166
|
+
|
|
81
167
|
void IOSStorageAdapterCpp::setSecure(const std::string& key, const std::string& value) {
|
|
82
168
|
NSString* nsKey = [NSString stringWithUTF8String:key.c_str()];
|
|
83
169
|
NSData* data = [[NSString stringWithUTF8String:value.c_str()] dataUsingEncoding:NSUTF8StringEncoding];
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
|
|
87
|
-
(__bridge id)kSecAttrService: kKeychainService,
|
|
88
|
-
(__bridge id)kSecAttrAccount: nsKey
|
|
89
|
-
};
|
|
170
|
+
NSString* group = keychainAccessGroup_.empty() ? nil : [NSString stringWithUTF8String:keychainAccessGroup_.c_str()];
|
|
171
|
+
NSMutableDictionary* query = baseKeychainQuery(nsKey, kKeychainService, group);
|
|
90
172
|
|
|
91
173
|
NSDictionary* updateAttributes = @{
|
|
92
174
|
(__bridge id)kSecValueData: data
|
|
@@ -95,21 +177,18 @@ void IOSStorageAdapterCpp::setSecure(const std::string& key, const std::string&
|
|
|
95
177
|
OSStatus status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)updateAttributes);
|
|
96
178
|
|
|
97
179
|
if (status == errSecItemNotFound) {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
SecItemAdd((__bridge CFDictionaryRef)addQuery, NULL);
|
|
180
|
+
query[(__bridge id)kSecValueData] = data;
|
|
181
|
+
query[(__bridge id)kSecAttrAccessible] = (__bridge id)accessControlAttr(accessControlLevel_);
|
|
182
|
+
SecItemAdd((__bridge CFDictionaryRef)query, NULL);
|
|
102
183
|
}
|
|
103
184
|
}
|
|
104
185
|
|
|
105
186
|
std::optional<std::string> IOSStorageAdapterCpp::getSecure(const std::string& key) {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
(__bridge id)kSecMatchLimit: (__bridge id)kSecMatchLimitOne
|
|
112
|
-
};
|
|
187
|
+
NSString* nsKey = [NSString stringWithUTF8String:key.c_str()];
|
|
188
|
+
NSString* group = keychainAccessGroup_.empty() ? nil : [NSString stringWithUTF8String:keychainAccessGroup_.c_str()];
|
|
189
|
+
NSMutableDictionary* query = baseKeychainQuery(nsKey, kKeychainService, group);
|
|
190
|
+
query[(__bridge id)kSecReturnData] = @YES;
|
|
191
|
+
query[(__bridge id)kSecMatchLimit] = (__bridge id)kSecMatchLimitOne;
|
|
113
192
|
|
|
114
193
|
CFTypeRef result = NULL;
|
|
115
194
|
if (SecItemCopyMatching((__bridge CFDictionaryRef)query, &result) == errSecSuccess && result) {
|
|
@@ -121,12 +200,39 @@ std::optional<std::string> IOSStorageAdapterCpp::getSecure(const std::string& ke
|
|
|
121
200
|
}
|
|
122
201
|
|
|
123
202
|
void IOSStorageAdapterCpp::deleteSecure(const std::string& key) {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
SecItemDelete((__bridge CFDictionaryRef)
|
|
203
|
+
NSString* nsKey = [NSString stringWithUTF8String:key.c_str()];
|
|
204
|
+
NSString* group = keychainAccessGroup_.empty() ? nil : [NSString stringWithUTF8String:keychainAccessGroup_.c_str()];
|
|
205
|
+
NSMutableDictionary* secureQuery = baseKeychainQuery(nsKey, kKeychainService, group);
|
|
206
|
+
SecItemDelete((__bridge CFDictionaryRef)secureQuery);
|
|
207
|
+
NSMutableDictionary* biometricQuery = baseKeychainQuery(nsKey, kBiometricKeychainService, group);
|
|
208
|
+
SecItemDelete((__bridge CFDictionaryRef)biometricQuery);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
bool IOSStorageAdapterCpp::hasSecure(const std::string& key) {
|
|
212
|
+
NSString* nsKey = [NSString stringWithUTF8String:key.c_str()];
|
|
213
|
+
NSString* group = keychainAccessGroup_.empty() ? nil : [NSString stringWithUTF8String:keychainAccessGroup_.c_str()];
|
|
214
|
+
NSMutableDictionary* secureQuery = baseKeychainQuery(nsKey, kKeychainService, group);
|
|
215
|
+
if (SecItemCopyMatching((__bridge CFDictionaryRef)secureQuery, NULL) == errSecSuccess) {
|
|
216
|
+
return true;
|
|
217
|
+
}
|
|
218
|
+
NSMutableDictionary* biometricQuery = baseKeychainQuery(nsKey, kBiometricKeychainService, group);
|
|
219
|
+
return SecItemCopyMatching((__bridge CFDictionaryRef)biometricQuery, NULL) == errSecSuccess;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
std::vector<std::string> IOSStorageAdapterCpp::getAllKeysSecure() {
|
|
223
|
+
NSString* group = keychainAccessGroup_.empty() ? nil : [NSString stringWithUTF8String:keychainAccessGroup_.c_str()];
|
|
224
|
+
std::vector<std::string> keys = keychainAccountsForService(kKeychainService, group);
|
|
225
|
+
const std::vector<std::string> biometricKeys = keychainAccountsForService(kBiometricKeychainService, group);
|
|
226
|
+
for (const auto& key : biometricKeys) {
|
|
227
|
+
if (std::find(keys.begin(), keys.end(), key) == keys.end()) {
|
|
228
|
+
keys.push_back(key);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return keys;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
size_t IOSStorageAdapterCpp::sizeSecure() {
|
|
235
|
+
return getAllKeysSecure().size();
|
|
130
236
|
}
|
|
131
237
|
|
|
132
238
|
void IOSStorageAdapterCpp::setSecureBatch(
|
|
@@ -155,19 +261,114 @@ void IOSStorageAdapterCpp::deleteSecureBatch(const std::vector<std::string>& key
|
|
|
155
261
|
}
|
|
156
262
|
}
|
|
157
263
|
|
|
158
|
-
void IOSStorageAdapterCpp::
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
264
|
+
void IOSStorageAdapterCpp::clearSecure() {
|
|
265
|
+
NSString* group = keychainAccessGroup_.empty() ? nil : [NSString stringWithUTF8String:keychainAccessGroup_.c_str()];
|
|
266
|
+
NSMutableDictionary* secureQuery = [@{
|
|
267
|
+
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
|
|
268
|
+
(__bridge id)kSecAttrService: kKeychainService
|
|
269
|
+
} mutableCopy];
|
|
270
|
+
if (group && group.length > 0) {
|
|
271
|
+
secureQuery[(__bridge id)kSecAttrAccessGroup] = group;
|
|
272
|
+
}
|
|
273
|
+
SecItemDelete((__bridge CFDictionaryRef)secureQuery);
|
|
274
|
+
|
|
275
|
+
NSMutableDictionary* biometricQuery = [@{
|
|
276
|
+
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
|
|
277
|
+
(__bridge id)kSecAttrService: kBiometricKeychainService
|
|
278
|
+
} mutableCopy];
|
|
279
|
+
if (group && group.length > 0) {
|
|
280
|
+
biometricQuery[(__bridge id)kSecAttrAccessGroup] = group;
|
|
163
281
|
}
|
|
282
|
+
SecItemDelete((__bridge CFDictionaryRef)biometricQuery);
|
|
164
283
|
}
|
|
165
284
|
|
|
166
|
-
|
|
167
|
-
|
|
285
|
+
// --- Configuration ---
|
|
286
|
+
|
|
287
|
+
void IOSStorageAdapterCpp::setSecureAccessControl(int level) {
|
|
288
|
+
accessControlLevel_ = level;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
void IOSStorageAdapterCpp::setKeychainAccessGroup(const std::string& group) {
|
|
292
|
+
keychainAccessGroup_ = group;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// --- Biometric (separate Keychain service with biometric ACL) ---
|
|
296
|
+
|
|
297
|
+
void IOSStorageAdapterCpp::setSecureBiometric(const std::string& key, const std::string& value) {
|
|
298
|
+
NSString* nsKey = [NSString stringWithUTF8String:key.c_str()];
|
|
299
|
+
NSData* data = [[NSString stringWithUTF8String:value.c_str()] dataUsingEncoding:NSUTF8StringEncoding];
|
|
300
|
+
NSString* group = keychainAccessGroup_.empty() ? nil : [NSString stringWithUTF8String:keychainAccessGroup_.c_str()];
|
|
301
|
+
|
|
302
|
+
// Delete existing item first (access control can't be updated in place)
|
|
303
|
+
NSMutableDictionary* deleteQuery = baseKeychainQuery(nsKey, kBiometricKeychainService, group);
|
|
304
|
+
SecItemDelete((__bridge CFDictionaryRef)deleteQuery);
|
|
305
|
+
|
|
306
|
+
CFErrorRef error = NULL;
|
|
307
|
+
SecAccessControlRef access = SecAccessControlCreateWithFlags(
|
|
308
|
+
kCFAllocatorDefault,
|
|
309
|
+
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
|
|
310
|
+
kSecAccessControlBiometryCurrentSet,
|
|
311
|
+
&error
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
if (error || !access) {
|
|
315
|
+
if (access) CFRelease(access);
|
|
316
|
+
throw std::runtime_error("NitroStorage: Failed to create biometric access control");
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
NSMutableDictionary* attrs = baseKeychainQuery(nsKey, kBiometricKeychainService, group);
|
|
320
|
+
attrs[(__bridge id)kSecValueData] = data;
|
|
321
|
+
attrs[(__bridge id)kSecAttrAccessControl] = (__bridge_transfer id)access;
|
|
322
|
+
|
|
323
|
+
OSStatus status = SecItemAdd((__bridge CFDictionaryRef)attrs, NULL);
|
|
324
|
+
if (status != errSecSuccess) {
|
|
325
|
+
throw std::runtime_error("NitroStorage: Biometric set failed with status " + std::to_string(status));
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
std::optional<std::string> IOSStorageAdapterCpp::getSecureBiometric(const std::string& key) {
|
|
330
|
+
NSString* nsKey = [NSString stringWithUTF8String:key.c_str()];
|
|
331
|
+
NSString* group = keychainAccessGroup_.empty() ? nil : [NSString stringWithUTF8String:keychainAccessGroup_.c_str()];
|
|
332
|
+
NSMutableDictionary* query = baseKeychainQuery(nsKey, kBiometricKeychainService, group);
|
|
333
|
+
query[(__bridge id)kSecReturnData] = @YES;
|
|
334
|
+
query[(__bridge id)kSecMatchLimit] = (__bridge id)kSecMatchLimitOne;
|
|
335
|
+
|
|
336
|
+
CFTypeRef result = NULL;
|
|
337
|
+
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
|
|
338
|
+
if (status == errSecSuccess && result) {
|
|
339
|
+
NSData* data = (__bridge_transfer NSData*)result;
|
|
340
|
+
NSString* str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
|
341
|
+
if (str) return std::string([str UTF8String]);
|
|
342
|
+
}
|
|
343
|
+
if (status == errSecUserCanceled || status == errSecAuthFailed) {
|
|
344
|
+
throw std::runtime_error("NitroStorage: Biometric authentication failed");
|
|
345
|
+
}
|
|
346
|
+
return std::nullopt;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
void IOSStorageAdapterCpp::deleteSecureBiometric(const std::string& key) {
|
|
350
|
+
NSString* nsKey = [NSString stringWithUTF8String:key.c_str()];
|
|
351
|
+
NSString* group = keychainAccessGroup_.empty() ? nil : [NSString stringWithUTF8String:keychainAccessGroup_.c_str()];
|
|
352
|
+
NSMutableDictionary* query = baseKeychainQuery(nsKey, kBiometricKeychainService, group);
|
|
353
|
+
SecItemDelete((__bridge CFDictionaryRef)query);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
bool IOSStorageAdapterCpp::hasSecureBiometric(const std::string& key) {
|
|
357
|
+
NSString* nsKey = [NSString stringWithUTF8String:key.c_str()];
|
|
358
|
+
NSString* group = keychainAccessGroup_.empty() ? nil : [NSString stringWithUTF8String:keychainAccessGroup_.c_str()];
|
|
359
|
+
NSMutableDictionary* query = baseKeychainQuery(nsKey, kBiometricKeychainService, group);
|
|
360
|
+
return SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL) == errSecSuccess;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
void IOSStorageAdapterCpp::clearSecureBiometric() {
|
|
364
|
+
NSString* group = keychainAccessGroup_.empty() ? nil : [NSString stringWithUTF8String:keychainAccessGroup_.c_str()];
|
|
365
|
+
NSMutableDictionary* query = [@{
|
|
168
366
|
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
|
|
169
|
-
(__bridge id)kSecAttrService:
|
|
170
|
-
};
|
|
367
|
+
(__bridge id)kSecAttrService: kBiometricKeychainService
|
|
368
|
+
} mutableCopy];
|
|
369
|
+
if (group && group.length > 0) {
|
|
370
|
+
query[(__bridge id)kSecAttrAccessGroup] = group;
|
|
371
|
+
}
|
|
171
372
|
SecItemDelete((__bridge CFDictionaryRef)query);
|
|
172
373
|
}
|
|
173
374
|
|
|
@@ -3,11 +3,33 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.StorageScope = void 0;
|
|
6
|
+
exports.StorageScope = exports.BiometricLevel = exports.AccessControl = void 0;
|
|
7
7
|
let StorageScope = exports.StorageScope = /*#__PURE__*/function (StorageScope) {
|
|
8
8
|
StorageScope[StorageScope["Memory"] = 0] = "Memory";
|
|
9
9
|
StorageScope[StorageScope["Disk"] = 1] = "Disk";
|
|
10
10
|
StorageScope[StorageScope["Secure"] = 2] = "Secure";
|
|
11
11
|
return StorageScope;
|
|
12
12
|
}({});
|
|
13
|
+
let AccessControl = exports.AccessControl = /*#__PURE__*/function (AccessControl) {
|
|
14
|
+
/** Accessible when unlocked (default). */
|
|
15
|
+
AccessControl[AccessControl["WhenUnlocked"] = 0] = "WhenUnlocked";
|
|
16
|
+
/** Accessible after first unlock until restart. Good for background token refresh. */
|
|
17
|
+
AccessControl[AccessControl["AfterFirstUnlock"] = 1] = "AfterFirstUnlock";
|
|
18
|
+
/** Accessible only when passcode is set, non-migratable. */
|
|
19
|
+
AccessControl[AccessControl["WhenPasscodeSetThisDeviceOnly"] = 2] = "WhenPasscodeSetThisDeviceOnly";
|
|
20
|
+
/** Same as WhenUnlocked but non-migratable between devices. */
|
|
21
|
+
AccessControl[AccessControl["WhenUnlockedThisDeviceOnly"] = 3] = "WhenUnlockedThisDeviceOnly";
|
|
22
|
+
/** Same as AfterFirstUnlock but non-migratable. */
|
|
23
|
+
AccessControl[AccessControl["AfterFirstUnlockThisDeviceOnly"] = 4] = "AfterFirstUnlockThisDeviceOnly";
|
|
24
|
+
return AccessControl;
|
|
25
|
+
}({});
|
|
26
|
+
let BiometricLevel = exports.BiometricLevel = /*#__PURE__*/function (BiometricLevel) {
|
|
27
|
+
/** No biometric requirement (default). */
|
|
28
|
+
BiometricLevel[BiometricLevel["None"] = 0] = "None";
|
|
29
|
+
/** Require biometric or passcode for each access. */
|
|
30
|
+
BiometricLevel[BiometricLevel["BiometryOrPasscode"] = 1] = "BiometryOrPasscode";
|
|
31
|
+
/** Require biometric only (no passcode fallback). */
|
|
32
|
+
BiometricLevel[BiometricLevel["BiometryOnly"] = 2] = "BiometryOnly";
|
|
33
|
+
return BiometricLevel;
|
|
34
|
+
}({});
|
|
13
35
|
//# sourceMappingURL=Storage.types.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["StorageScope","exports"],"sourceRoot":"../../src","sources":["Storage.types.ts"],"mappings":";;;;;;IAAYA,YAAY,GAAAC,OAAA,CAAAD,YAAA,0BAAZA,YAAY;EAAZA,YAAY,CAAZA,YAAY;EAAZA,YAAY,CAAZA,YAAY;EAAZA,YAAY,CAAZA,YAAY;EAAA,OAAZA,YAAY;AAAA","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":["StorageScope","exports","AccessControl","BiometricLevel"],"sourceRoot":"../../src","sources":["Storage.types.ts"],"mappings":";;;;;;IAAYA,YAAY,GAAAC,OAAA,CAAAD,YAAA,0BAAZA,YAAY;EAAZA,YAAY,CAAZA,YAAY;EAAZA,YAAY,CAAZA,YAAY;EAAZA,YAAY,CAAZA,YAAY;EAAA,OAAZA,YAAY;AAAA;AAAA,IAMZE,aAAa,GAAAD,OAAA,CAAAC,aAAA,0BAAbA,aAAa;EACvB;EADUA,aAAa,CAAbA,aAAa;EAGvB;EAHUA,aAAa,CAAbA,aAAa;EAKvB;EALUA,aAAa,CAAbA,aAAa;EAOvB;EAPUA,aAAa,CAAbA,aAAa;EASvB;EATUA,aAAa,CAAbA,aAAa;EAAA,OAAbA,aAAa;AAAA;AAAA,IAabC,cAAc,GAAAF,OAAA,CAAAE,cAAA,0BAAdA,cAAc;EACxB;EADUA,cAAc,CAAdA,cAAc;EAGxB;EAHUA,cAAc,CAAdA,cAAc;EAKxB;EALUA,cAAc,CAAdA,cAAc;EAAA,OAAdA,cAAc;AAAA","ignoreList":[]}
|