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.
I wanted to pass along the resolution here.
This problem is caused by a bug in iOS 17 (FB13066335, r. 114525162). We believe that the bug is fixed in the currently seeding iOS 17.1b3.
The bug caused keychain items protected by .applicationPassword
to become unreadable. However, it only affects items created by iOS 14 and earlier. That explains why only a small fraction of users were hitting it.
A wide variety of apps hit this problem because this technique was used by a popular third-party library.
With the fix in place, the keychain items will become readable again (assuming the app hasn’t done anything to ‘break’ them otherwise).
And, no, I can’t say when iOS 17.1 will be released (-: See tip 3 in Quinn’s Top Ten DevForums Tips.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"