react-native-nitro-storage 0.5.6 → 0.5.8

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.
@@ -190,61 +190,27 @@ static NSMutableDictionary* allAccountsQuery(NSString* service, NSString* access
190
190
  return query;
191
191
  }
192
192
 
193
- static std::vector<std::string> keychainAccountsForService(NSString* service, NSString* accessGroup) {
194
- NSMutableDictionary* query = allAccountsQuery(service, accessGroup);
195
- disableKeychainInteraction(query);
196
- CFTypeRef result = NULL;
197
- std::vector<std::string> keys;
198
- OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
199
- if (status == errSecInteractionNotAllowed) {
200
- throw taggedStorageError(
201
- "keychain_locked",
202
- "NitroStorage: Keychain is locked (errSecInteractionNotAllowed). "
203
- "The item is not accessible until the device is unlocked."
204
- );
205
- }
206
- if (status == errSecSuccess && result) {
207
- id items = (__bridge_transfer id)result;
208
- NSArray* itemArray = nil;
209
- if ([items isKindOfClass:[NSArray class]]) {
210
- itemArray = (NSArray*)items;
211
- } else if ([items isKindOfClass:[NSDictionary class]]) {
212
- itemArray = @[(NSDictionary*)items];
213
- }
214
- if (itemArray) {
215
- keys.reserve(itemArray.count);
216
- for (NSDictionary* item in itemArray) {
217
- NSString* account = item[(__bridge id)kSecAttrAccount];
218
- if (account) {
219
- keys.push_back(std::string([account UTF8String]));
220
- }
221
- }
222
- }
223
- }
224
- return keys;
193
+ static NSString* nsStringFromStdString(const std::string& value) {
194
+ return [NSString stringWithUTF8String:value.c_str()];
225
195
  }
226
196
 
227
- void IOSStorageAdapterCpp::setSecure(const std::string& key, const std::string& value) {
228
- NSString* nsKey = [NSString stringWithUTF8String:key.c_str()];
229
- NSData* data = [[NSString stringWithUTF8String:value.c_str()] dataUsingEncoding:NSUTF8StringEncoding];
230
- std::string groupStr;
231
- int accessControlLevel;
232
- {
233
- std::lock_guard<std::mutex> lock(accessGroupMutex_);
234
- groupStr = keychainAccessGroup_;
235
- accessControlLevel = accessControlLevel_;
236
- }
237
- NSString* group = groupStr.empty() ? nil : [NSString stringWithUTF8String:groupStr.c_str()];
238
- NSMutableDictionary* query = baseKeychainQuery(nsKey, kKeychainService, group);
197
+ static NSData* nsDataFromStdString(const std::string& value) {
198
+ return [nsStringFromStdString(value) dataUsingEncoding:NSUTF8StringEncoding];
199
+ }
239
200
 
201
+ static void setSecureValue(
202
+ NSString* nsKey,
203
+ NSData* data,
204
+ NSString* group,
205
+ int accessControlLevel
206
+ ) {
207
+ NSMutableDictionary* query = baseKeychainQuery(nsKey, kKeychainService, group);
240
208
  NSDictionary* updateAttributes = @{
241
209
  (__bridge id)kSecValueData: data
242
210
  };
243
211
 
244
212
  OSStatus status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)updateAttributes);
245
-
246
213
  if (status == errSecSuccess) {
247
- markSecureKeySet(key);
248
214
  return;
249
215
  }
250
216
 
@@ -252,20 +218,19 @@ void IOSStorageAdapterCpp::setSecure(const std::string& key, const std::string&
252
218
  query[(__bridge id)kSecValueData] = data;
253
219
  query[(__bridge id)kSecAttrAccessible] = (__bridge id)accessControlAttr(accessControlLevel);
254
220
  const OSStatus addStatus = SecItemAdd((__bridge CFDictionaryRef)query, NULL);
255
- if (addStatus != errSecSuccess) {
256
- if (addStatus == errSecInteractionNotAllowed) {
257
- throw taggedStorageError(
258
- "keychain_locked",
259
- "NitroStorage: Keychain is locked (errSecInteractionNotAllowed). "
260
- "The item is not accessible until the device is unlocked."
261
- );
262
- }
263
- throw std::runtime_error(
264
- "NitroStorage: Secure set failed with status " + std::to_string(addStatus)
221
+ if (addStatus == errSecSuccess) {
222
+ return;
223
+ }
224
+ if (addStatus == errSecInteractionNotAllowed) {
225
+ throw taggedStorageError(
226
+ "keychain_locked",
227
+ "NitroStorage: Keychain is locked (errSecInteractionNotAllowed). "
228
+ "The item is not accessible until the device is unlocked."
265
229
  );
266
230
  }
267
- markSecureKeySet(key);
268
- return;
231
+ throw std::runtime_error(
232
+ "NitroStorage: Secure set failed with status " + std::to_string(addStatus)
233
+ );
269
234
  }
270
235
 
271
236
  if (status == errSecInteractionNotAllowed) {
@@ -280,14 +245,7 @@ void IOSStorageAdapterCpp::setSecure(const std::string& key, const std::string&
280
245
  );
281
246
  }
282
247
 
283
- std::optional<std::string> IOSStorageAdapterCpp::getSecure(const std::string& key) {
284
- NSString* nsKey = [NSString stringWithUTF8String:key.c_str()];
285
- std::string groupStr;
286
- {
287
- std::lock_guard<std::mutex> lock(accessGroupMutex_);
288
- groupStr = keychainAccessGroup_;
289
- }
290
- NSString* group = groupStr.empty() ? nil : [NSString stringWithUTF8String:groupStr.c_str()];
248
+ static std::optional<std::string> getSecureValue(NSString* nsKey, NSString* group) {
291
249
  NSMutableDictionary* query = baseKeychainQuery(nsKey, kKeychainService, group);
292
250
  query[(__bridge id)kSecReturnData] = @YES;
293
251
  query[(__bridge id)kSecMatchLimit] = (__bridge id)kSecMatchLimitOne;
@@ -310,15 +268,7 @@ std::optional<std::string> IOSStorageAdapterCpp::getSecure(const std::string& ke
310
268
  return std::nullopt;
311
269
  }
312
270
 
313
- void IOSStorageAdapterCpp::deleteSecure(const std::string& key) {
314
- NSString* nsKey = [NSString stringWithUTF8String:key.c_str()];
315
- std::string groupStr;
316
- {
317
- std::lock_guard<std::mutex> lock(accessGroupMutex_);
318
- groupStr = keychainAccessGroup_;
319
- }
320
- NSString* group = groupStr.empty() ? nil : [NSString stringWithUTF8String:groupStr.c_str()];
321
-
271
+ static void deleteSecureValue(NSString* nsKey, NSString* group) {
322
272
  NSMutableDictionary* secureQuery = baseKeychainQuery(nsKey, kKeychainService, group);
323
273
  OSStatus secureStatus = SecItemDelete((__bridge CFDictionaryRef)secureQuery);
324
274
  if (secureStatus == errSecInteractionNotAllowed) {
@@ -338,9 +288,77 @@ void IOSStorageAdapterCpp::deleteSecure(const std::string& key) {
338
288
  "The item is not accessible until the device is unlocked."
339
289
  );
340
290
  }
291
+ }
292
+
293
+ static std::vector<std::string> keychainAccountsForService(NSString* service, NSString* accessGroup) {
294
+ NSMutableDictionary* query = allAccountsQuery(service, accessGroup);
295
+ disableKeychainInteraction(query);
296
+ CFTypeRef result = NULL;
297
+ std::vector<std::string> keys;
298
+ OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
299
+ if (status == errSecInteractionNotAllowed) {
300
+ throw taggedStorageError(
301
+ "keychain_locked",
302
+ "NitroStorage: Keychain is locked (errSecInteractionNotAllowed). "
303
+ "The item is not accessible until the device is unlocked."
304
+ );
305
+ }
306
+ if (status == errSecSuccess && result) {
307
+ id items = (__bridge_transfer id)result;
308
+ NSArray* itemArray = nil;
309
+ if ([items isKindOfClass:[NSArray class]]) {
310
+ itemArray = (NSArray*)items;
311
+ } else if ([items isKindOfClass:[NSDictionary class]]) {
312
+ itemArray = @[(NSDictionary*)items];
313
+ }
314
+ if (itemArray) {
315
+ keys.reserve(itemArray.count);
316
+ for (NSDictionary* item in itemArray) {
317
+ NSString* account = item[(__bridge id)kSecAttrAccount];
318
+ if (account) {
319
+ keys.push_back(std::string([account UTF8String]));
320
+ }
321
+ }
322
+ }
323
+ }
324
+ return keys;
325
+ }
326
+
327
+ void IOSStorageAdapterCpp::setSecure(const std::string& key, const std::string& value) {
328
+ NSString* nsKey = nsStringFromStdString(key);
329
+ NSData* data = nsDataFromStdString(value);
330
+ std::string groupStr;
331
+ int accessControlLevel;
332
+ {
333
+ std::lock_guard<std::mutex> lock(accessGroupMutex_);
334
+ groupStr = keychainAccessGroup_;
335
+ accessControlLevel = accessControlLevel_;
336
+ }
337
+ NSString* group = groupStr.empty() ? nil : [NSString stringWithUTF8String:groupStr.c_str()];
338
+ setSecureValue(nsKey, data, group, accessControlLevel);
339
+ markSecureKeySet(key);
340
+ }
341
+
342
+ std::optional<std::string> IOSStorageAdapterCpp::getSecure(const std::string& key) {
343
+ NSString* nsKey = nsStringFromStdString(key);
344
+ std::string groupStr;
345
+ {
346
+ std::lock_guard<std::mutex> lock(accessGroupMutex_);
347
+ groupStr = keychainAccessGroup_;
348
+ }
349
+ NSString* group = groupStr.empty() ? nil : [NSString stringWithUTF8String:groupStr.c_str()];
350
+ return getSecureValue(nsKey, group);
351
+ }
341
352
 
342
- // errSecItemNotFound means the item was already gone — that's fine (idempotent).
343
- // Only update the cache if the delete actually ran (success or item-not-found).
353
+ void IOSStorageAdapterCpp::deleteSecure(const std::string& key) {
354
+ NSString* nsKey = nsStringFromStdString(key);
355
+ std::string groupStr;
356
+ {
357
+ std::lock_guard<std::mutex> lock(accessGroupMutex_);
358
+ groupStr = keychainAccessGroup_;
359
+ }
360
+ NSString* group = groupStr.empty() ? nil : [NSString stringWithUTF8String:groupStr.c_str()];
361
+ deleteSecureValue(nsKey, group);
344
362
  markSecureKeyRemoved(key);
345
363
  markBiometricKeyRemoved(key);
346
364
  }
@@ -400,25 +418,53 @@ void IOSStorageAdapterCpp::setSecureBatch(
400
418
  const std::vector<std::string>& keys,
401
419
  const std::vector<std::string>& values
402
420
  ) {
421
+ std::string groupStr;
422
+ int accessControlLevel;
423
+ {
424
+ std::lock_guard<std::mutex> lock(accessGroupMutex_);
425
+ groupStr = keychainAccessGroup_;
426
+ accessControlLevel = accessControlLevel_;
427
+ }
428
+ NSString* group = groupStr.empty() ? nil : [NSString stringWithUTF8String:groupStr.c_str()];
403
429
  for (size_t i = 0; i < keys.size() && i < values.size(); ++i) {
404
- setSecure(keys[i], values[i]);
430
+ setSecureValue(
431
+ nsStringFromStdString(keys[i]),
432
+ nsDataFromStdString(values[i]),
433
+ group,
434
+ accessControlLevel
435
+ );
436
+ markSecureKeySet(keys[i]);
405
437
  }
406
438
  }
407
439
 
408
440
  std::vector<std::optional<std::string>> IOSStorageAdapterCpp::getSecureBatch(
409
441
  const std::vector<std::string>& keys
410
442
  ) {
443
+ std::string groupStr;
444
+ {
445
+ std::lock_guard<std::mutex> lock(accessGroupMutex_);
446
+ groupStr = keychainAccessGroup_;
447
+ }
448
+ NSString* group = groupStr.empty() ? nil : [NSString stringWithUTF8String:groupStr.c_str()];
411
449
  std::vector<std::optional<std::string>> results;
412
450
  results.reserve(keys.size());
413
451
  for (const auto& key : keys) {
414
- results.push_back(getSecure(key));
452
+ results.push_back(getSecureValue(nsStringFromStdString(key), group));
415
453
  }
416
454
  return results;
417
455
  }
418
456
 
419
457
  void IOSStorageAdapterCpp::deleteSecureBatch(const std::vector<std::string>& keys) {
458
+ std::string groupStr;
459
+ {
460
+ std::lock_guard<std::mutex> lock(accessGroupMutex_);
461
+ groupStr = keychainAccessGroup_;
462
+ }
463
+ NSString* group = groupStr.empty() ? nil : [NSString stringWithUTF8String:groupStr.c_str()];
420
464
  for (const auto& key : keys) {
421
- deleteSecure(key);
465
+ deleteSecureValue(nsStringFromStdString(key), group);
466
+ markSecureKeyRemoved(key);
467
+ markBiometricKeyRemoved(key);
422
468
  }
423
469
  }
424
470