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.

Not sure if this helps, but we also use silent push notifications for CallKit functionality (with content-available). We see nearly this exact same issue - here's how ours typically happens, while it is very very rarely reproduced.


1. Device receives a silent push (incoming video call for us).

2. App checks keychain for a user session - sees nil and returns

3. Upon opening app the next time (possibly still active in the background), the value we're looking for in Keychain is nil - user appears to be logged out and is presented with a login screen


Note: In our case, after item #3, if we kill the app and reopen it, everything is back to normal (Keychain returns a value for our user session)


My guess is that somehow Keychain is locked for reads during that entire app session.


We're also looking for a workaround. We've thought about using UserDefaults as a backup solution. Does anyone have anything to add?

What are you setting the

kSecAttrAccessible
attribute to?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

We've experienced this issue with Keychain on multiple projects now, even with `
kSecAttrAccessibleAlways`, so we've been forced to use UserDefaults or basic file system storage.


The original bug I filed (24956853) in 2016 was marked as a dupe, with the main issue closed as well, but this is definitely not fixed. There is something bad happening with keychain when background modes and/or silent push notifications are used on iOS.


https://forums.developer.apple.com/message/325932?et=watches.email.thread#325932

kSecAttrAccessible is set to

kSecAttrAccessibleAlwaysThisDeviceOnly


I've also added an SO post here with much more information

The original bug I filed (24956853) in 2016 was marked as a dupe, with the main issue closed as well, but this is definitely not fixed.

Your bug (r. 24956853) was closed as a dup of the infamous -34018 bug (r. 18766047) which was actually fixed. It seems likely that this is a different issue. I recommend that you file a new bug about this.

The hard part of this is providing enough information in the bug to be actionable. One option is to add code to your app that posts a local notification when it notices the problem. The user can then trigger a sysdiagnose log, which should be included with the bug report.

This probably isn’t something you should inflict on normal users, but it might be worthwhile including in versions you ship to beta users.

If you do file a bug, please post your bug number, just for the record.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

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?

I'm having this problem in 2025!

Once every 20-30 runs, my app will fail to get an item from the keychain at launchtime or in the background. The error returned is -25300.

I am going to switch to use UserDefaults instead, but I was curious to see if this issue had been fixed, or if there is usage or workarounds.

I have raised a similar issue here https://discussions.apple.com/thread/256079126

I am also getting errSecItemNotFound in 20 or 30 runs of my app although the item is present in the keychain.

iOS Keychain randomly returning -25300
 
 
Q