First off, I have watched the WWDC 2013 session on protecting secrets with the keychain. I want to do a basic passcode store. Watched the whole video, but found what I needed in the first 10 minutes of the video. It seems straightforward, but I don't completely understand how the data encoding and retrieval works.
PROBLEM: after secItemCopyMatching, I check my NSData object to make sure it is not nil before converting it to a NSString. Problem is, it is always nil. Below is how I'm saving the keychain entry or update, followed by how I'm retrieving it. Any help and explanation would be very much appreciated.
UPDATE (EDITED): Fruity Geek, thanks for the response. I've updated my code below using __bridge. My problem now boils down to, am I storing and retrieving the password correctly? Have I got both wrong or just one or the other? My NSData instance is always nil. I am checking returns codes and my SecItemAdd and SecItemUpdate (when the keychaing entry exists) are working correctly. I can't seem to retrieve the string value of the data (passcode) stored to compare it with the passcode entered by the user. Appreciate the help guys and gals. Here is what I am doing now:
UPDATE #2: (Edited with Fruity Geek's answers and final working version. My edits only include changes to the code below.)
Set keychain entry:
NSData *secret = [_backupPassword dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *query = @{
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrService: twServiceName,
(__bridge id)kSecAttrAccount: twAccountName,
(__bridge id)kSecValueData: secret,
};
OSStatus status =
SecItemAdd((__bridge CFDictionaryRef)query, NULL);
if (status == errSecDuplicateItem) {
// this item exists in the keychain already, update it
query = @{
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrService: twServiceName,
(__bridge id)kSecAttrAccount: twAccountName,
};
NSDictionary *changes = @{
(__bridge id)kSecValueData: secret,
};
status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)changes);
}
Retrieve password from keychain:
NSDictionary *query = @{
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrService: twServiceName,
(__bridge id)kSecAttrAccount: twAccountName,
(__bridge id)kSecReturnData: @YES,
};
NSData *data = NULL;
CFTypeRef dataTypeRef = (__bridge CFTypeRef)data;
OSStatus status =
SecItemCopyMatching((__bridge CFDictionaryRef)query, &dataTypeRef);
NSData *data = (__bridge NSData *)dataTypeRef;
NSString *passcode = @"none";
if (status == errSecSuccess) {
// we found a keychain entry, set the passcode
if (data)
passcode = [NSString stringWithUTF8String:[data bytes]];
}
twServiceName and twAccountName are static NSStrings.
As I said, I don't quite what I am doing with __bridge or CFTypeRef. I looked through apples docs, numerous posts here and other sites, but keychain and these terms are brand new to me and I'm still trying to figure it out. Hoping someone here can point out my error and help me understand. Thanks in advance for the help.
iOS 7 / Xcode 5