|
16 | 16 | return defaults ?: [NSUserDefaults standardUserDefaults]; |
17 | 17 | } |
18 | 18 |
|
| 19 | +// Prevents the Keychain from showing auth UI. On iOS 14+ kSecUseAuthenticationUIFail is |
| 20 | +// deprecated; the correct replacement is an LAContext with interactionNotAllowed = YES. |
| 21 | +static void disableKeychainInteraction(NSMutableDictionary* query) { |
| 22 | + LAContext* ctx = [[LAContext alloc] init]; |
| 23 | + ctx.interactionNotAllowed = YES; |
| 24 | + query[(__bridge id)kSecUseAuthenticationContext] = ctx; |
| 25 | +} |
| 26 | + |
19 | 27 | static CFStringRef accessControlAttr(int level) { |
20 | 28 | switch (level) { |
21 | 29 | case 1: return kSecAttrAccessibleAfterFirstUnlock; |
@@ -178,7 +186,7 @@ static CFStringRef accessControlAttr(int level) { |
178 | 186 |
|
179 | 187 | static std::vector<std::string> keychainAccountsForService(NSString* service, NSString* accessGroup) { |
180 | 188 | NSMutableDictionary* query = allAccountsQuery(service, accessGroup); |
181 | | - query[(__bridge id)kSecUseAuthenticationUI] = (__bridge id)kSecUseAuthenticationUIFail; |
| 189 | + disableKeychainInteraction(query); |
182 | 190 | CFTypeRef result = NULL; |
183 | 191 | std::vector<std::string> keys; |
184 | 192 | OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result); |
@@ -274,7 +282,7 @@ static CFStringRef accessControlAttr(int level) { |
274 | 282 | NSMutableDictionary* query = baseKeychainQuery(nsKey, kKeychainService, group); |
275 | 283 | query[(__bridge id)kSecReturnData] = @YES; |
276 | 284 | query[(__bridge id)kSecMatchLimit] = (__bridge id)kSecMatchLimitOne; |
277 | | - query[(__bridge id)kSecUseAuthenticationUI] = (__bridge id)kSecUseAuthenticationUIFail; |
| 285 | + disableKeychainInteraction(query); |
278 | 286 |
|
279 | 287 | CFTypeRef result = NULL; |
280 | 288 | OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result); |
@@ -334,12 +342,12 @@ static CFStringRef accessControlAttr(int level) { |
334 | 342 | } |
335 | 343 | NSString* group = groupStr.empty() ? nil : [NSString stringWithUTF8String:groupStr.c_str()]; |
336 | 344 | NSMutableDictionary* secureQuery = baseKeychainQuery(nsKey, kKeychainService, group); |
337 | | - secureQuery[(__bridge id)kSecUseAuthenticationUI] = (__bridge id)kSecUseAuthenticationUIFail; |
| 345 | + disableKeychainInteraction(secureQuery); |
338 | 346 | if (SecItemCopyMatching((__bridge CFDictionaryRef)secureQuery, NULL) == errSecSuccess) { |
339 | 347 | return true; |
340 | 348 | } |
341 | 349 | NSMutableDictionary* biometricQuery = baseKeychainQuery(nsKey, kBiometricKeychainService, group); |
342 | | - biometricQuery[(__bridge id)kSecUseAuthenticationUI] = (__bridge id)kSecUseAuthenticationUIFail; |
| 350 | + disableKeychainInteraction(biometricQuery); |
343 | 351 | return SecItemCopyMatching((__bridge CFDictionaryRef)biometricQuery, NULL) == errSecSuccess; |
344 | 352 | } |
345 | 353 |
|
@@ -484,13 +492,13 @@ static CFStringRef accessControlAttr(int level) { |
484 | 492 | } |
485 | 493 | NSString* group = groupStr.empty() ? nil : [NSString stringWithUTF8String:groupStr.c_str()]; |
486 | 494 |
|
487 | | - // Capture backup before delete — use kSecUseAuthenticationUIFail to avoid prompting |
| 495 | + // Capture backup before delete — must not prompt for auth |
488 | 496 | std::optional<std::string> backup = std::nullopt; |
489 | 497 | { |
490 | 498 | NSMutableDictionary* backupQuery = baseKeychainQuery(nsKey, kBiometricKeychainService, group); |
491 | 499 | backupQuery[(__bridge id)kSecReturnData] = @YES; |
492 | 500 | backupQuery[(__bridge id)kSecMatchLimit] = (__bridge id)kSecMatchLimitOne; |
493 | | - backupQuery[(__bridge id)kSecUseAuthenticationUI] = (__bridge id)kSecUseAuthenticationUIFail; |
| 501 | + disableKeychainInteraction(backupQuery); |
494 | 502 | CFTypeRef backupResult = NULL; |
495 | 503 | if (SecItemCopyMatching((__bridge CFDictionaryRef)backupQuery, &backupResult) == errSecSuccess && backupResult) { |
496 | 504 | NSData* bData = (__bridge_transfer NSData*)backupResult; |
@@ -606,7 +614,7 @@ static CFStringRef accessControlAttr(int level) { |
606 | 614 | } |
607 | 615 | NSString* group = groupStr.empty() ? nil : [NSString stringWithUTF8String:groupStr.c_str()]; |
608 | 616 | NSMutableDictionary* query = baseKeychainQuery(nsKey, kBiometricKeychainService, group); |
609 | | - query[(__bridge id)kSecUseAuthenticationUI] = (__bridge id)kSecUseAuthenticationUIFail; |
| 617 | + disableKeychainInteraction(query); |
610 | 618 | return SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL) == errSecSuccess; |
611 | 619 | } |
612 | 620 |
|
|
0 commit comments