At present, we have been receiving numerous reports from customers who integrate our SDK who have been encountering the failures (errSecItemNotFound) while trying to retrieve a key using SecItemCopyMatching. We are raising this query we are still in the midst of properly reproducing this issue though it has been reported to occur in several devices during the OS upgrade to the iOS 17 betas. This issue is still occurring in the latest iOS 17 beta 7. This issue was not present in previous iOS version. At present, we are of the conclusion that this issue is occurring randomly amongst devices that upgraded to the iOS 17 betas and it is not limited to older devices.
What we believe is occurring is that:
- A key is created and stored into Keychain using SecItemAdd.
- The same key is queried at a later timepoint but encounters the error errSecItemNotFound.
- Our SDK then attempts to regenerate a new key for the same label and attribute to store it using SecItemAdd, but the system then reports errSecDuplicateItem at the key already exists.
- The workaround here includes a manual deletion of the said key.
This issue seems to occur only during an OS upgrade to the iOS17 betas with the likelihood that the key was already present in Keychain prior to the upgrade. I share below the snippet relating to how this said key is generated, stored and retrieved. // Initial key is added
// A random data of 32 bytes length is generated
CFDataRef dataRef = <32bytes of data>;
*attr = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
BREAK_IF_NULL(*attr);
CFDictionarySetValue(*attr, kSecClass, kSecClassGenericPassword);
CFStringRef aKey = CFStringCreateWithCString(kCFAllocatorDefault, "KEY_ACCOUNT", kCFStringEncodingUTF8);
CFDictionaryAddValue(*attr, kSecAttrAccount, aKey);
CFDictionarySetValue(*attr, kSecReturnData, kCFBooleanFalse); CFDictionarySetValue(*attr, kSecAttrAccessible, kSecAttrAccessibleAfterFirstUnlock);
label = CFStringCreateWithFormat(NULL, NULL, CFSTR("A_LABEL"));
CFDictionarySetValue(attr, kSecAttrService, label);
CFDictionarySetValue(attr, kSecValueData, dataRef); SecItemAdd(attr);
// Query for retrieval of key
label = CFStringCreateWithFormat(NULL, NULL, CFSTR("A_LABEL")); CFDictionarySetValue(attributes, kSecAttrService, label);
CFDictionarySetValue(attributes, kSecReturnData, kCFBooleanTrue);
CFDictionarySetValue(attributes, kSecMatchLimit, limit);
CFDictionarySetValue(attributes, kSecReturnAttributes, returnAttributes);
osStatus = SecItemCopyMatching(attributes, result);
if (errSecItemNotFound == osStatus) { }
Please do let me know if more information could be useful. At present, we have ensured that the key generated are well-within the size limits and is stored simply as a kSecClassGenericPassword with limited access control to the key. Additionally, the query used was intended to be generalised to avoid encountering such occurrences.
The fix for the specific issue discussed upthread (FB13066335
) shipped in iOS 17.1.
However, that’s one specific fix. It’s perfectly feasible that there are other issues with similar symptoms. In my experience very few people are committed enough to investigate issues like this with the determination shown by the OP (thanks mervyn.ong!).
If you’re seeing a mysterious keychain problems, I recommend that you read through:
A significant majority of such problems are caused by folks using the SecItem API incorrectly [1].
If that doesn’t help, you’ll need to kick of your own investigation. I have a post, Investigating hard-to-reproduce keychain problems, that outlines my recommended approach for this.
If you manage to isolate this per the advice in that post, feel free to start a new thread with the details.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
[1] Well, at a meta level you could argue that they’re caused by the SecItem API being so hard to use correctly, and I’d probably agree with you |-: