|  | // | 
|  | //  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 |