123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293 |
- //
- // SSKeychainQuery.m
- // SSKeychain
- //
- // Created by Caleb Davenport on 3/19/13.
- // Copyright (c) 2013-2014 Sam Soffes. All rights reserved.
- //
- #import "SSKeychainQuery.h"
- #import "SSKeychain.h"
- @implementation SSKeychainQuery
- @synthesize account = _account;
- @synthesize service = _service;
- @synthesize label = _label;
- @synthesize passwordData = _passwordData;
- #if __IPHONE_3_0 && TARGET_OS_IPHONE
- @synthesize accessGroup = _accessGroup;
- #endif
- #ifdef SSKEYCHAIN_SYNCHRONIZATION_AVAILABLE
- @synthesize synchronizationMode = _synchronizationMode;
- #endif
- #pragma mark - Public
- - (BOOL)save:(NSError *__autoreleasing *)error {
- OSStatus status = SSKeychainErrorBadArguments;
- if (!self.service || !self.account || !self.passwordData) {
- if (error) {
- *error = [[self class] errorWithCode:status];
- }
- return NO;
- }
- NSMutableDictionary *query = nil;
- NSMutableDictionary * searchQuery = [self query];
- status = SecItemCopyMatching((__bridge CFDictionaryRef)searchQuery, nil);
- if (status == errSecSuccess) {//item already exists, update it!
- query = [[NSMutableDictionary alloc]init];
- [query setObject:self.passwordData forKey:(__bridge id)kSecValueData];
- status = SecItemUpdate((__bridge CFDictionaryRef)(searchQuery), (__bridge CFDictionaryRef)(query));
- }else if(status == errSecItemNotFound){//item not found, create it!
- query = [self query];
- if (self.label) {
- [query setObject:self.label forKey:(__bridge id)kSecAttrLabel];
- }
- [query setObject:self.passwordData forKey:(__bridge id)kSecValueData];
- #if __IPHONE_4_0 && TARGET_OS_IPHONE
- CFTypeRef accessibilityType = [SSKeychain accessibilityType];
- if (accessibilityType) {
- [query setObject:(__bridge id)accessibilityType forKey:(__bridge id)kSecAttrAccessible];
- }
- #endif
- status = SecItemAdd((__bridge CFDictionaryRef)query, NULL);
- }
- if (status != errSecSuccess && error != NULL) {
- *error = [[self class] errorWithCode:status];
- }
- return (status == errSecSuccess);}
- - (BOOL)deleteItem:(NSError *__autoreleasing *)error {
- OSStatus status = SSKeychainErrorBadArguments;
- if (!self.service || !self.account) {
- if (error) {
- *error = [[self class] errorWithCode:status];
- }
- return NO;
- }
- NSMutableDictionary *query = [self query];
- #if TARGET_OS_IPHONE
- status = SecItemDelete((__bridge CFDictionaryRef)query);
- #else
- CFTypeRef result = NULL;
- [query setObject:@YES forKey:(__bridge id)kSecReturnRef];
- status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
- if (status == errSecSuccess) {
- status = SecKeychainItemDelete((SecKeychainItemRef)result);
- CFRelease(result);
- }
- #endif
- if (status != errSecSuccess && error != NULL) {
- *error = [[self class] errorWithCode:status];
- }
- return (status == errSecSuccess);
- }
- - (NSArray *)fetchAll:(NSError *__autoreleasing *)error {
- OSStatus status = SSKeychainErrorBadArguments;
- NSMutableDictionary *query = [self query];
- [query setObject:@YES forKey:(__bridge id)kSecReturnAttributes];
- [query setObject:(__bridge id)kSecMatchLimitAll forKey:(__bridge id)kSecMatchLimit];
- #if __IPHONE_4_0 && TARGET_OS_IPHONE
- CFTypeRef accessibilityType = [SSKeychain accessibilityType];
- if (accessibilityType) {
- [query setObject:(__bridge id)accessibilityType forKey:(__bridge id)kSecAttrAccessible];
- }
- #endif
- CFTypeRef result = NULL;
- status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
- if (status != errSecSuccess && error != NULL) {
- *error = [[self class] errorWithCode:status];
- return nil;
- }
- return (__bridge_transfer NSArray *)result;
- }
- - (BOOL)fetch:(NSError *__autoreleasing *)error {
- OSStatus status = SSKeychainErrorBadArguments;
- if (!self.service || !self.account) {
- if (error) {
- *error = [[self class] errorWithCode:status];
- }
- return NO;
- }
- CFTypeRef result = NULL;
- NSMutableDictionary *query = [self query];
- [query setObject:@YES forKey:(__bridge id)kSecReturnData];
- [query setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit];
- status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
- if (status != errSecSuccess) {
- if (error) {
- *error = [[self class] errorWithCode:status];
- }
- return NO;
- }
- self.passwordData = (__bridge_transfer NSData *)result;
- return YES;
- }
- #pragma mark - Accessors
- - (void)setPasswordObject:(id<NSCoding>)object {
- self.passwordData = [NSKeyedArchiver archivedDataWithRootObject:object];
- }
- - (id<NSCoding>)passwordObject {
- if ([self.passwordData length]) {
- return [NSKeyedUnarchiver unarchiveObjectWithData:self.passwordData];
- }
- return nil;
- }
- - (void)setPassword:(NSString *)password {
- self.passwordData = [password dataUsingEncoding:NSUTF8StringEncoding];
- }
- - (NSString *)password {
- if ([self.passwordData length]) {
- return [[NSString alloc] initWithData:self.passwordData encoding:NSUTF8StringEncoding];
- }
- return nil;
- }
- #pragma mark - Synchronization Status
- #ifdef SSKEYCHAIN_SYNCHRONIZATION_AVAILABLE
- + (BOOL)isSynchronizationAvailable {
- #if TARGET_OS_IPHONE
- // Apple suggested way to check for 7.0 at runtime
- // https://developer.apple.com/library/ios/documentation/userexperience/conceptual/transitionguide/SupportingEarlieriOS.html
- return floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_6_1;
- #else
- return floor(NSFoundationVersionNumber) > NSFoundationVersionNumber10_8_4;
- #endif
- }
- #endif
- #pragma mark - Private
- - (NSMutableDictionary *)query {
- NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:3];
- [dictionary setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
- if (self.service) {
- [dictionary setObject:self.service forKey:(__bridge id)kSecAttrService];
- }
- if (self.account) {
- [dictionary setObject:self.account forKey:(__bridge id)kSecAttrAccount];
- }
- #if __IPHONE_3_0 && TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
- if (self.accessGroup) {
- [dictionary setObject:self.accessGroup forKey:(__bridge id)kSecAttrAccessGroup];
- }
- #endif
- #ifdef SSKEYCHAIN_SYNCHRONIZATION_AVAILABLE
- if ([[self class] isSynchronizationAvailable]) {
- id value;
- switch (self.synchronizationMode) {
- case SSKeychainQuerySynchronizationModeNo: {
- value = @NO;
- break;
- }
- case SSKeychainQuerySynchronizationModeYes: {
- value = @YES;
- break;
- }
- case SSKeychainQuerySynchronizationModeAny: {
- value = (__bridge id)(kSecAttrSynchronizableAny);
- break;
- }
- }
- [dictionary setObject:value forKey:(__bridge id)(kSecAttrSynchronizable)];
- }
- #endif
- return dictionary;
- }
- + (NSError *)errorWithCode:(OSStatus) code {
- NSString *message = nil;
- switch (code) {
- case errSecSuccess: return nil;
- case SSKeychainErrorBadArguments: message = NSLocalizedStringFromTable(@"SSKeychainErrorBadArguments", @"SSKeychain", nil); break;
- #if TARGET_OS_IPHONE
- case errSecUnimplemented: {
- message = NSLocalizedStringFromTable(@"errSecUnimplemented", @"SSKeychain", nil);
- break;
- }
- case errSecParam: {
- message = NSLocalizedStringFromTable(@"errSecParam", @"SSKeychain", nil);
- break;
- }
- case errSecAllocate: {
- message = NSLocalizedStringFromTable(@"errSecAllocate", @"SSKeychain", nil);
- break;
- }
- case errSecNotAvailable: {
- message = NSLocalizedStringFromTable(@"errSecNotAvailable", @"SSKeychain", nil);
- break;
- }
- case errSecDuplicateItem: {
- message = NSLocalizedStringFromTable(@"errSecDuplicateItem", @"SSKeychain", nil);
- break;
- }
- case errSecItemNotFound: {
- message = NSLocalizedStringFromTable(@"errSecItemNotFound", @"SSKeychain", nil);
- break;
- }
- case errSecInteractionNotAllowed: {
- message = NSLocalizedStringFromTable(@"errSecInteractionNotAllowed", @"SSKeychain", nil);
- break;
- }
- case errSecDecode: {
- message = NSLocalizedStringFromTable(@"errSecDecode", @"SSKeychain", nil);
- break;
- }
- case errSecAuthFailed: {
- message = NSLocalizedStringFromTable(@"errSecAuthFailed", @"SSKeychain", nil);
- break;
- }
- default: {
- message = NSLocalizedStringFromTable(@"errSecDefault", @"SSKeychain", nil);
- }
- #else
- default:
- message = (__bridge_transfer NSString *)SecCopyErrorMessageString(code, NULL);
- #endif
- }
- NSDictionary *userInfo = nil;
- if (message) {
- userInfo = @{ NSLocalizedDescriptionKey : message };
- }
- return [NSError errorWithDomain:kSSKeychainErrorDomain code:code userInfo:userInfo];
- }
- @end
|