| // |
| // SSKeychain.m |
| // SSToolkit |
| // |
| // Created by Sam Soffes on 5/19/10. |
| // Copyright (c) 2009-2011 Sam Soffes. All rights reserved. |
| // |
| |
| #import "SSKeychain.h" |
| |
| NSString *const kSSKeychainErrorDomain = @"com.samsoffes.sskeychain"; |
| |
| NSString *const kSSKeychainAccountKey = @"acct"; |
| NSString *const kSSKeychainCreatedAtKey = @"cdat"; |
| NSString *const kSSKeychainClassKey = @"labl"; |
| NSString *const kSSKeychainDescriptionKey = @"desc"; |
| NSString *const kSSKeychainLabelKey = @"labl"; |
| NSString *const kSSKeychainLastModifiedKey = @"mdat"; |
| NSString *const kSSKeychainWhereKey = @"svce"; |
| |
| #if __IPHONE_4_0 && TARGET_OS_IPHONE |
| CFTypeRef SSKeychainAccessibilityType = NULL; |
| #endif |
| |
| @interface SSKeychain () |
| + (NSMutableDictionary *)_queryForService:(NSString *)service account:(NSString *)account; |
| @end |
| |
| @implementation SSKeychain |
| |
| #pragma mark - Getting Accounts |
| |
| + (NSArray *)allAccounts { |
| return [self accountsForService:nil error:nil]; |
| } |
| |
| |
| + (NSArray *)allAccounts:(NSError **)error { |
| return [self accountsForService:nil error:error]; |
| } |
| |
| |
| + (NSArray *)accountsForService:(NSString *)service { |
| return [self accountsForService:service error:nil]; |
| } |
| |
| |
| + (NSArray *)accountsForService:(NSString *)service error:(NSError **)error { |
| OSStatus status = SSKeychainErrorBadArguments; |
| NSMutableDictionary *query = [self _queryForService:service account:nil]; |
| #if __has_feature(objc_arc) |
| [query setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kSecReturnAttributes]; |
| [query setObject:(__bridge id)kSecMatchLimitAll forKey:(__bridge id)kSecMatchLimit]; |
| #else |
| [query setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnAttributes]; |
| [query setObject:(id)kSecMatchLimitAll forKey:(id)kSecMatchLimit]; |
| #endif |
| |
| CFTypeRef result = NULL; |
| #if __has_feature(objc_arc) |
| status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result); |
| #else |
| status = SecItemCopyMatching((CFDictionaryRef)query, &result); |
| #endif |
| if (status != noErr && error != NULL) { |
| *error = [NSError errorWithDomain:kSSKeychainErrorDomain code:status userInfo:nil]; |
| return nil; |
| } |
| |
| #if __has_feature(objc_arc) |
| return (__bridge_transfer NSArray *)result; |
| #else |
| return [(NSArray *)result autorelease]; |
| #endif |
| } |
| |
| |
| #pragma mark - Getting Passwords |
| |
| + (NSString *)passwordForService:(NSString *)service account:(NSString *)account { |
| return [self passwordForService:service account:account error:nil]; |
| } |
| |
| |
| + (NSString *)passwordForService:(NSString *)service account:(NSString *)account error:(NSError **)error { |
| NSData *data = [self passwordDataForService:service account:account error:error]; |
| if (data.length > 0) { |
| NSString *string = [[NSString alloc] initWithData:(NSData *)data encoding:NSUTF8StringEncoding]; |
| #if !__has_feature(objc_arc) |
| [string autorelease]; |
| #endif |
| return string; |
| } |
| |
| return nil; |
| } |
| |
| |
| + (NSData *)passwordDataForService:(NSString *)service account:(NSString *)account { |
| return [self passwordDataForService:service account:account error:nil]; |
| } |
| |
| |
| + (NSData *)passwordDataForService:(NSString *)service account:(NSString *)account error:(NSError **)error { |
| OSStatus status = SSKeychainErrorBadArguments; |
| if (!service || !account) { |
| if (error) { |
| *error = [NSError errorWithDomain:kSSKeychainErrorDomain code:status userInfo:nil]; |
| } |
| return nil; |
| } |
| |
| CFTypeRef result = NULL; |
| NSMutableDictionary *query = [self _queryForService:service account:account]; |
| #if __has_feature(objc_arc) |
| [query setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData]; |
| [query setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit]; |
| status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result); |
| #else |
| [query setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData]; |
| [query setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit]; |
| status = SecItemCopyMatching((CFDictionaryRef)query, &result); |
| #endif |
| |
| if (status != noErr && error != NULL) { |
| *error = [NSError errorWithDomain:kSSKeychainErrorDomain code:status userInfo:nil]; |
| return nil; |
| } |
| |
| #if __has_feature(objc_arc) |
| return (__bridge_transfer NSData *)result; |
| #else |
| return [(NSData *)result autorelease]; |
| #endif |
| } |
| |
| |
| #pragma mark - Deleting Passwords |
| |
| + (BOOL)deletePasswordForService:(NSString *)service account:(NSString *)account { |
| return [self deletePasswordForService:service account:account error:nil]; |
| } |
| |
| |
| + (BOOL)deletePasswordForService:(NSString *)service account:(NSString *)account error:(NSError **)error { |
| OSStatus status = SSKeychainErrorBadArguments; |
| if (service && account) { |
| NSMutableDictionary *query = [self _queryForService:service account:account]; |
| #if __has_feature(objc_arc) |
| status = SecItemDelete((__bridge CFDictionaryRef)query); |
| #else |
| status = SecItemDelete((CFDictionaryRef)query); |
| #endif |
| } |
| if (status != noErr && error != NULL) { |
| *error = [NSError errorWithDomain:kSSKeychainErrorDomain code:status userInfo:nil]; |
| } |
| return (status == noErr); |
| |
| } |
| |
| |
| #pragma mark - Setting Passwords |
| |
| + (BOOL)setPassword:(NSString *)password forService:(NSString *)service account:(NSString *)account { |
| return [self setPassword:password forService:service account:account error:nil]; |
| } |
| |
| |
| + (BOOL)setPassword:(NSString *)password forService:(NSString *)service account:(NSString *)account error:(NSError **)error { |
| NSData *data = [password dataUsingEncoding:NSUTF8StringEncoding]; |
| return [self setPasswordData:data forService:service account:account error:error]; |
| } |
| |
| |
| + (BOOL)setPasswordData:(NSData *)password forService:(NSString *)service account:(NSString *)account { |
| return [self setPasswordData:password forService:service account:account error:nil]; |
| } |
| |
| |
| + (BOOL)setPasswordData:(NSData *)password forService:(NSString *)service account:(NSString *)account error:(NSError **)error { |
| OSStatus status = SSKeychainErrorBadArguments; |
| if (password && service && account) { |
| [self deletePasswordForService:service account:account]; |
| NSMutableDictionary *query = [self _queryForService:service account:account]; |
| #if __has_feature(objc_arc) |
| [query setObject:password forKey:(__bridge id)kSecValueData]; |
| #else |
| [query setObject:password forKey:(id)kSecValueData]; |
| #endif |
| |
| #if __IPHONE_4_0 && TARGET_OS_IPHONE |
| if (SSKeychainAccessibilityType) { |
| #if __has_feature(objc_arc) |
| [query setObject:(id)[self accessibilityType] forKey:(__bridge id)kSecAttrAccessible]; |
| #else |
| [query setObject:(id)[self accessibilityType] forKey:(id)kSecAttrAccessible]; |
| #endif |
| } |
| #endif |
| |
| #if __has_feature(objc_arc) |
| status = SecItemAdd((__bridge CFDictionaryRef)query, NULL); |
| #else |
| status = SecItemAdd((CFDictionaryRef)query, NULL); |
| #endif |
| } |
| if (status != noErr && error != NULL) { |
| *error = [NSError errorWithDomain:kSSKeychainErrorDomain code:status userInfo:nil]; |
| } |
| return (status == noErr); |
| } |
| |
| |
| #pragma mark - Configuration |
| |
| #if __IPHONE_4_0 && TARGET_OS_IPHONE |
| + (CFTypeRef)accessibilityType { |
| return SSKeychainAccessibilityType; |
| } |
| |
| |
| + (void)setAccessibilityType:(CFTypeRef)accessibilityType { |
| CFRetain(accessibilityType); |
| if (SSKeychainAccessibilityType) { |
| CFRelease(SSKeychainAccessibilityType); |
| } |
| SSKeychainAccessibilityType = accessibilityType; |
| } |
| #endif |
| |
| |
| #pragma mark - Private |
| |
| + (NSMutableDictionary *)_queryForService:(NSString *)service account:(NSString *)account { |
| NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:3]; |
| #if __has_feature(objc_arc) |
| [dictionary setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass]; |
| #else |
| [dictionary setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass]; |
| #endif |
| |
| if (service) { |
| #if __has_feature(objc_arc) |
| [dictionary setObject:service forKey:(__bridge id)kSecAttrService]; |
| #else |
| [dictionary setObject:service forKey:(id)kSecAttrService]; |
| #endif |
| } |
| |
| if (account) { |
| #if __has_feature(objc_arc) |
| [dictionary setObject:account forKey:(__bridge id)kSecAttrAccount]; |
| #else |
| [dictionary setObject:account forKey:(id)kSecAttrAccount]; |
| #endif |
| } |
| |
| return dictionary; |
| } |
| |
| @end |