263 lines
7.9 KiB
Objective-C
263 lines
7.9 KiB
Objective-C
//
|
|
// 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
|