From ad3e987db1f84ac06b56223bb8710b43be4caee0 Mon Sep 17 00:00:00 2001 From: liamnichols Date: Mon, 1 Sep 2014 19:18:28 +0100 Subject: [PATCH 1/8] implemented support for touch id --- SSKeychain.xcodeproj/project.pbxproj | 12 +++++++ SSKeychain/SSKeychainAccessControl.h | 51 ++++++++++++++++++++++++++++ SSKeychain/SSKeychainAccessControl.m | 50 +++++++++++++++++++++++++++ SSKeychain/SSKeychainQuery.h | 15 ++++++++ SSKeychain/SSKeychainQuery.m | 38 +++++++++++++++++++++ 5 files changed, 166 insertions(+) create mode 100644 SSKeychain/SSKeychainAccessControl.h create mode 100644 SSKeychain/SSKeychainAccessControl.m diff --git a/SSKeychain.xcodeproj/project.pbxproj b/SSKeychain.xcodeproj/project.pbxproj index 5677bf2..c2ddca9 100644 --- a/SSKeychain.xcodeproj/project.pbxproj +++ b/SSKeychain.xcodeproj/project.pbxproj @@ -7,6 +7,10 @@ objects = { /* Begin PBXBuildFile section */ + 4412CE1C1A9A372F00C35DE1 /* SSKeychainAccessControl.h in Headers */ = {isa = PBXBuildFile; fileRef = 4412CE1A1A9A372F00C35DE1 /* SSKeychainAccessControl.h */; }; + 4412CE1D1A9A372F00C35DE1 /* SSKeychainAccessControl.h in Headers */ = {isa = PBXBuildFile; fileRef = 4412CE1A1A9A372F00C35DE1 /* SSKeychainAccessControl.h */; }; + 4412CE1E1A9A372F00C35DE1 /* SSKeychainAccessControl.m in Sources */ = {isa = PBXBuildFile; fileRef = 4412CE1B1A9A372F00C35DE1 /* SSKeychainAccessControl.m */; }; + 4412CE1F1A9A372F00C35DE1 /* SSKeychainAccessControl.m in Sources */ = {isa = PBXBuildFile; fileRef = 4412CE1B1A9A372F00C35DE1 /* SSKeychainAccessControl.m */; }; E8A6665B1A844D3A00287CA3 /* SSKeychain.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E8A666341A844CC400287CA3 /* SSKeychain.framework */; }; E8A666641A844DB700287CA3 /* SSKeychain.h in Headers */ = {isa = PBXBuildFile; fileRef = 21CC42EF17DB878300201DDC /* SSKeychain.h */; settings = {ATTRIBUTES = (Public, ); }; }; E8A666651A844DB700287CA3 /* SSKeychain.m in Sources */ = {isa = PBXBuildFile; fileRef = 21CC42F017DB878300201DDC /* SSKeychain.m */; }; @@ -58,6 +62,8 @@ 21CC42F117DB878300201DDC /* SSKeychainQuery.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SSKeychainQuery.h; sourceTree = ""; }; 21CC42F217DB878300201DDC /* SSKeychainQuery.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SSKeychainQuery.m; sourceTree = ""; }; 21CC42F917DB87C300201DDC /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; + 4412CE1A1A9A372F00C35DE1 /* SSKeychainAccessControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SSKeychainAccessControl.h; sourceTree = ""; }; + 4412CE1B1A9A372F00C35DE1 /* SSKeychainAccessControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SSKeychainAccessControl.m; sourceTree = ""; }; E8A666341A844CC400287CA3 /* SSKeychain.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SSKeychain.framework; sourceTree = BUILT_PRODUCTS_DIR; }; E8A6664F1A844CF100287CA3 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; E8A666551A844D3A00287CA3 /* SSKeychain iOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SSKeychain iOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -166,6 +172,8 @@ 21CC42F017DB878300201DDC /* SSKeychain.m */, 21CC42F117DB878300201DDC /* SSKeychainQuery.h */, 21CC42F217DB878300201DDC /* SSKeychainQuery.m */, + 4412CE1A1A9A372F00C35DE1 /* SSKeychainAccessControl.h */, + 4412CE1B1A9A372F00C35DE1 /* SSKeychainAccessControl.m */, ); path = SSKeychain; sourceTree = ""; @@ -177,6 +185,7 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + 4412CE1C1A9A372F00C35DE1 /* SSKeychainAccessControl.h in Headers */, E8A666641A844DB700287CA3 /* SSKeychain.h in Headers */, E8A666661A844DB700287CA3 /* SSKeychainQuery.h in Headers */, ); @@ -186,6 +195,7 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + 4412CE1D1A9A372F00C35DE1 /* SSKeychainAccessControl.h in Headers */, E8A6668A1A844E5400287CA3 /* SSKeychain.h in Headers */, E8A6668C1A844E5400287CA3 /* SSKeychainQuery.h in Headers */, ); @@ -341,6 +351,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 4412CE1E1A9A372F00C35DE1 /* SSKeychainAccessControl.m in Sources */, E8A666671A844DB700287CA3 /* SSKeychainQuery.m in Sources */, E8A666651A844DB700287CA3 /* SSKeychain.m in Sources */, ); @@ -358,6 +369,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 4412CE1F1A9A372F00C35DE1 /* SSKeychainAccessControl.m in Sources */, E8A6668D1A844E5400287CA3 /* SSKeychainQuery.m in Sources */, E8A6668B1A844E5400287CA3 /* SSKeychain.m in Sources */, ); diff --git a/SSKeychain/SSKeychainAccessControl.h b/SSKeychain/SSKeychainAccessControl.h new file mode 100644 index 0000000..98df58e --- /dev/null +++ b/SSKeychain/SSKeychainAccessControl.h @@ -0,0 +1,51 @@ +// +// SSKeychainAccessControl.h +// SSKeychain +// +// Created by Liam Nichols on 01/09/2014. +// Copyright (c) 2014 Sam Soffes. All rights reserved. +// + +#import + +/** kSecAttrAccessible */ +typedef NS_ENUM(NSUInteger, SSKeychainAccessibility) { + /** kSecAttrAccessibleWhenUnlocked */ + SSKeychainAccessibilityWhenUnlocked = 1, + + /** kSecAttrAccessibleAfterFirstUnlock */ + SSKeychainAccessibilityAfterFirstUnlock, + + /** kSecAttrAccessibleAlways */ + SSKeychainAccessibilityAlways, + + /** kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly */ + SSKeychainAccessibilityWhenPasscodeSetThisDeviceOnly, + + /** kSecAttrAccessibleWhenUnlockedThisDeviceOnly */ + SSKeychainAccessibilityWhenUnlockedThisDeviceOnly, + + /** kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly */ + SSKeychainAccessibilityAfterFisrtUnlockThisDeviceOnly, + + /** kSecAttrAccessibleAlwaysThisDeviceOnly */ + SSKeychainAccessibilityAlwaysThisDeviceOnly +}; + +/** SecAccessControlCreateFlags */ +typedef NS_ENUM(NSInteger, SSKeychainCreateFlags) { + /** kSecAccessControlUserPresence */ + SSKeychainCreateFlagUserPresence = 1 << 0 +}; + +extern CFTypeRef getSecAttrAccessibility(SSKeychainAccessibility ssAttr); + +@interface SSKeychainAccessControl : NSObject + ++ (instancetype)accessControlWithAccessibility:(SSKeychainAccessibility)accesibility flags:(SSKeychainCreateFlags)flags; + +@property (nonatomic, assign) SSKeychainAccessibility accessibility; + +@property (nonatomic, assign) SSKeychainCreateFlags flags; + +@end diff --git a/SSKeychain/SSKeychainAccessControl.m b/SSKeychain/SSKeychainAccessControl.m new file mode 100644 index 0000000..f845f31 --- /dev/null +++ b/SSKeychain/SSKeychainAccessControl.m @@ -0,0 +1,50 @@ +// +// SSKeychainAccessControl.m +// SSKeychain +// +// Created by Liam Nichols on 01/09/2014. +// Copyright (c) 2014 Sam Soffes. All rights reserved. +// + +#import "SSKeychainAccessControl.h" + +@implementation SSKeychainAccessControl + ++ (instancetype)accessControlWithAccessibility:(SSKeychainAccessibility)accesibility flags:(SSKeychainCreateFlags)flags +{ + SSKeychainAccessControl *accessControl = [self new]; + accessControl.accessibility = accesibility; + accessControl.flags = flags; + return accessControl; +} + +@end + +CFTypeRef getSecAttrAccessibility(SSKeychainAccessibility ssAttr) +{ + switch (ssAttr) { + case SSKeychainAccessibilityAlways: + return kSecAttrAccessibleAlways; + + case SSKeychainAccessibilityWhenUnlocked: + return kSecAttrAccessibleWhenUnlocked; + + case SSKeychainAccessibilityAfterFirstUnlock: + return kSecAttrAccessibleAfterFirstUnlock; + + case SSKeychainAccessibilityAlwaysThisDeviceOnly: + return kSecAttrAccessibleAlwaysThisDeviceOnly; + + case SSKeychainAccessibilityWhenUnlockedThisDeviceOnly: + return kSecAttrAccessibleWhenUnlockedThisDeviceOnly; + + case SSKeychainAccessibilityWhenPasscodeSetThisDeviceOnly: + return kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly; + + case SSKeychainAccessibilityAfterFisrtUnlockThisDeviceOnly: + return kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly; + + default: + return NULL; + } +} diff --git a/SSKeychain/SSKeychainQuery.h b/SSKeychain/SSKeychainQuery.h index f0579e1..a7a1f94 100644 --- a/SSKeychain/SSKeychainQuery.h +++ b/SSKeychain/SSKeychainQuery.h @@ -9,6 +9,10 @@ @import Foundation; @import Security; +#if __IPHONE_8_0 || __MAC_10_10 + #import "SSKeychainAccessControl.h" +#endif + #if __IPHONE_7_0 || __MAC_10_9 // Keychain synchronization available at compile time #define SSKEYCHAIN_SYNCHRONIZATION_AVAILABLE 1 @@ -46,6 +50,17 @@ typedef NS_ENUM(NSUInteger, SSKeychainQuerySynchronizationMode) { @property (nonatomic) SSKeychainQuerySynchronizationMode synchronizationMode; #endif +#if __IPHONE_8_0 || __MAC_10_10 +/** kSecUseOperationPrompt */ +@property (nonatomic, copy) NSString *useOperationPrompt; + +/** kSecUseNoAuthenticationUI */ +@property (nonatomic, assign) NSNumber *useNoAuthenticationUI; + +/** kSecAttrAccessControl */ +@property (nonatomic, strong) SSKeychainAccessControl *accessControl; +#endif + /** Root storage for password information */ @property (nonatomic, copy) NSData *passwordData; diff --git a/SSKeychain/SSKeychainQuery.m b/SSKeychain/SSKeychainQuery.m index 70ee516..e734fe3 100644 --- a/SSKeychain/SSKeychainQuery.m +++ b/SSKeychain/SSKeychainQuery.m @@ -48,6 +48,31 @@ - (BOOL)save:(NSError *__autoreleasing *)error { [query setObject:(__bridge id)accessibilityType forKey:(__bridge id)kSecAttrAccessible]; } #endif + +#if __IPHONE_8_0 || __MAC_10_10 + if (self.useNoAuthenticationUI) { + [query setObject:self.useNoAuthenticationUI forKey:(__bridge id)kSecUseNoAuthenticationUI]; + } + if (self.accessControl) { + CFErrorRef sacError = NULL; + SecAccessControlRef sacObject; + + sacObject = SecAccessControlCreateWithFlags(kCFAllocatorDefault, + getSecAttrAccessibility(self.accessControl.accessibility), + (CFIndex)self.accessControl.flags, + &sacError); + + if (sacObject == NULL || sacError != NULL) { + if (error) { + *error = (__bridge NSError *)sacError; + } + return NO; + } + + [query setObject:(__bridge id)sacObject forKey:(__bridge id)kSecAttrAccessControl]; + } +#endif + status = SecItemAdd((__bridge CFDictionaryRef)query, NULL); if (status != errSecSuccess && error != NULL) { @@ -94,6 +119,12 @@ - (NSArray *)fetchAll:(NSError *__autoreleasing *)error { [query setObject:@YES forKey:(__bridge id)kSecReturnAttributes]; [query setObject:(__bridge id)kSecMatchLimitAll forKey:(__bridge id)kSecMatchLimit]; +#if __IPHONE_8_0 || __MAC_10_10 + if (self.useOperationPrompt) { + [query setObject:self.useOperationPrompt forKey:(__bridge id)kSecUseOperationPrompt]; + } +#endif + CFTypeRef result = NULL; status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result); if (status != errSecSuccess && error != NULL) { @@ -118,6 +149,13 @@ - (BOOL)fetch:(NSError *__autoreleasing *)error { NSMutableDictionary *query = [self query]; [query setObject:@YES forKey:(__bridge id)kSecReturnData]; [query setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit]; + +#if __IPHONE_8_0 || __MAC_10_10 + if (self.useOperationPrompt) { + [query setObject:self.useOperationPrompt forKey:(__bridge id)kSecUseOperationPrompt]; + } +#endif + status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result); if (status != errSecSuccess) { From e32292ecef8b76e01ba8538b4d6694e34c6a8337 Mon Sep 17 00:00:00 2001 From: liamnichols Date: Mon, 1 Sep 2014 19:27:36 +0100 Subject: [PATCH 2/8] added update query --- SSKeychain/SSKeychainQuery.h | 11 ++++++++++- SSKeychain/SSKeychainQuery.m | 38 ++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/SSKeychain/SSKeychainQuery.h b/SSKeychain/SSKeychainQuery.h index a7a1f94..3890b9a 100644 --- a/SSKeychain/SSKeychainQuery.h +++ b/SSKeychain/SSKeychainQuery.h @@ -78,7 +78,7 @@ typedef NS_ENUM(NSUInteger, SSKeychainQuerySynchronizationMode) { ///------------------------ -/// @name Saving & Deleting +/// @name Saving, Updating & Deleting ///------------------------ /** @@ -91,6 +91,15 @@ typedef NS_ENUM(NSUInteger, SSKeychainQuerySynchronizationMode) { */ - (BOOL)save:(NSError **)error; +/** + Updates the receiver's attributes. + + @param error Populated should an error occur. + + @return `YES` if saving was successful, `NO` otherwise. + */ +- (BOOL)update:(NSError **)error; + /** Delete keychain items that match the given account, service, and access group. diff --git a/SSKeychain/SSKeychainQuery.m b/SSKeychain/SSKeychainQuery.m index e734fe3..ee548fe 100644 --- a/SSKeychain/SSKeychainQuery.m +++ b/SSKeychain/SSKeychainQuery.m @@ -82,6 +82,44 @@ - (BOOL)save:(NSError *__autoreleasing *)error { return (status == errSecSuccess); } +- (BOOL)update:(NSError *__autoreleasing *)error +{ + OSStatus status = SSKeychainErrorBadArguments; + if (!self.service || !self.account || !self.passwordData) { + if (error) { + *error = [[self class] errorWithCode:status]; + } + return NO; + } + + NSMutableDictionary *query = [self query]; + NSMutableDictionary *changes = [NSMutableDictionary dictionary]; + + [changes setObject:self.passwordData forKey:(__bridge id)kSecValueData]; + if (self.label) { + [changes setObject:self.label forKey:(__bridge id)kSecAttrLabel]; + } +#if __IPHONE_4_0 && TARGET_OS_IPHONE + CFTypeRef accessibilityType = [SSKeychain accessibilityType]; + if (accessibilityType) { + [changes setObject:(__bridge id)accessibilityType forKey:(__bridge id)kSecAttrAccessible]; + } +#endif + +#if __IPHONE_8_0 || __MAC_10_10 + if (self.useOperationPrompt) { + [changes setObject:self.useOperationPrompt forKey:(__bridge id)kSecUseOperationPrompt]; + } +#endif + + status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)changes); + + if (status != errSecSuccess && error != NULL) { + *error = [[self class] errorWithCode:status]; + } + + return (status == errSecSuccess); +} - (BOOL)deleteItem:(NSError *__autoreleasing *)error { OSStatus status = SSKeychainErrorBadArguments; From 5d34786b82c654fea749e80e881d08a68a856d65 Mon Sep 17 00:00:00 2001 From: Liam Nichols Date: Tue, 2 Sep 2014 16:57:34 +0100 Subject: [PATCH 3/8] Update SSKeychainQuery.m --- SSKeychain/SSKeychainQuery.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SSKeychain/SSKeychainQuery.m b/SSKeychain/SSKeychainQuery.m index ee548fe..08116bc 100644 --- a/SSKeychain/SSKeychainQuery.m +++ b/SSKeychain/SSKeychainQuery.m @@ -108,7 +108,7 @@ - (BOOL)update:(NSError *__autoreleasing *)error #if __IPHONE_8_0 || __MAC_10_10 if (self.useOperationPrompt) { - [changes setObject:self.useOperationPrompt forKey:(__bridge id)kSecUseOperationPrompt]; + [query setObject:self.useOperationPrompt forKey:(__bridge id)kSecUseOperationPrompt]; } #endif From 3f636484130cef6f52ca03646a5085ba32b7b244 Mon Sep 17 00:00:00 2001 From: Rich Hodgkins Date: Sat, 10 Jan 2015 14:08:18 +0000 Subject: [PATCH 4/8] Correct enum for keychain create flags `SSKeychainCreateFlags` should use `NS_OPTIONS` not `NS_ENUM` so it can be correctly converted in Swift. --- SSKeychain/SSKeychainAccessControl.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SSKeychain/SSKeychainAccessControl.h b/SSKeychain/SSKeychainAccessControl.h index 98df58e..39d1a81 100644 --- a/SSKeychain/SSKeychainAccessControl.h +++ b/SSKeychain/SSKeychainAccessControl.h @@ -33,9 +33,9 @@ typedef NS_ENUM(NSUInteger, SSKeychainAccessibility) { }; /** SecAccessControlCreateFlags */ -typedef NS_ENUM(NSInteger, SSKeychainCreateFlags) { +typedef NS_OPTIONS(NSUInteger, SSKeychainCreateFlags) { /** kSecAccessControlUserPresence */ - SSKeychainCreateFlagUserPresence = 1 << 0 + SSKeychainCreateFlagUserPresence = 1UL << 0 }; extern CFTypeRef getSecAttrAccessibility(SSKeychainAccessibility ssAttr); From 0c9519134d454575dd35a1bd0b1ec28d9aab1707 Mon Sep 17 00:00:00 2001 From: Rich Hodgkins Date: Sun, 22 Feb 2015 16:19:08 +0000 Subject: [PATCH 5/8] Marked access control header as Public as this was added after the recent framework refactor. Also changed to use @import. --- SSKeychain.xcodeproj/project.pbxproj | 4 ++-- SSKeychain/SSKeychainAccessControl.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/SSKeychain.xcodeproj/project.pbxproj b/SSKeychain.xcodeproj/project.pbxproj index c2ddca9..71081fa 100644 --- a/SSKeychain.xcodeproj/project.pbxproj +++ b/SSKeychain.xcodeproj/project.pbxproj @@ -7,8 +7,8 @@ objects = { /* Begin PBXBuildFile section */ - 4412CE1C1A9A372F00C35DE1 /* SSKeychainAccessControl.h in Headers */ = {isa = PBXBuildFile; fileRef = 4412CE1A1A9A372F00C35DE1 /* SSKeychainAccessControl.h */; }; - 4412CE1D1A9A372F00C35DE1 /* SSKeychainAccessControl.h in Headers */ = {isa = PBXBuildFile; fileRef = 4412CE1A1A9A372F00C35DE1 /* SSKeychainAccessControl.h */; }; + 4412CE1C1A9A372F00C35DE1 /* SSKeychainAccessControl.h in Headers */ = {isa = PBXBuildFile; fileRef = 4412CE1A1A9A372F00C35DE1 /* SSKeychainAccessControl.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4412CE1D1A9A372F00C35DE1 /* SSKeychainAccessControl.h in Headers */ = {isa = PBXBuildFile; fileRef = 4412CE1A1A9A372F00C35DE1 /* SSKeychainAccessControl.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4412CE1E1A9A372F00C35DE1 /* SSKeychainAccessControl.m in Sources */ = {isa = PBXBuildFile; fileRef = 4412CE1B1A9A372F00C35DE1 /* SSKeychainAccessControl.m */; }; 4412CE1F1A9A372F00C35DE1 /* SSKeychainAccessControl.m in Sources */ = {isa = PBXBuildFile; fileRef = 4412CE1B1A9A372F00C35DE1 /* SSKeychainAccessControl.m */; }; E8A6665B1A844D3A00287CA3 /* SSKeychain.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E8A666341A844CC400287CA3 /* SSKeychain.framework */; }; diff --git a/SSKeychain/SSKeychainAccessControl.h b/SSKeychain/SSKeychainAccessControl.h index 39d1a81..eb139c9 100644 --- a/SSKeychain/SSKeychainAccessControl.h +++ b/SSKeychain/SSKeychainAccessControl.h @@ -6,7 +6,7 @@ // Copyright (c) 2014 Sam Soffes. All rights reserved. // -#import +@import Foundation; /** kSecAttrAccessible */ typedef NS_ENUM(NSUInteger, SSKeychainAccessibility) { From 7958da0bd59d1915d75affe382cbf1d588c42492 Mon Sep 17 00:00:00 2001 From: Rich Hodgkins Date: Sun, 22 Feb 2015 16:39:55 +0000 Subject: [PATCH 6/8] Added #define and method for compile and runtime checking to see if access control is available, matching the system used for checking is synchronization is available --- SSKeychain/SSKeychainQuery.h | 26 ++++++++++++++++++++++++-- SSKeychain/SSKeychainQuery.m | 29 +++++++++++++++++++++++++---- 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/SSKeychain/SSKeychainQuery.h b/SSKeychain/SSKeychainQuery.h index 3890b9a..6619ee3 100644 --- a/SSKeychain/SSKeychainQuery.h +++ b/SSKeychain/SSKeychainQuery.h @@ -10,6 +10,10 @@ @import Security; #if __IPHONE_8_0 || __MAC_10_10 + #define SSKEYCHAIN_ACCESS_CONTROL_AVAILABLE 1 +#endif + +#ifdef SSKEYCHAIN_ACCESS_CONTROL_AVAILABLE #import "SSKeychainAccessControl.h" #endif @@ -50,13 +54,15 @@ typedef NS_ENUM(NSUInteger, SSKeychainQuerySynchronizationMode) { @property (nonatomic) SSKeychainQuerySynchronizationMode synchronizationMode; #endif -#if __IPHONE_8_0 || __MAC_10_10 +#if __IPHONE_8_0 && TARGET_OS_IPHONE /** kSecUseOperationPrompt */ @property (nonatomic, copy) NSString *useOperationPrompt; /** kSecUseNoAuthenticationUI */ @property (nonatomic, assign) NSNumber *useNoAuthenticationUI; +#endif +#if SSKEYCHAIN_ACCESS_CONTROL_AVAILABLE /** kSecAttrAccessControl */ @property (nonatomic, strong) SSKeychainAccessControl *accessControl; #endif @@ -145,7 +151,7 @@ typedef NS_ENUM(NSUInteger, SSKeychainQuerySynchronizationMode) { #ifdef SSKEYCHAIN_SYNCHRONIZATION_AVAILABLE /** - Returns a boolean indicating if keychain synchronization is available on the device at runtime. The #define + Returns a boolean indicating if keychain synchronization is available on the device at runtime. The #define SSKEYCHAIN_SYNCHRONIZATION_AVAILABLE is only for compile time. If you are checking for the presence of synchronization, you should use this method. @@ -154,4 +160,20 @@ typedef NS_ENUM(NSUInteger, SSKeychainQuerySynchronizationMode) { + (BOOL)isSynchronizationAvailable; #endif + +///----------------------------- +/// @name Access Control Status +///----------------------------- + +#ifdef SSKEYCHAIN_ACCESS_CONTROL_AVAILABLE +/** + Returns a boolean indicating if keychain access control is available on the device at runtime. The #define + SSKEYCHAIN_ACCESS_CONTROL_AVAILABLE is only for compile time. If you are checking for the presence of access control, + you should use this method. + + @return A value indicating if keychain access control is available + */ ++ (BOOL)isAccessControlAvailable; +#endif + @end diff --git a/SSKeychain/SSKeychainQuery.m b/SSKeychain/SSKeychainQuery.m index 08116bc..80c38b4 100644 --- a/SSKeychain/SSKeychainQuery.m +++ b/SSKeychain/SSKeychainQuery.m @@ -20,6 +20,10 @@ @implementation SSKeychainQuery @synthesize accessGroup = _accessGroup; #endif +#ifdef SSKEYCHAIN_ACCESS_CONTROL_AVAILABLE +@synthesize accessControl = _accessControl; +#endif + #ifdef SSKEYCHAIN_SYNCHRONIZATION_AVAILABLE @synthesize synchronizationMode = _synchronizationMode; #endif @@ -49,10 +53,12 @@ - (BOOL)save:(NSError *__autoreleasing *)error { } #endif -#if __IPHONE_8_0 || __MAC_10_10 +#if __IPHONE_8_0 && TARGET_OS_IPHONE if (self.useNoAuthenticationUI) { [query setObject:self.useNoAuthenticationUI forKey:(__bridge id)kSecUseNoAuthenticationUI]; } +#endif +#if SSKEYCHAIN_ACCESS_CONTROL_AVAILABLE if (self.accessControl) { CFErrorRef sacError = NULL; SecAccessControlRef sacObject; @@ -106,7 +112,7 @@ - (BOOL)update:(NSError *__autoreleasing *)error } #endif -#if __IPHONE_8_0 || __MAC_10_10 +#if __IPHONE_8_0 && TARGET_OS_IPHONE if (self.useOperationPrompt) { [query setObject:self.useOperationPrompt forKey:(__bridge id)kSecUseOperationPrompt]; } @@ -157,7 +163,7 @@ - (NSArray *)fetchAll:(NSError *__autoreleasing *)error { [query setObject:@YES forKey:(__bridge id)kSecReturnAttributes]; [query setObject:(__bridge id)kSecMatchLimitAll forKey:(__bridge id)kSecMatchLimit]; -#if __IPHONE_8_0 || __MAC_10_10 +#if __IPHONE_8_0 && TARGET_OS_IPHONE if (self.useOperationPrompt) { [query setObject:self.useOperationPrompt forKey:(__bridge id)kSecUseOperationPrompt]; } @@ -188,7 +194,7 @@ - (BOOL)fetch:(NSError *__autoreleasing *)error { [query setObject:@YES forKey:(__bridge id)kSecReturnData]; [query setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit]; -#if __IPHONE_8_0 || __MAC_10_10 +#if __IPHONE_8_0 && TARGET_OS_IPHONE if (self.useOperationPrompt) { [query setObject:self.useOperationPrompt forKey:(__bridge id)kSecUseOperationPrompt]; } @@ -251,6 +257,21 @@ + (BOOL)isSynchronizationAvailable { #endif +#pragma mark - Access Control Status + +#ifdef SSKEYCHAIN_ACCESS_CONTROL_AVAILABLE ++ (BOOL)isAccessControlAvailable { +#if TARGET_OS_IPHONE + // Apple suggested way to check for 8.0 at runtime + // https://developer.apple.com/library/ios/documentation/userexperience/conceptual/transitionguide/SupportingEarlieriOS.html + return floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_7_1; +#else + return floor(NSFoundationVersionNumber) > NSFoundationVersionNumber10_9_2; +#endif +} +#endif + + #pragma mark - Private - (NSMutableDictionary *)query { From 7ab5273cf677fb72e62dd70e435db7f0ef5af136 Mon Sep 17 00:00:00 2001 From: Rich Hodgkins Date: Sun, 22 Feb 2015 16:42:09 +0000 Subject: [PATCH 7/8] Changed header import to non-user header type and added @import to access control header --- SSKeychain/SSKeychainAccessControl.h | 1 + SSKeychain/SSKeychainQuery.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/SSKeychain/SSKeychainAccessControl.h b/SSKeychain/SSKeychainAccessControl.h index eb139c9..a34eaf3 100644 --- a/SSKeychain/SSKeychainAccessControl.h +++ b/SSKeychain/SSKeychainAccessControl.h @@ -7,6 +7,7 @@ // @import Foundation; +@import Security; /** kSecAttrAccessible */ typedef NS_ENUM(NSUInteger, SSKeychainAccessibility) { diff --git a/SSKeychain/SSKeychainQuery.h b/SSKeychain/SSKeychainQuery.h index 6619ee3..63608bc 100644 --- a/SSKeychain/SSKeychainQuery.h +++ b/SSKeychain/SSKeychainQuery.h @@ -14,7 +14,7 @@ #endif #ifdef SSKEYCHAIN_ACCESS_CONTROL_AVAILABLE - #import "SSKeychainAccessControl.h" + #import #endif #if __IPHONE_7_0 || __MAC_10_9 From 5dca9368de7b94c39ceb2b252d7fbf8fd3ddf8e3 Mon Sep 17 00:00:00 2001 From: Rich Hodgkins Date: Sun, 22 Feb 2015 16:43:44 +0000 Subject: [PATCH 8/8] Fixed Xcode warning about SSKeychain.strings being imported as UTF-8, while it actually contains UTF-16 --- SSKeychain.xcodeproj/project.pbxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SSKeychain.xcodeproj/project.pbxproj b/SSKeychain.xcodeproj/project.pbxproj index 71081fa..211a496 100644 --- a/SSKeychain.xcodeproj/project.pbxproj +++ b/SSKeychain.xcodeproj/project.pbxproj @@ -56,7 +56,7 @@ 21CC42AE17DB874300201DDC /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 21CC42C317DB874300201DDC /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 21CC42DB17DB877900201DDC /* SSKeychainTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SSKeychainTests.m; sourceTree = ""; }; - 21CC42EE17DB878300201DDC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/SSKeychain.strings; sourceTree = ""; }; + 21CC42EE17DB878300201DDC /* en */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/SSKeychain.strings; sourceTree = ""; }; 21CC42EF17DB878300201DDC /* SSKeychain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SSKeychain.h; sourceTree = ""; }; 21CC42F017DB878300201DDC /* SSKeychain.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SSKeychain.m; sourceTree = ""; }; 21CC42F117DB878300201DDC /* SSKeychainQuery.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SSKeychainQuery.h; sourceTree = ""; };