Dearest Stack Overflow Helpful Beings Of Light,
When using the kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly attribute in a keychain entry for SecItemAdd(), what other attributes need to be included? My understanding is that kSecAttrAccessControl and kSecUseAuthenticationUI are appropriate, using a SecAccessControlRef instance and @(BOOL), accordingly. This, however, is not working for me.
I am attempting to save two dictionaries in two keychain entries.
One is succeeding and the other is failing, The one that is succeeding uses kSecAttrAccessibleAfterFirstUnlock for accessibility. The one that is failing uses kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, and fails with a status of errSecParam (-50) - suggesting I’m using an incorrect parameter in what I pass to SecItemAdd().
In the successful keychain entry there are 5 key/value pairs in the dictionary passed to SecItemAdd().
keychainQuery __NSDictionaryM * 5 key/value pairs
[0] @"svce" : @"session"
[1] @"acct" : @"com.xxx.yyy”
[2] @"v_Data" : 490 bytes
[3] @"class" : @"genp"
[4] @"pdmn" : @"ck"
The @“ck” value for @“pdmn” corresponds to the kSecAttrAccessibleAfterFirstUnlock accessibility attribute. The keychain item is added, and able to be retrieved and the data successfully read from it.
In the unsuccessful keychain entry there are 7 key/value pairs in the dictionary passed to SecItemAdd().
keychainQuery __NSDictionaryM * 7 key/value pairs
[0] @"svce" : @"credentials"
[1] @"acct" : @"com.xxx.yyy”
[2] @"v_Data" : 370 bytes
[3] @"class" : @"genp"
[4] @"pdmn" : @"akpu"
[5] @"accc" : (no summary)
[6] @"u_AuthUI" : @"1"
The @“akpu” value for @“pdmn” corresponds to the kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly accessibility attribute. For this dictionary kSecAttrAccessControl and kSecUseAuthenticationUI are also specified, using a SecAccessControlRef instance and @(YES) accordingly.
Here’s how the base query is being created:
+ (NSMutableDictionary *)getKeychainQuery
{
NSMutableDictionary *queryDict = [NSMutableDictionary dictionary];
queryDict[(__bridge id)kSecClass] = (__bridge id)kSecClassGenericPassword;
queryDict[(__bridge id)kSecAttrService] = [self service];
queryDict[(__bridge id)kSecAttrAccount] = [self account];
if( [self userPresence] )
{
CFErrorRef error = NULL;
SecAccessControlRef sacObject = SecAccessControlCreateWithFlags(kCFAllocatorDefault,
[self accessibility],
kSecAccessControlUserPresence, &error);
if (error == NULL || sacObject != NULL)
{
queryDict[(__bridge id)kSecAttrAccessControl] = (__bridge_transfer id)sacObject;
queryDict[(__bridge id)kSecUseAuthenticationUI] = @YES;
}
}
return queryDict;
}
Here’s the usage:
+ (void)saveToKeychain:(XGKeychainItem *)keychainItem
{
NSMutableDictionary *keychainQuery = [self getKeychainQuery];
keychainQuery[(__bridge id)kSecAttrAccessible] = (__bridge id)[self accessibility];
NSData *keychainData = [NSKeyedArchiver archivedDataWithRootObject:keychainItem];
keychainQuery[(__bridge id)kSecValueData] = keychainData;
OSStatus addStatus = SecItemAdd((__bridge CFDictionaryRef)keychainQuery, NULL);
NSLog(@"ADD STATUS: %d for service - %@", (int)addStatus, [self service]);
}
If, immediately before passing the problem dictionary to SecItemAdd(), I remove the two attributes that are not in the first dictionary, and set the accessibility to match the first, the keychain item is successfully added.
So, it seems that there’s something wrong with my usage of kSecUseAuthenticationUI, kSecAttrAccessControl, and/or with my SecAccessControlRef. All of these, however, seem to check out with the documentation and all of the example code I can find, as well as all of the Stack Overflow threads I’ve dug up.
What am I screwing up? Why is one of these parameters displeasing to iOS? If the suspect offending attributes are removed, I can decode the data without issue, so I'm confident my password data dictionary is not the problem. What's my damage?