Search code examples
iosobjective-ckeychain

SecItemCopyMatching keeps returning errSecItemNotFound


I'm trying to use Keychain Services to save a value, which would persist even if a user reinstalls the app. So I check if an item exists using SecItemCopyMatching, which returns errSecItemNotFound the first time and add a new item using SecItemAdd, which returns errSecSuccess, but the value of _attrs is nil. Also, when the code is called second time, the SecItemCopyMatching still returns errSecItemNotFound, as if SecItemAdd wasn't called. So what that could be related to?

    CFMutableDictionaryRef _attrs = nil;
    NSString* key = @"<unique key>";
    NSMutableDictionary* query = [NSMutableDictionary dictionary];
    query[(__bridge id)kSecClass] = (__bridge id)kSecClassGenericPassword;
    query[(__bridge id)kSecAttrLabel] = key;
    query[(__bridge id)kSecMatchLimit] = (__bridge id)kSecMatchLimitOne;
    query[(__bridge id)kSecReturnAttributes] = (__bridge id)kCFBooleanTrue;

    OSStatus err = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef*)&_attrs);

    if (err == errSecSuccess) {
        return YES;
    }
    NSString* str = @"<some data>";
    if (err == errSecItemNotFound) {
        query[(__bridge id)kSecValueData] = NSData_from_string(string_from_NSString(str));
        query[(__bridge id)kSecAttrAccessible] = (__bridge id)kSecAttrAccessibleAlways;
        err = SecItemAdd((__bridge CFDictionaryRef)query, (CFTypeRef*)&_attrs);
        assert(err == errSecSuccess);
    }

Solution

  • You are re-using query for your call to SecItemAdd, and the kSecMatchLimit value being present in the dictionary for this function is breaking it. You should remove this key before calling SecItemAdd.

    It's also worth noting that [str dataUsingEncoding:NSUTF8StringEncoding] might be a better choice than whatever NSData_from_string(string_from_NSString(str)) is, depending on what you're doing.