Keychain error -25300 (errSecItemNotFound) when disabling and re-enabling Touch ID

霸气de小男生 提交于 2021-02-07 10:35:20

问题


I save the password to keychain with Touch ID flags:

+ (void)setPasscode:(NSString *)passcode
{
    CFErrorRef error = NULL;
    SecAccessControlRef sacObject;

    sacObject = SecAccessControlCreateWithFlags(kCFAllocatorDefault,
                                                    kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
                                                    kSecAccessControlUserPresence, &error);


    if(sacObject == NULL || error != NULL)
    {
        DLog(@"can't create sacObject: %@", error);
        return;
    }

    NSDictionary *attributes = @{
                                 (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
                                 (__bridge id)kSecAttrService: kKeychainServiceName,
                                 (__bridge id)kSecValueData: [passcode dataUsingEncoding:NSUTF8StringEncoding],
                                 (__bridge id)kSecUseNoAuthenticationUI: @YES,
                                 (__bridge id)kSecAttrAccessControl: (__bridge_transfer id)sacObject
                                 };

    dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        SecItemAdd((__bridge CFDictionaryRef)attributes, nil);
    });
}

And retrieving it:

+ (void)getCurrentPasscodeWithSuccess:(void (^)(NSString *))success failure:(void (^)(OSStatus))failure
{
    NSDictionary *query = @{
                            (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
                            (__bridge id)kSecAttrService: kKeychainServiceName,
                            (__bridge id)kSecReturnData: @YES,
                            (__bridge id)kSecUseOperationPrompt: kOperationPrompt
                            };

    dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        CFTypeRef dataTypeRef = NULL;

        OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)(query), &dataTypeRef);
        if (status == errSecSuccess)
        {
            if (success) {
                NSData *resultData = ( __bridge_transfer NSData *)dataTypeRef;
                NSString *result = [[NSString alloc] initWithData:resultData encoding:NSUTF8StringEncoding];
                success(result);
            }
        } else {
            if (failure) {
                failure(status);
            }
        }
    });
}

This works fine. But if you disable Touch ID, and re-enable it on device, SecItemCopyMatching returns OSStatus -25300 (errSecItemNotFound). Problem is that item still exists there (I think). Because when I try tio access it, Touch ID prompt comes up.

I tried to check if item exists with the following method:

+ (void)checkIfPasscodeExistsInKeychainWithCompletion:(void (^)(BOOL))completion
{
    NSDictionary *query = @{

                            (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
                            (__bridge id)kSecAttrService: kKeychainServiceName,
                            };

    dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        CFTypeRef dataTypeRef = NULL;

        OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)(query), &dataTypeRef);
        BOOL exists = status != errSecItemNotFound;

        if (completion) {
            completion(exists);
        }
    });
}

And this triggers the Touch ID prompt, and then returns error that it doesn't exist when touch is provided.

But if I remove (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword line, I get the status that it exists in keychain.


回答1:


You are using kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly as the Accessiblity constant for your item. This means that the item can only be added when there is a passcode set on the device, with the addition of the TouchID. If you remove your device passcode (or in your case TouchID) after your item is saved in the keychain, this item will no longer be available.

Even if you add the device passcode (or TouchID) back, this item is no longer available. You have to create a duplicate copy of the same item.

For more information on kSecAttrAccessible constant, refer the docs: Keychain Item Accessibility Constants.

For information on how the whole process works, refer Apple's security whitepaper




回答2:


It looks like an Apple bug, I've opened rdar://24237713 some time ago.

SecItemCopyMatching with queries containing match limit other than kSecMatchLimitAll will return incorrect results after device passcode was turned on & off. It seems like old items which are not accessible anymore aren't deleted from internal storage but only marked as unavailable, but they are still participating in creating result set.

Test project to demonstrate the issue: https://github.com/mndgs/TestKeychainBug



来源:https://stackoverflow.com/questions/32721826/keychain-error-25300-errsecitemnotfound-when-disabling-and-re-enabling-touch

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!