iOS Keychain randomly returning -25300

I am facing a strange issue in my app. My code saves a string password in the iOS keychain to be accessed later on. It works just fine most of the times and I am able to fetch the password back after reinstallation or device restart or both.

Problem: Sometimes which is actually rare and hard to reproduce, it does NOT return the password and instead it returns null and error status:-25300(errSecItemNotFound). Another thing is that this problem got prominent after iOS 9 update. Happening on iOS 9.1 too.


Code for setting:


NSMutableDictionary *query = [self _queryForService:service account:account]; 
[query setObject:password forKey:(__bridge id)kSecValueData]; 
status = SecItemAdd((__bridge CFDictionaryRef)query, NULL);

if (status != errSecSuccess && error != NULL) { 
*error = [NSError errorWithDomain:kAppKeychainErrorDomain code:status userInfo:nil]; } 
return (status == noErr);


Code for fetching:


CFTypeRef result = NULL; 
NSMutableDictionary *query = [self _queryForService:service account:account]; 
[query setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData]; 
[query setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit]; 
status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);

if (status != errSecSuccess && error != NULL) { 
*error = [NSError errorWithDomain:kAppKeychainErrorDomain code:status userInfo:nil]; 
return nil; 
} 
return (__bridge_transfer NSData *)result;


Has anyone got any ideas why this is happening? Many thanks.

Post not yet marked as solved Up vote post of captalvins Down vote post of captalvins
25k views

Replies

Did you ever fail a new radar for this? If so, do you know what the status is? We may be running into this as well.

I'm not sure if what we're seeing is exactly the same, but we're getting it using


query[kSecAttrKeyClass as String] = kSecAttrKeyClassPublic

...

let status = SecItemCopyMatching(query as CFDictionary, &item)


It definitely happens after certain iOS upgrades (e.g. 10.0 to 12.3), and may also occasionally happen when the app is backgrounded/device locked (which is probably due to the flags we set when we created the key).


However we're hoping that setting kSecAttrKeyClass to kSecAttrKeyClassPrivate, then SecKeyCopyPublicKey to get the public key bytes will solve it - the problem is currently we can only test by finding old devices running v10 and upgrading them, and finding such devices is enormously difficult!

I am still facing similar issue in few devices. Currently facing in iPhone 11, iOS 13.3.

In most of the cases app working fine without any issue. But in very few cases keyChain is return `nil`. And when it happens, until I reinstall app, this is always happening. Restarting the app is not working.


Is there any possible solution for this issue?

I'm having the same issue. I have three apps with the same code base. Two of them were approved and the other one was rejected. For some reason when I try to get the info from the keychain using apple sign-in I get no results. Here is my code to load/save data.

Code Block class func save(key: String, data: Data) throws {
    let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
                                kSecValueData as String: data,
                                kSecAttrAccount as String: key]
    SecItemDelete(query as CFDictionary)
    let status = SecItemAdd(query as CFDictionary, nil)
    if status != errSecSuccess {
      throw error(from: status)
    }
  }
  class func load(key: String) throws -> Data? {
    let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
                                kSecMatchLimit as String: kSecMatchLimitOne,
                                kSecReturnData as String: true,
                                kSecAttrAccount as String: key]
    var queryResult: AnyObject?
    let status = withUnsafeMutablePointer(to: &queryResult) {
      SecItemCopyMatching(query as CFDictionary, $0)
    }
    switch status {
    case errSecSuccess:
      let dataType = type(of: queryResult)
      guard let data = queryResult as? Data else {
        throw KeyChainError.dataConversionError(actualType: "'\(dataType)'")
      }
      return data
    case errSecItemNotFound:
      return nil
    default:
      throw error(from: status)
    }
  }

Is there any possible solution for this issue?