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.