From ab40c6632124ae8a8338eac0b88377fe40e6bb5c Mon Sep 17 00:00:00 2001 From: liamnichols Date: Mon, 1 Sep 2014 19:18:28 +0100 Subject: [PATCH 1/5] implemented support for touch id --- Example/iOS/SSAppDelegate.m | 29 ++++++++++++++++ SSKeychain.xcodeproj/project.pbxproj | 18 ++++++++++ SSKeychain/SSKeychainAccessControl.h | 51 ++++++++++++++++++++++++++++ SSKeychain/SSKeychainAccessControl.m | 50 +++++++++++++++++++++++++++ SSKeychain/SSKeychainQuery.h | 15 ++++++++ SSKeychain/SSKeychainQuery.m | 38 +++++++++++++++++++++ 6 files changed, 201 insertions(+) create mode 100644 SSKeychain/SSKeychainAccessControl.h create mode 100644 SSKeychain/SSKeychainAccessControl.m diff --git a/Example/iOS/SSAppDelegate.m b/Example/iOS/SSAppDelegate.m index 655260a..d485019 100644 --- a/Example/iOS/SSAppDelegate.m +++ b/Example/iOS/SSAppDelegate.m @@ -7,10 +7,39 @@ // #import "SSAppDelegate.h" +#import "SSKeychain.h" @implementation SSAppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + +// SSKeychainQuery *query = [[SSKeychainQuery alloc] init]; +// query.service = @"serviceName"; +// query.account = @"accountName"; +// query.password = @"securedPassword"; +// query.useNoAuthenticationUI = @YES; +// query.accessControl = [SSKeychainAccessControl accessControlWithAccessibility:SSKeychainAccessibilityWhenUnlockedThisDeviceOnly flags:SSKeychainCreateFlagUserPresence]; +// +// +// NSError *error = nil; +// BOOL result = [query save:&error]; + + + + + SSKeychainQuery *query = [[SSKeychainQuery alloc] init]; + query.service = @"serviceName"; + query.account = @"accountName"; + + + dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){ + NSError *error = nil; + BOOL result = [query fetch:&error]; + NSLog(@"Result: %@", result ? @"Success" : @"Failure"); + NSLog(@"Error: %@", error); + NSLog(@"Data: %@", query.passwordData); + }); + self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible]; diff --git a/SSKeychain.xcodeproj/project.pbxproj b/SSKeychain.xcodeproj/project.pbxproj index 0f79e44..a781343 100644 --- a/SSKeychain.xcodeproj/project.pbxproj +++ b/SSKeychain.xcodeproj/project.pbxproj @@ -38,6 +38,7 @@ 21DB72BA18ECA0BB00531E87 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 21DB72B518ECA0BB00531E87 /* main.m */; }; 21DB72BB18ECA0BB00531E87 /* SSAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 21DB72B718ECA0BB00531E87 /* SSAppDelegate.m */; }; 21F116AC18EC9E6400126982 /* SSKeychainMac.entitlements in Resources */ = {isa = PBXBuildFile; fileRef = 21F116AB18EC9E6400126982 /* SSKeychainMac.entitlements */; }; + 5361F5F519B4E71E005E92AD /* SSKeychainAccessControl.m in Sources */ = {isa = PBXBuildFile; fileRef = 5361F5F419B4E71E005E92AD /* SSKeychainAccessControl.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -90,6 +91,8 @@ 21DB72B718ECA0BB00531E87 /* SSAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SSAppDelegate.m; sourceTree = ""; }; 21DB72B818ECA0BB00531E87 /* SSKeychain-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "SSKeychain-Info.plist"; sourceTree = ""; }; 21F116AB18EC9E6400126982 /* SSKeychainMac.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = SSKeychainMac.entitlements; sourceTree = ""; }; + 5361F5F319B4E71E005E92AD /* SSKeychainAccessControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SSKeychainAccessControl.h; sourceTree = ""; }; + 5361F5F419B4E71E005E92AD /* SSKeychainAccessControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SSKeychainAccessControl.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -224,6 +227,8 @@ 21CC42F017DB878300201DDC /* SSKeychain.m */, 21CC42F117DB878300201DDC /* SSKeychainQuery.h */, 21CC42F217DB878300201DDC /* SSKeychainQuery.m */, + 5361F5F319B4E71E005E92AD /* SSKeychainAccessControl.h */, + 5361F5F419B4E71E005E92AD /* SSKeychainAccessControl.m */, ); path = SSKeychain; sourceTree = ""; @@ -333,6 +338,9 @@ 21B85B4218EC9456009D2B98 = { TestTargetID = 21CC42A617DB874300201DDC; }; + 21CC42A617DB874300201DDC = { + DevelopmentTeam = RPD4MEV338; + }; 21CC42C117DB874300201DDC = { TestTargetID = 21CC42A617DB874300201DDC; }; @@ -421,6 +429,7 @@ buildActionMask = 2147483647; files = ( 21DB72BB18ECA0BB00531E87 /* SSAppDelegate.m in Sources */, + 5361F5F519B4E71E005E92AD /* SSKeychainAccessControl.m in Sources */, 21CC42F717DB878300201DDC /* SSKeychainQuery.m in Sources */, 21DB72BA18ECA0BB00531E87 /* main.m in Sources */, 21CC42F517DB878300201DDC /* SSKeychain.m in Sources */, @@ -515,8 +524,11 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; INFOPLIST_FILE = "$(SRCROOT)/Example/iOS/SSKeychain-Info.plist"; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = ""; WRAPPER_EXTENSION = app; }; name = Test; @@ -759,8 +771,11 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; INFOPLIST_FILE = "$(SRCROOT)/Example/iOS/SSKeychain-Info.plist"; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = ""; WRAPPER_EXTENSION = app; }; name = Debug; @@ -770,8 +785,11 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; INFOPLIST_FILE = "$(SRCROOT)/Example/iOS/SSKeychain-Info.plist"; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = ""; WRAPPER_EXTENSION = app; }; name = Release; 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 7035db3..ac5651f 100644 --- a/SSKeychain/SSKeychainQuery.h +++ b/SSKeychain/SSKeychainQuery.h @@ -9,6 +9,10 @@ #import #import +#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 b93c620..4417093 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 && error != NULL) { From 6e93cb417ee95f8a92e578f27db6a67abe18da40 Mon Sep 17 00:00:00 2001 From: liamnichols Date: Mon, 1 Sep 2014 19:27:36 +0100 Subject: [PATCH 2/5] 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 ac5651f..de5d977 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 4417093..d97f7b7 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 9824364b82fdcdb801d986bc0cc26b095e6fd437 Mon Sep 17 00:00:00 2001 From: liamnichols Date: Mon, 1 Sep 2014 19:29:01 +0100 Subject: [PATCH 3/5] removed test code --- Example/iOS/SSAppDelegate.m | 30 +----------------------------- 1 file changed, 1 insertion(+), 29 deletions(-) diff --git a/Example/iOS/SSAppDelegate.m b/Example/iOS/SSAppDelegate.m index d485019..2a8197a 100644 --- a/Example/iOS/SSAppDelegate.m +++ b/Example/iOS/SSAppDelegate.m @@ -11,35 +11,7 @@ @implementation SSAppDelegate -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - -// SSKeychainQuery *query = [[SSKeychainQuery alloc] init]; -// query.service = @"serviceName"; -// query.account = @"accountName"; -// query.password = @"securedPassword"; -// query.useNoAuthenticationUI = @YES; -// query.accessControl = [SSKeychainAccessControl accessControlWithAccessibility:SSKeychainAccessibilityWhenUnlockedThisDeviceOnly flags:SSKeychainCreateFlagUserPresence]; -// -// -// NSError *error = nil; -// BOOL result = [query save:&error]; - - - - - SSKeychainQuery *query = [[SSKeychainQuery alloc] init]; - query.service = @"serviceName"; - query.account = @"accountName"; - - - dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){ - NSError *error = nil; - BOOL result = [query fetch:&error]; - NSLog(@"Result: %@", result ? @"Success" : @"Failure"); - NSLog(@"Error: %@", error); - NSLog(@"Data: %@", query.passwordData); - }); - +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible]; From 987f1d0652b2522d645afb6edf0b0c2bbab78d69 Mon Sep 17 00:00:00 2001 From: Liam Nichols Date: Tue, 2 Sep 2014 16:57:34 +0100 Subject: [PATCH 4/5] 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 d97f7b7..a384e9f 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 0bec357c1f110e9ff1e105c25fc68e2db41cf5aa Mon Sep 17 00:00:00 2001 From: Rich Hodgkins Date: Sat, 10 Jan 2015 14:08:18 +0000 Subject: [PATCH 5/5] 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);