Hello,
I use the keychain to store a sensitive data.
Since iOS 9.2 beta 3, I cannot retrieve a sensitive data created from a previous version of iOS (e.g. iOS 9.1).
I have an error errSecItemNotFound when using SecItemCopyMatching.
No problem with iOS 9.1 (nor iOS 9.2 beta 2 nor iOS 7.x/8.x/9.0).
Very strange: my source code creates a new sensitive data if it doesn't exist, so with iOS 9.2 beta 3 I have a new sensitive data, but if I switch back to iOS 9.1, I retrieve the old sensitive data, and so on when going back to iOS 9.2 beta 3...
As I use the exact same query, it seems the keychain is duplicated...
Here is my code to add a sensitive data:
NSMutableDictionary *symmetricKeyAttr = [NSMutableDictionary dictionary];
[symmetricKeyAttr setObject:(__bridge id)kSecAttrAccessibleWhenUnlockedThisDeviceOnly forKey:(__bridge id)kSecAttrAccessible];
[symmetricKeyAttr setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
[symmetricKeyAttr setObject:[NSNumber numberWithUnsignedInt:CSSM_ALGID_AES] forKey:(__bridge id)kSecAttrKeyType];
[symmetricKeyAttr setObject:[NSNumber numberWithUnsignedInt:(unsigned int)(kChosenCipherKeySize << 3)] forKey:(__bridge id)kSecAttrKeySizeInBits];
[symmetricKeyAttr setObject:[NSNumber numberWithUnsignedInt:(unsigned int)(kChosenCipherKeySize << 3)]
forKey:(__bridge id)kSecAttrEffectiveKeySize];
[symmetricKeyAttr setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecAttrCanEncrypt];
[symmetricKeyAttr setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecAttrCanDecrypt];
[symmetricKeyAttr setObject:(id)kCFBooleanFalse forKey:(__bridge id)kSecAttrCanDerive];
[symmetricKeyAttr setObject:(id)kCFBooleanFalse forKey:(__bridge id)kSecAttrCanSign];
[symmetricKeyAttr setObject:(id)kCFBooleanFalse forKey:(__bridge id)kSecAttrCanVerify];
[symmetricKeyAttr setObject:(id)kCFBooleanFalse forKey:(__bridge id)kSecAttrCanWrap];
[symmetricKeyAttr setObject:(id)kCFBooleanFalse forKey:(__bridge id)kSecAttrCanUnwrap];
[symmetricKeyAttr setObject:accessGroup forKey:(__bridge id)kSecAttrAccessGroup];
[symmetricKeyAttr setObject:applicationTag forKey:(__bridge id)kSecAttrApplicationTag];
[symmetricKeyAttr setObject:sensitiveData forKey:(__bridge id)kSecValueData];
OSStatus sanityCheck = SecItemAdd((__bridge CFDictionaryRef) symmetricKeyAttr, NULL);
Here is my code to get a sensitive data:
NSMutableDictionary * querySymmetricKey = [NSMutableDictionary dictionary];
[querySymmetricKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
[querySymmetricKey setObject:[NSNumber numberWithUnsignedInt:CSSM_ALGID_AES] forKey:(__bridge id)kSecAttrKeyType];
[querySymmetricKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnData];
[querySymmetricKey setObject:applicationTag forKey:(__bridge id)kSecAttrApplicationTag];
[querySymmetricKey setObject:accessGroup forKey:(__bridge id)kSecAttrAccessGroup];
CFDataRef symmetricKeyDataRef = NULL;
OSStatus sanityCheck = SecItemCopyMatching((__bridge CFDictionaryRef)querySymmetricKey, (CFTypeRef *)&symmetricKeyDataRef);
Where:
- sensitiveData is the sensitive data to store (e.g. <ac746cc2 80f72948 59d0d8b7 a5de4bad 5d9e9eb1 a400fba3 c85f3f2e 675d58bf>)
- accessGroup is the concatenation of team identifier and application identifier (e.g. XXXXXXXXXX.com.toto.tata)
- applicationTag is the tag related to the sensible data (e.g. <746F746F)
Note: I switch between iOS 9.1 and 9.2 beta 3 by using IPSW files without doing a backup restore, but I have the same problem by doing backup restore.
Any idea?
EDIT 1: The issue occurs with 64-bit devices only, no problem with 32-bit devices.
EDIT 2:
- Replacing CSSM_ALGID_AES by CSSM_ALGID_NONE solves the issue (i.e. data created with iOS 9.1 can be properly retrieved with iOS 9.2 beta 3), but it's not acceptable because I must be able to read data created on iOS 9.1 with CSSM_ALGID_AES.
- The issue is not related to kSecAttrAccessGroup: I still have the problem when I remove this property.
EDIT 3:
- I've "reproduced" the issue with sample from Apple (https://developer.apple.com/library/ios/samplecode/CryptoExercise).
- This sample uses CSSM_ALGID_AES too and not kSecAttrAccessGroup.
- Using 64-bit device: a key created with iOS 9.1 (<bdd17fe1 f515e2b1 14de7c43 c4cb6a70>) is found with iOS 9.2 beta 3 but it has a different value (<73b205e2 46230f69 fa0f347c 2958e6b1>) !!
- Using 32-bit device: key is the same between iOS 9.1 and iOS 9.2 beta 3.
The issue is that the value CSSM_ALGID_AES is valid only on OS X. It has never been valid on iOS if you check the headers closely.
You should change your code to store your custom data using only those SecItem attributes that are defined for iOS.
Note that those CSSM_ALGID_* constants are defined in the app; they don't exist in the iOS SDK. They do exist in the OS X SDK.
The definitive list is in the header file Security/SecItem.h. Search for "kSecAttrKeyType Value Constants". As of iOS 9.2.1 the only legal values are kSecAttrKeyTypeRSA and kSecAttrKeyTypeEC.
Best regards,
--gc