Calls to the the Keychain Services fail with errSecNotAvailable when executing a target using the command line tools and the iphone simulator is set to Hardware version 6.0 (10A403). If I change the simulator version to other previous version (4.3, 5.0, 5.1) and re-execute using the same command line script the calls succeed.
I'm running latest XCode 4.5 and the command line tools were downloaded from within XCode.
To reproduce this error just do the following:
- Setup an ios library project with a OCUnit target
- Set Base SDK to 6.0
- Set iOS Deployment Target to 4.3
- Copy and paste the code st the end of the post into the test project (it will only try to store a password and retrieve it)
- Add the Security.framework to the OCUnit target
Execute the OCUnit target in XCode and see the test pass with whatever Hardware Version is set in the iphone simulator (just change it between executions).
Execute the OCUnit target from the command line using:
xcodebuild -target TARGET_NAME_HERE -sdk iphonesimulator -configuration Release TEST_AFTER_BUILD=YES
with iphone simulator set to Hardware Version 6.0 and the test will fail. If you change the iphone simulator Hardware Version to 4.3, 5.0 or 5.1 and execute the command line script again the test will succeed.
Is this a command line tool problem? an iphone simulator problem? an OCUnit target running from the command line problem?
Who likes having unit tests that only pass when comets are aligned??
Any ideas?
Here is the code:
#define KEYCHAIN_ITEM_ATTRIBUTES (id)kSecClassGenericPassword, kSecClass, @"MyService", kSecAttrService, @"MyPassword", kSecAttrAccount
const NSString* MyPassword = @"blabla";
- (void)testExample
{
// remove previous keychain item
OSStatus status = SecItemDelete((CFTypeRef)[NSDictionary dictionaryWithObjectsAndKeys:KEYCHAIN_ITEM_ATTRIBUTES, nil]);
NSLog(@"SecItemDelete status:%ld",status);
NSParameterAssert(status == errSecSuccess || status == errSecItemNotFound);
// add keychain item with new value
NSData *data = [MyPassword dataUsingEncoding:NSUTF8StringEncoding];
status = SecItemAdd((CFTypeRef)[NSDictionary dictionaryWithObjectsAndKeys:KEYCHAIN_ITEM_ATTRIBUTES, data, kSecValueData, nil], NULL);
NSLog(@"SecItemAdd status:%ld",status);
NSParameterAssert(status == errSecSuccess);
// get password
status = SecItemCopyMatching((CFTypeRef)[NSDictionary dictionaryWithObjectsAndKeys:KEYCHAIN_ITEM_ATTRIBUTES,
kSecMatchLimitOne, kSecMatchLimit, kCFBooleanTrue, kSecReturnData, nil], (CFTypeRef *)&data);
NSLog(@"SecItemCopyMatching status:%ld",status);
NSParameterAssert(status == errSecSuccess);
if (status == errSecItemNotFound)
NSLog(@"SecItemCopyMatching status:%ld", status);
else
NSLog(@"SecItemCopyMatching result:%@",[[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]);
}
Regarding securityd daemon not being launched, I can check it IS being launched, using
launchctl list | grep securityd
after the simulator starts, and getting
- 0 com.apple.iPhoneSimulator:com.apple.securityd
I also tried stopping this securityd daemon and launch another manually... I looked at GTM's RunIPhoneUnitTest.sh script for a simple line I could use, but when I try this
launchctl submit -l ios6securityd -- /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator6.0.sdk/usr/libexec/securityd
gives me a -5 status code on that daemon.
I came across this because I was having trouble getting keychain access when trying to run my unit tests from the Xcode 4.5.1 UI. Luckily, the breakage is something I'm familiar with from CI builds with most previous Xcode versions as well, which is that the simulator's securityd isn't properly launched.
Try launching securityd first, and see if that helps:
That worked for me.