From 1826aae43c68147357dffbf925d5bd71bd804eb6 Mon Sep 17 00:00:00 2001 From: Roger Oba Date: Tue, 17 Apr 2018 12:10:49 -0300 Subject: [PATCH 1/8] Import support to Keychain Access Control features from "@rhodgkins". --- SAMKeychain.xcodeproj/project.pbxproj | 20 ++++++ Sources/SAMKeychainAccessControl.h | 52 ++++++++++++++ Sources/SAMKeychainAccessControl.m | 50 ++++++++++++++ Sources/SAMKeychainQuery.h | 51 +++++++++++++- Sources/SAMKeychainQuery.m | 97 ++++++++++++++++++++++++++- Tests/KeychainTests.swift | 2 +- 6 files changed, 268 insertions(+), 4 deletions(-) create mode 100644 Sources/SAMKeychainAccessControl.h create mode 100644 Sources/SAMKeychainAccessControl.m diff --git a/SAMKeychain.xcodeproj/project.pbxproj b/SAMKeychain.xcodeproj/project.pbxproj index 4e54734..a10cf20 100644 --- a/SAMKeychain.xcodeproj/project.pbxproj +++ b/SAMKeychain.xcodeproj/project.pbxproj @@ -31,6 +31,14 @@ 21632DD31C9282BD00C40D7D /* KeychainTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21632DD21C9282BD00C40D7D /* KeychainTests.swift */; }; 21632DD41C9282BD00C40D7D /* KeychainTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21632DD21C9282BD00C40D7D /* KeychainTests.swift */; }; 21632DD51C9282BD00C40D7D /* KeychainTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21632DD21C9282BD00C40D7D /* KeychainTests.swift */; }; + C598E1962086415500B79528 /* SAMKeychainAccessControl.h in Headers */ = {isa = PBXBuildFile; fileRef = C598E1942086415500B79528 /* SAMKeychainAccessControl.h */; }; + C598E1972086415500B79528 /* SAMKeychainAccessControl.h in Headers */ = {isa = PBXBuildFile; fileRef = C598E1942086415500B79528 /* SAMKeychainAccessControl.h */; }; + C598E1982086415500B79528 /* SAMKeychainAccessControl.h in Headers */ = {isa = PBXBuildFile; fileRef = C598E1942086415500B79528 /* SAMKeychainAccessControl.h */; }; + C598E1992086415500B79528 /* SAMKeychainAccessControl.h in Headers */ = {isa = PBXBuildFile; fileRef = C598E1942086415500B79528 /* SAMKeychainAccessControl.h */; }; + C598E19A2086415500B79528 /* SAMKeychainAccessControl.m in Sources */ = {isa = PBXBuildFile; fileRef = C598E1952086415500B79528 /* SAMKeychainAccessControl.m */; }; + C598E19B2086415500B79528 /* SAMKeychainAccessControl.m in Sources */ = {isa = PBXBuildFile; fileRef = C598E1952086415500B79528 /* SAMKeychainAccessControl.m */; }; + C598E19C2086415500B79528 /* SAMKeychainAccessControl.m in Sources */ = {isa = PBXBuildFile; fileRef = C598E1952086415500B79528 /* SAMKeychainAccessControl.m */; }; + C598E19D2086415500B79528 /* SAMKeychainAccessControl.m in Sources */ = {isa = PBXBuildFile; fileRef = C598E1952086415500B79528 /* SAMKeychainAccessControl.m */; }; E8A6665B1A844D3A00287CA3 /* SAMKeychain.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E8A666341A844CC400287CA3 /* SAMKeychain.framework */; }; E8A6667A1A844E4100287CA3 /* SAMKeychain.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E8A6666F1A844E4100287CA3 /* SAMKeychain.framework */; }; /* End PBXBuildFile section */ @@ -82,6 +90,8 @@ 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; }; 21CC42F917DB87C300201DDC /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; + C598E1942086415500B79528 /* SAMKeychainAccessControl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SAMKeychainAccessControl.h; sourceTree = ""; }; + C598E1952086415500B79528 /* SAMKeychainAccessControl.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SAMKeychainAccessControl.m; sourceTree = ""; }; E8A666341A844CC400287CA3 /* SAMKeychain.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SAMKeychain.framework; sourceTree = BUILT_PRODUCTS_DIR; }; E8A666551A844D3A00287CA3 /* SAMKeychainTests-iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SAMKeychainTests-iOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; E8A6666F1A844E4100287CA3 /* SAMKeychain.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SAMKeychain.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -151,6 +161,8 @@ 21632D7D1C92599100C40D7D /* SAMKeychain.m */, 21632D7E1C92599100C40D7D /* SAMKeychainQuery.h */, 21632D7F1C92599100C40D7D /* SAMKeychainQuery.m */, + C598E1942086415500B79528 /* SAMKeychainAccessControl.h */, + C598E1952086415500B79528 /* SAMKeychainAccessControl.m */, ); path = Sources; sourceTree = ""; @@ -232,6 +244,7 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + C598E1982086415500B79528 /* SAMKeychainAccessControl.h in Headers */, 21632DB11C925A6000C40D7D /* SAMKeychain.h in Headers */, 21632DB31C925A6000C40D7D /* SAMKeychainQuery.h in Headers */, ); @@ -241,6 +254,7 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + C598E1992086415500B79528 /* SAMKeychainAccessControl.h in Headers */, 21632DC51C925B3700C40D7D /* SAMKeychain.h in Headers */, 21632DC71C925B3700C40D7D /* SAMKeychainQuery.h in Headers */, ); @@ -250,6 +264,7 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + C598E1962086415500B79528 /* SAMKeychainAccessControl.h in Headers */, 21632D821C92599100C40D7D /* SAMKeychain.h in Headers */, 21632D841C92599100C40D7D /* SAMKeychainQuery.h in Headers */, ); @@ -259,6 +274,7 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + C598E1972086415500B79528 /* SAMKeychainAccessControl.h in Headers */, 21632D8F1C9259C100C40D7D /* SAMKeychain.h in Headers */, 21632D911C9259C100C40D7D /* SAMKeychainQuery.h in Headers */, ); @@ -505,6 +521,7 @@ buildActionMask = 2147483647; files = ( 21632DB41C925A6000C40D7D /* SAMKeychainQuery.m in Sources */, + C598E19C2086415500B79528 /* SAMKeychainAccessControl.m in Sources */, 21632DB21C925A6000C40D7D /* SAMKeychain.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -522,6 +539,7 @@ buildActionMask = 2147483647; files = ( 21632DC81C925B3700C40D7D /* SAMKeychainQuery.m in Sources */, + C598E19D2086415500B79528 /* SAMKeychainAccessControl.m in Sources */, 21632DC61C925B3700C40D7D /* SAMKeychain.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -531,6 +549,7 @@ buildActionMask = 2147483647; files = ( 21632D851C92599100C40D7D /* SAMKeychainQuery.m in Sources */, + C598E19A2086415500B79528 /* SAMKeychainAccessControl.m in Sources */, 21632D831C92599100C40D7D /* SAMKeychain.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -548,6 +567,7 @@ buildActionMask = 2147483647; files = ( 21632D921C9259C100C40D7D /* SAMKeychainQuery.m in Sources */, + C598E19B2086415500B79528 /* SAMKeychainAccessControl.m in Sources */, 21632D901C9259C100C40D7D /* SAMKeychain.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Sources/SAMKeychainAccessControl.h b/Sources/SAMKeychainAccessControl.h new file mode 100644 index 0000000..e9a778a --- /dev/null +++ b/Sources/SAMKeychainAccessControl.h @@ -0,0 +1,52 @@ +// +// SAMKeychainAccessControl.h +// SAMKeychain +// +// Created by Liam Nichols on 01/09/2014. +// Copyright (c) 2014 Sam Soffes. All rights reserved. +// + +@import Foundation; +@import Security; + +/** kSecAttrAccessible */ +typedef NS_ENUM(NSUInteger, SAMKeychainAccessibility) { + /** kSecAttrAccessibleWhenUnlocked */ + SAMKeychainAccessibilityWhenUnlocked = 1, + + /** kSecAttrAccessibleAfterFirstUnlock */ + SAMKeychainAccessibilityAfterFirstUnlock, + + /** kSecAttrAccessibleAlways */ + SAMKeychainAccessibilityAlways, + + /** kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly */ + SAMKeychainAccessibilityWhenPasscodeSetThisDeviceOnly, + + /** kSecAttrAccessibleWhenUnlockedThisDeviceOnly */ + SAMKeychainAccessibilityWhenUnlockedThisDeviceOnly, + + /** kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly */ + SAMKeychainAccessibilityAfterFisrtUnlockThisDeviceOnly, + + /** kSecAttrAccessibleAlwaysThisDeviceOnly */ + SAMKeychainAccessibilityAlwaysThisDeviceOnly +}; + +/** SecAccessControlCreateFlags */ +typedef NS_OPTIONS(NSUInteger, SAMKeychainCreateFlags) { + /** kSecAccessControlUserPresence */ + SAMKeychainCreateFlagUserPresence = 1UL << 0 +}; + +extern CFTypeRef getSecAttrAccessibility(SAMKeychainAccessibility ssAttr); + +@interface SAMKeychainAccessControl : NSObject + ++ (instancetype)accessControlWithAccessibility:(SAMKeychainAccessibility)accesibility flags:(SAMKeychainCreateFlags)flags; + +@property (nonatomic, assign) SAMKeychainAccessibility accessibility; + +@property (nonatomic, assign) SAMKeychainCreateFlags flags; + +@end diff --git a/Sources/SAMKeychainAccessControl.m b/Sources/SAMKeychainAccessControl.m new file mode 100644 index 0000000..a427946 --- /dev/null +++ b/Sources/SAMKeychainAccessControl.m @@ -0,0 +1,50 @@ +// +// SAMKeychainAccessControl.m +// SAMKeychain +// +// Created by Liam Nichols on 01/09/2014. +// Copyright (c) 2014 Sam Soffes. All rights reserved. +// + +#import "SAMKeychainAccessControl.h" + +@implementation SAMKeychainAccessControl + ++ (instancetype)accessControlWithAccessibility:(SAMKeychainAccessibility)accesibility flags:(SAMKeychainCreateFlags)flags +{ + SAMKeychainAccessControl *accessControl = [self new]; + accessControl.accessibility = accesibility; + accessControl.flags = flags; + return accessControl; +} + +@end + +CFTypeRef getSecAttrAccessibility(SAMKeychainAccessibility ssAttr) +{ + switch (ssAttr) { + case SAMKeychainAccessibilityAlways: + return kSecAttrAccessibleAlways; + + case SAMKeychainAccessibilityWhenUnlocked: + return kSecAttrAccessibleWhenUnlocked; + + case SAMKeychainAccessibilityAfterFirstUnlock: + return kSecAttrAccessibleAfterFirstUnlock; + + case SAMKeychainAccessibilityAlwaysThisDeviceOnly: + return kSecAttrAccessibleAlwaysThisDeviceOnly; + + case SAMKeychainAccessibilityWhenUnlockedThisDeviceOnly: + return kSecAttrAccessibleWhenUnlockedThisDeviceOnly; + + case SAMKeychainAccessibilityWhenPasscodeSetThisDeviceOnly: + return kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly; + + case SAMKeychainAccessibilityAfterFisrtUnlockThisDeviceOnly: + return kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly; + + default: + return NULL; + } +} diff --git a/Sources/SAMKeychainQuery.h b/Sources/SAMKeychainQuery.h index 84658e2..c478df4 100644 --- a/Sources/SAMKeychainQuery.h +++ b/Sources/SAMKeychainQuery.h @@ -14,6 +14,14 @@ #import #endif +#if __IPHONE_8_0 || __MAC_10_10 + #define SAMKEYCHAIN_ACCESS_CONTROL_AVAILABLE 1 +#endif + +#ifdef SAMKEYCHAIN_ACCESS_CONTROL_AVAILABLE + #import +#endif + NS_ASSUME_NONNULL_BEGIN #if __IPHONE_7_0 || __MAC_10_9 @@ -58,6 +66,20 @@ typedef NS_ENUM(NSUInteger, SAMKeychainQuerySynchronizationMode) { @property (nonatomic) SAMKeychainQuerySynchronizationMode synchronizationMode; #endif +#if __IPHONE_8_0 && TARGET_OS_IPHONE +/** kSecUseOperationPrompt */ +@property (nonatomic, copy) NSString *useOperationPrompt; + +/** kSecUseNoAuthenticationUI */ +@property (nonatomic, assign) NSNumber *useNoAuthenticationUI; +#endif + +#if SAMKEYCHAIN_ACCESS_CONTROL_AVAILABLE +/** kSecAttrAccessControl */ +@property (nonatomic, strong) SAMKeychainAccessControl *accessControl; +#endif + + /** Root storage for password information */ @property (nonatomic, copy, nullable) NSData *passwordData; @@ -75,7 +97,7 @@ typedef NS_ENUM(NSUInteger, SAMKeychainQuerySynchronizationMode) { ///------------------------ -/// @name Saving & Deleting +/// @name Saving, Updating & Deleting ///------------------------ /** @@ -88,6 +110,15 @@ typedef NS_ENUM(NSUInteger, SAMKeychainQuerySynchronizationMode) { */ - (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. @@ -133,7 +164,7 @@ typedef NS_ENUM(NSUInteger, SAMKeychainQuerySynchronizationMode) { #ifdef SAMKEYCHAIN_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 SAMKEYCHAIN_SYNCHRONIZATION_AVAILABLE is only for compile time. If you are checking for the presence of synchronization, you should use this method. @@ -142,6 +173,22 @@ typedef NS_ENUM(NSUInteger, SAMKeychainQuerySynchronizationMode) { + (BOOL)isSynchronizationAvailable; #endif + +///----------------------------- +/// @name Access Control Status +///----------------------------- + +#ifdef SAMKEYCHAIN_ACCESS_CONTROL_AVAILABLE +/** + Returns a boolean indicating if keychain access control is available on the device at runtime. The #define + SAMKEYCHAIN_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 NS_ASSUME_NONNULL_END diff --git a/Sources/SAMKeychainQuery.m b/Sources/SAMKeychainQuery.m index 00ecb80..9713d69 100644 --- a/Sources/SAMKeychainQuery.m +++ b/Sources/SAMKeychainQuery.m @@ -20,6 +20,10 @@ @implementation SAMKeychainQuery @synthesize accessGroup = _accessGroup; #endif +#ifdef SAMKEYCHAIN_ACCESS_CONTROL_AVAILABLE +@synthesize accessControl = _accessControl; +#endif + #ifdef SAMKEYCHAIN_SYNCHRONIZATION_AVAILABLE @synthesize synchronizationMode = _synchronizationMode; #endif @@ -59,13 +63,78 @@ - (BOOL)save:(NSError *__autoreleasing *)error { [query setObject:(__bridge id)accessibilityType forKey:(__bridge id)kSecAttrAccessible]; } #endif + +#if __IPHONE_8_0 && TARGET_OS_IPHONE + if (self.useNoAuthenticationUI) { + [query setObject:self.useNoAuthenticationUI forKey:(__bridge id)kSecUseNoAuthenticationUI]; + } +#endif +#if SAMKEYCHAIN_ACCESS_CONTROL_AVAILABLE + 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) { *error = [[self class] errorWithCode:status]; } - return (status == errSecSuccess);} + return (status == errSecSuccess); +} +- (BOOL)update:(NSError *__autoreleasing *)error { + OSStatus status = SAMKeychainErrorBadArguments; + 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 = [SAMKeychain accessibilityType]; + if (accessibilityType) { + [changes setObject:(__bridge id)accessibilityType forKey:(__bridge id)kSecAttrAccessible]; + } +#endif + +#if __IPHONE_8_0 && TARGET_OS_IPHONE + if (self.useOperationPrompt) { + [query 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 = SAMKeychainErrorBadArguments; @@ -117,6 +186,12 @@ - (nullable NSArray *)fetchAll:(NSError *__autoreleasing *)error { } #endif +#if __IPHONE_8_0 && TARGET_OS_IPHONE + if (self.useOperationPrompt) { + [query setObject:self.useOperationPrompt forKey:(__bridge id)kSecUseOperationPrompt]; + } +#endif + CFTypeRef result = NULL; OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result); if (status != errSecSuccess && error != NULL) { @@ -141,6 +216,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 && TARGET_OS_IPHONE + if (self.useOperationPrompt) { + [query setObject:self.useOperationPrompt forKey:(__bridge id)kSecUseOperationPrompt]; + } +#endif + status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result); if (status != errSecSuccess) { @@ -197,6 +279,19 @@ + (BOOL)isSynchronizationAvailable { } #endif +#pragma mark - Access Control Status + +#ifdef SAMKEYCHAIN_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 diff --git a/Tests/KeychainTests.swift b/Tests/KeychainTests.swift index f51c480..3a3ee08 100644 --- a/Tests/KeychainTests.swift +++ b/Tests/KeychainTests.swift @@ -195,7 +195,7 @@ class KeychainTests: XCTestCase { private func accounts(accounts: [[String: AnyObject]], containsAccountWithName name: String) -> Bool { for account in accounts { - if let acct = account["acct"] as? String where acct == name { + if let acct = account["acct"] as? String, acct == name { return true } } From 90e5e9e3d3e2b344145e736cbfaa18beb905e221 Mon Sep 17 00:00:00 2001 From: Roger Oba Date: Tue, 17 Apr 2018 13:15:17 -0300 Subject: [PATCH 2/8] Extend the support of Access Control creation flags, like biometrics. --- Sources/SAMKeychainAccessControl.h | 49 ++++++++++++++++++++++++++++-- Sources/SAMKeychainAccessControl.m | 37 ++++++---------------- 2 files changed, 56 insertions(+), 30 deletions(-) diff --git a/Sources/SAMKeychainAccessControl.h b/Sources/SAMKeychainAccessControl.h index e9a778a..3c8f73f 100644 --- a/Sources/SAMKeychainAccessControl.h +++ b/Sources/SAMKeychainAccessControl.h @@ -35,9 +35,52 @@ typedef NS_ENUM(NSUInteger, SAMKeychainAccessibility) { /** SecAccessControlCreateFlags */ typedef NS_OPTIONS(NSUInteger, SAMKeychainCreateFlags) { - /** kSecAccessControlUserPresence */ - SAMKeychainCreateFlagUserPresence = 1UL << 0 -}; + /** kSecAccessControlUserPresence + User presence policy using biometry or Passcode. Biometry does not have to be available or enrolled. Item is still + accessible by Touch ID even if fingers are added or removed. Item is still accessible by Face ID if user is re-enrolled. + */ + SAMKeychainCreateFlagUserPresence = 1UL << 0, + /** kSecAccessControlBiometryAny + Constraint: Touch ID (any finger) or Face ID. Touch ID or Face ID must be available. With Touch ID + at least one finger must be enrolled. With Face ID user has to be enrolled. Item is still accessible by Touch ID even + if fingers are added or removed. Item is still accessible by Face ID if user is re-enrolled. + */ + SAMKeychainCreateFlagBiometryAny CF_ENUM_AVAILABLE(10_13_4, 11_3) = 1u << 1, + /** kSecAccessControlTouchIDAny + Deprecated, please use kSecAccessControlBiometryAny instead. + */ + SAMKeychainCreateFlagTouchIDAny API_DEPRECATED_WITH_REPLACEMENT("kSecAccessControlBiometryAny", macos(10.12.1, 10.13.4), ios(9.0, 11.3)) = 1u << 1, + /** kSecAccessControlBiometryCurrentSet + Constraint: Touch ID from the set of currently enrolled fingers. Touch ID must be available and at least one finger must + be enrolled. When fingers are added or removed, the item is invalidated. When Face ID is re-enrolled this item is invalidated. + */ + SAMKeychainCreateFlagBiometryCurrentSet CF_ENUM_AVAILABLE(10_13_4, 11_3) = 1u << 3, + /** kSecAccessControlTouchIDCurrentSet + Deprecated, please use kSecAccessControlBiometryCurrentSet instead. + */ + SAMKeychainCreateFlagTouchIDCurrentSet API_DEPRECATED_WITH_REPLACEMENT("kSecAccessControlBiometryCurrentSet", macos(10.12.1, 10.13.4), ios(9.0, 11.3)) = 1u << 3, + /** kSecAccessControlDevicePasscode + Constraint: Device passcode + */ + SAMKeychainCreateFlagDevicePasscode CF_ENUM_AVAILABLE(10_11, 9_0) = 1u << 4, + /** kSecAccessControlOr + Constraint logic operation: when using more than one constraint, at least one of them must be satisfied. + */ + SAMKeychainCreateFlagOr CF_ENUM_AVAILABLE(10_12_1, 9_0) = 1u << 14, + /** kSecAccessControlAnd + Constraint logic operation: when using more than one constraint, all must be satisfied. + */ + SAMKeychainCreateFlagAnd CF_ENUM_AVAILABLE(10_12_1, 9_0) = 1u << 15, + /** kSecAccessControlPrivateKeyUsage + Create access control for private key operations (i.e. sign operation) + */ + SAMKeychainCreateFlagPrivateKeyUsage CF_ENUM_AVAILABLE(10_12_1, 9_0) = 1u << 30, + /** kSecAccessControlApplicationPassword + Security: Application provided password for data encryption key generation. This is not a constraint but additional item + encryption mechanism. + */ + SAMKeychainCreateFlagApplicationPassword CF_ENUM_AVAILABLE(10_12_1, 9_0) = 1u << 31, +} __OSX_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0); extern CFTypeRef getSecAttrAccessibility(SAMKeychainAccessibility ssAttr); diff --git a/Sources/SAMKeychainAccessControl.m b/Sources/SAMKeychainAccessControl.m index a427946..6ad2ee2 100644 --- a/Sources/SAMKeychainAccessControl.m +++ b/Sources/SAMKeychainAccessControl.m @@ -10,8 +10,7 @@ @implementation SAMKeychainAccessControl -+ (instancetype)accessControlWithAccessibility:(SAMKeychainAccessibility)accesibility flags:(SAMKeychainCreateFlags)flags -{ ++ (instancetype)accessControlWithAccessibility:(SAMKeychainAccessibility)accesibility flags:(SAMKeychainCreateFlags)flags { SAMKeychainAccessControl *accessControl = [self new]; accessControl.accessibility = accesibility; accessControl.flags = flags; @@ -20,31 +19,15 @@ + (instancetype)accessControlWithAccessibility:(SAMKeychainAccessibility)accesib @end -CFTypeRef getSecAttrAccessibility(SAMKeychainAccessibility ssAttr) -{ +CFTypeRef getSecAttrAccessibility(SAMKeychainAccessibility ssAttr) { switch (ssAttr) { - case SAMKeychainAccessibilityAlways: - return kSecAttrAccessibleAlways; - - case SAMKeychainAccessibilityWhenUnlocked: - return kSecAttrAccessibleWhenUnlocked; - - case SAMKeychainAccessibilityAfterFirstUnlock: - return kSecAttrAccessibleAfterFirstUnlock; - - case SAMKeychainAccessibilityAlwaysThisDeviceOnly: - return kSecAttrAccessibleAlwaysThisDeviceOnly; - - case SAMKeychainAccessibilityWhenUnlockedThisDeviceOnly: - return kSecAttrAccessibleWhenUnlockedThisDeviceOnly; - - case SAMKeychainAccessibilityWhenPasscodeSetThisDeviceOnly: - return kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly; - - case SAMKeychainAccessibilityAfterFisrtUnlockThisDeviceOnly: - return kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly; - - default: - return NULL; + case SAMKeychainAccessibilityAlways: return kSecAttrAccessibleAlways; + case SAMKeychainAccessibilityWhenUnlocked: return kSecAttrAccessibleWhenUnlocked; + case SAMKeychainAccessibilityAfterFirstUnlock: return kSecAttrAccessibleAfterFirstUnlock; + case SAMKeychainAccessibilityAlwaysThisDeviceOnly: return kSecAttrAccessibleAlwaysThisDeviceOnly; + case SAMKeychainAccessibilityWhenUnlockedThisDeviceOnly: return kSecAttrAccessibleWhenUnlockedThisDeviceOnly; + case SAMKeychainAccessibilityWhenPasscodeSetThisDeviceOnly: return kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly; + case SAMKeychainAccessibilityAfterFisrtUnlockThisDeviceOnly: return kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly; + default: return NULL; } } From e2ae355074ee23d9a7003e9b6c2274ef7fc8b8b5 Mon Sep 17 00:00:00 2001 From: Roger Oba Date: Tue, 17 Apr 2018 13:15:25 -0300 Subject: [PATCH 3/8] Fix typo. --- Sources/SAMKeychainAccessControl.h | 2 +- Sources/SAMKeychainAccessControl.m | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/SAMKeychainAccessControl.h b/Sources/SAMKeychainAccessControl.h index 3c8f73f..520cdd3 100644 --- a/Sources/SAMKeychainAccessControl.h +++ b/Sources/SAMKeychainAccessControl.h @@ -27,7 +27,7 @@ typedef NS_ENUM(NSUInteger, SAMKeychainAccessibility) { SAMKeychainAccessibilityWhenUnlockedThisDeviceOnly, /** kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly */ - SAMKeychainAccessibilityAfterFisrtUnlockThisDeviceOnly, + SAMKeychainAccessibilityAfterFirstUnlockThisDeviceOnly, /** kSecAttrAccessibleAlwaysThisDeviceOnly */ SAMKeychainAccessibilityAlwaysThisDeviceOnly diff --git a/Sources/SAMKeychainAccessControl.m b/Sources/SAMKeychainAccessControl.m index 6ad2ee2..b894ab9 100644 --- a/Sources/SAMKeychainAccessControl.m +++ b/Sources/SAMKeychainAccessControl.m @@ -27,7 +27,7 @@ CFTypeRef getSecAttrAccessibility(SAMKeychainAccessibility ssAttr) { case SAMKeychainAccessibilityAlwaysThisDeviceOnly: return kSecAttrAccessibleAlwaysThisDeviceOnly; case SAMKeychainAccessibilityWhenUnlockedThisDeviceOnly: return kSecAttrAccessibleWhenUnlockedThisDeviceOnly; case SAMKeychainAccessibilityWhenPasscodeSetThisDeviceOnly: return kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly; - case SAMKeychainAccessibilityAfterFisrtUnlockThisDeviceOnly: return kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly; + case SAMKeychainAccessibilityAfterFirstUnlockThisDeviceOnly: return kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly; default: return NULL; } } From a7caf8144117d2652d7678cebc75e2b32c33eef3 Mon Sep 17 00:00:00 2001 From: Roger Oba Date: Tue, 17 Apr 2018 15:13:03 -0300 Subject: [PATCH 4/8] Remove 2 unnecessary keychain creation flags, simplifying interfaces. --- Sources/SAMKeychainAccessControl.h | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/Sources/SAMKeychainAccessControl.h b/Sources/SAMKeychainAccessControl.h index 520cdd3..2b2bdff 100644 --- a/Sources/SAMKeychainAccessControl.h +++ b/Sources/SAMKeychainAccessControl.h @@ -39,42 +39,41 @@ typedef NS_OPTIONS(NSUInteger, SAMKeychainCreateFlags) { User presence policy using biometry or Passcode. Biometry does not have to be available or enrolled. Item is still accessible by Touch ID even if fingers are added or removed. Item is still accessible by Face ID if user is re-enrolled. */ - SAMKeychainCreateFlagUserPresence = 1UL << 0, + SAMKeychainCreateFlagUserPresence = 1u << 0, + /** kSecAccessControlBiometryAny Constraint: Touch ID (any finger) or Face ID. Touch ID or Face ID must be available. With Touch ID at least one finger must be enrolled. With Face ID user has to be enrolled. Item is still accessible by Touch ID even if fingers are added or removed. Item is still accessible by Face ID if user is re-enrolled. */ - SAMKeychainCreateFlagBiometryAny CF_ENUM_AVAILABLE(10_13_4, 11_3) = 1u << 1, - /** kSecAccessControlTouchIDAny - Deprecated, please use kSecAccessControlBiometryAny instead. - */ - SAMKeychainCreateFlagTouchIDAny API_DEPRECATED_WITH_REPLACEMENT("kSecAccessControlBiometryAny", macos(10.12.1, 10.13.4), ios(9.0, 11.3)) = 1u << 1, + SAMKeychainCreateFlagBiometryAny CF_ENUM_AVAILABLE(10_12_1, 9_0) = 1u << 1, + /** kSecAccessControlBiometryCurrentSet Constraint: Touch ID from the set of currently enrolled fingers. Touch ID must be available and at least one finger must be enrolled. When fingers are added or removed, the item is invalidated. When Face ID is re-enrolled this item is invalidated. */ - SAMKeychainCreateFlagBiometryCurrentSet CF_ENUM_AVAILABLE(10_13_4, 11_3) = 1u << 3, - /** kSecAccessControlTouchIDCurrentSet - Deprecated, please use kSecAccessControlBiometryCurrentSet instead. - */ - SAMKeychainCreateFlagTouchIDCurrentSet API_DEPRECATED_WITH_REPLACEMENT("kSecAccessControlBiometryCurrentSet", macos(10.12.1, 10.13.4), ios(9.0, 11.3)) = 1u << 3, + SAMKeychainCreateFlagBiometryCurrentSet CF_ENUM_AVAILABLE(10_12_1, 9_0) = 1u << 3, + /** kSecAccessControlDevicePasscode Constraint: Device passcode */ SAMKeychainCreateFlagDevicePasscode CF_ENUM_AVAILABLE(10_11, 9_0) = 1u << 4, + /** kSecAccessControlOr Constraint logic operation: when using more than one constraint, at least one of them must be satisfied. */ SAMKeychainCreateFlagOr CF_ENUM_AVAILABLE(10_12_1, 9_0) = 1u << 14, + /** kSecAccessControlAnd Constraint logic operation: when using more than one constraint, all must be satisfied. */ SAMKeychainCreateFlagAnd CF_ENUM_AVAILABLE(10_12_1, 9_0) = 1u << 15, + /** kSecAccessControlPrivateKeyUsage Create access control for private key operations (i.e. sign operation) */ SAMKeychainCreateFlagPrivateKeyUsage CF_ENUM_AVAILABLE(10_12_1, 9_0) = 1u << 30, + /** kSecAccessControlApplicationPassword Security: Application provided password for data encryption key generation. This is not a constraint but additional item encryption mechanism. From 13b4066c33019106e7a9aa3cbecfb33493071637 Mon Sep 17 00:00:00 2001 From: Roger Oba Date: Tue, 17 Apr 2018 15:20:49 -0300 Subject: [PATCH 5/8] Deprecate `useNoAuthenticationUI`. Use `useAuthenticationUI` instead. --- Sources/SAMKeychainQuery.h | 5 ++++- Sources/SAMKeychainQuery.m | 8 +++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Sources/SAMKeychainQuery.h b/Sources/SAMKeychainQuery.h index c478df4..04ae072 100644 --- a/Sources/SAMKeychainQuery.h +++ b/Sources/SAMKeychainQuery.h @@ -71,7 +71,10 @@ typedef NS_ENUM(NSUInteger, SAMKeychainQuerySynchronizationMode) { @property (nonatomic, copy) NSString *useOperationPrompt; /** kSecUseNoAuthenticationUI */ -@property (nonatomic, assign) NSNumber *useNoAuthenticationUI; +@property (nonatomic, assign) NSNumber *useNoAuthenticationUI DEPRECATED_MSG_ATTRIBUTE("Use -useAuthenticationUI instead."); + +/** kSecUseAuthenticationUI */ +@property (nonatomic, assign) NSNumber *useAuthenticationUI; #endif #if SAMKEYCHAIN_ACCESS_CONTROL_AVAILABLE diff --git a/Sources/SAMKeychainQuery.m b/Sources/SAMKeychainQuery.m index 9713d69..fd2d4fb 100644 --- a/Sources/SAMKeychainQuery.m +++ b/Sources/SAMKeychainQuery.m @@ -65,8 +65,14 @@ - (BOOL)save:(NSError *__autoreleasing *)error { #endif #if __IPHONE_8_0 && TARGET_OS_IPHONE +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" if (self.useNoAuthenticationUI) { - [query setObject:self.useNoAuthenticationUI forKey:(__bridge id)kSecUseNoAuthenticationUI]; + self.useAuthenticationUI = @(!self.useNoAuthenticationUI.boolValue); + } +#pragma clang diagnostic pop + if (self.useAuthenticationUI) { + [query setObject:self.useAuthenticationUI forKey:(__bridge id)kSecUseAuthenticationUI]; } #endif #if SAMKEYCHAIN_ACCESS_CONTROL_AVAILABLE From deffb2db32568281d9c45049dd085722cdc69a7c Mon Sep 17 00:00:00 2001 From: Roger Oba Date: Fri, 20 Apr 2018 21:47:20 -0300 Subject: [PATCH 6/8] Add easy verification for device pincode and biometry authentication. --- SAMKeychain.xcodeproj/project.pbxproj | 4 ++++ Sources/SAMKeychainQuery.h | 16 ++++++++++++++++ Sources/SAMKeychainQuery.m | 11 +++++++++++ 3 files changed, 31 insertions(+) diff --git a/SAMKeychain.xcodeproj/project.pbxproj b/SAMKeychain.xcodeproj/project.pbxproj index a10cf20..cc2de11 100644 --- a/SAMKeychain.xcodeproj/project.pbxproj +++ b/SAMKeychain.xcodeproj/project.pbxproj @@ -39,6 +39,7 @@ C598E19B2086415500B79528 /* SAMKeychainAccessControl.m in Sources */ = {isa = PBXBuildFile; fileRef = C598E1952086415500B79528 /* SAMKeychainAccessControl.m */; }; C598E19C2086415500B79528 /* SAMKeychainAccessControl.m in Sources */ = {isa = PBXBuildFile; fileRef = C598E1952086415500B79528 /* SAMKeychainAccessControl.m */; }; C598E19D2086415500B79528 /* SAMKeychainAccessControl.m in Sources */ = {isa = PBXBuildFile; fileRef = C598E1952086415500B79528 /* SAMKeychainAccessControl.m */; }; + C5AF91D4208AC10C00136637 /* LocalAuthentication.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C5AF91D3208AC10C00136637 /* LocalAuthentication.framework */; }; E8A6665B1A844D3A00287CA3 /* SAMKeychain.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E8A666341A844CC400287CA3 /* SAMKeychain.framework */; }; E8A6667A1A844E4100287CA3 /* SAMKeychain.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E8A6666F1A844E4100287CA3 /* SAMKeychain.framework */; }; /* End PBXBuildFile section */ @@ -92,6 +93,7 @@ 21CC42F917DB87C300201DDC /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; C598E1942086415500B79528 /* SAMKeychainAccessControl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SAMKeychainAccessControl.h; sourceTree = ""; }; C598E1952086415500B79528 /* SAMKeychainAccessControl.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SAMKeychainAccessControl.m; sourceTree = ""; }; + C5AF91D3208AC10C00136637 /* LocalAuthentication.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = LocalAuthentication.framework; path = System/Library/Frameworks/LocalAuthentication.framework; sourceTree = SDKROOT; }; E8A666341A844CC400287CA3 /* SAMKeychain.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SAMKeychain.framework; sourceTree = BUILT_PRODUCTS_DIR; }; E8A666551A844D3A00287CA3 /* SAMKeychainTests-iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SAMKeychainTests-iOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; E8A6666F1A844E4100287CA3 /* SAMKeychain.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SAMKeychain.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -125,6 +127,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + C5AF91D4208AC10C00136637 /* LocalAuthentication.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -216,6 +219,7 @@ 21CC42A917DB874300201DDC /* Frameworks */ = { isa = PBXGroup; children = ( + C5AF91D3208AC10C00136637 /* LocalAuthentication.framework */, 21B85B6E18EC963A009D2B98 /* Cocoa.framework */, 21B85B1E18EC9391009D2B98 /* Security.framework */, 21CC42F917DB87C300201DDC /* Security.framework */, diff --git a/Sources/SAMKeychainQuery.h b/Sources/SAMKeychainQuery.h index 04ae072..df3ba50 100644 --- a/Sources/SAMKeychainQuery.h +++ b/Sources/SAMKeychainQuery.h @@ -190,6 +190,22 @@ typedef NS_ENUM(NSUInteger, SAMKeychainQuerySynchronizationMode) { @return A value indicating if keychain access control is available */ + (BOOL)isAccessControlAvailable; + +#if TARGET_OS_IPHONE +/** + Returns a boolean indicating if biometry is configured on the device. + + @return A value indicating if authentication by biometry is configured + */ ++ (BOOL)isBiometricsAuthenticationAvailable; + +/** + Returns a boolean indicating if device passcode or biometry is configured on the device. + + @return A value indicating if device passcode or biometry is configured + */ ++ (BOOL)isPasscodeOrBiometricsAuthenticationAvailable; +#endif #endif @end diff --git a/Sources/SAMKeychainQuery.m b/Sources/SAMKeychainQuery.m index fd2d4fb..6da0447 100644 --- a/Sources/SAMKeychainQuery.m +++ b/Sources/SAMKeychainQuery.m @@ -8,6 +8,7 @@ #import "SAMKeychainQuery.h" #import "SAMKeychain.h" +#import @implementation SAMKeychainQuery @@ -297,6 +298,16 @@ + (BOOL)isAccessControlAvailable { return floor(NSFoundationVersionNumber) > NSFoundationVersionNumber10_9_2; #endif } + +#if TARGET_OS_IPHONE ++ (BOOL)isBiometricsAuthenticationAvailable { + return [[LAContext new] canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:nil]; +} + ++ (BOOL)isPasscodeOrBiometricsAuthenticationAvailable { + return [[LAContext new] canEvaluatePolicy:LAPolicyDeviceOwnerAuthentication error:nil]; +} +#endif #endif #pragma mark - Private From aa3217552bae64c64a4b57223f9b26d2fac92e2d Mon Sep 17 00:00:00 2001 From: Roger Oba Date: Sat, 21 Apr 2018 00:05:11 -0300 Subject: [PATCH 7/8] Rename functions that look up for passcode and biometry availability. --- Sources/SAMKeychainQuery.h | 4 ++-- Sources/SAMKeychainQuery.m | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/SAMKeychainQuery.h b/Sources/SAMKeychainQuery.h index df3ba50..9bf2b39 100644 --- a/Sources/SAMKeychainQuery.h +++ b/Sources/SAMKeychainQuery.h @@ -197,14 +197,14 @@ typedef NS_ENUM(NSUInteger, SAMKeychainQuerySynchronizationMode) { @return A value indicating if authentication by biometry is configured */ -+ (BOOL)isBiometricsAuthenticationAvailable; ++ (BOOL)isBiometryAvailable; /** Returns a boolean indicating if device passcode or biometry is configured on the device. @return A value indicating if device passcode or biometry is configured */ -+ (BOOL)isPasscodeOrBiometricsAuthenticationAvailable; ++ (BOOL)isPasscodeOrBiometryAvailable; #endif #endif diff --git a/Sources/SAMKeychainQuery.m b/Sources/SAMKeychainQuery.m index 6da0447..acc8960 100644 --- a/Sources/SAMKeychainQuery.m +++ b/Sources/SAMKeychainQuery.m @@ -300,11 +300,11 @@ + (BOOL)isAccessControlAvailable { } #if TARGET_OS_IPHONE -+ (BOOL)isBiometricsAuthenticationAvailable { ++ (BOOL)isBiometryAvailable { return [[LAContext new] canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:nil]; } -+ (BOOL)isPasscodeOrBiometricsAuthenticationAvailable { ++ (BOOL)isPasscodeOrBiometryAvailable { return [[LAContext new] canEvaluatePolicy:LAPolicyDeviceOwnerAuthentication error:nil]; } #endif From 8fe5ebf8c66806ef83f284e22421925bfe3205ea Mon Sep 17 00:00:00 2001 From: Roger Oba Date: Sat, 21 Apr 2018 01:51:15 -0300 Subject: [PATCH 8/8] Fix bug setting mutually exclusive properties, causing query failure. --- Sources/SAMKeychainQuery.m | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Sources/SAMKeychainQuery.m b/Sources/SAMKeychainQuery.m index acc8960..b48220e 100644 --- a/Sources/SAMKeychainQuery.m +++ b/Sources/SAMKeychainQuery.m @@ -47,9 +47,15 @@ - (BOOL)save:(NSError *__autoreleasing *)error { [query setObject:self.passwordData forKey:(__bridge id)kSecValueData]; #if __IPHONE_4_0 && TARGET_OS_IPHONE CFTypeRef accessibilityType = [SAMKeychain accessibilityType]; +#if SAMKEYCHAIN_ACCESS_CONTROL_AVAILABLE + if (accessibilityType && !self.accessControl) { // accessibilityType and accessControl are mutually exclusive + [query setObject:(__bridge id)accessibilityType forKey:(__bridge id)kSecAttrAccessible]; + } +#else if (accessibilityType) { [query setObject:(__bridge id)accessibilityType forKey:(__bridge id)kSecAttrAccessible]; } +#endif #endif status = SecItemUpdate((__bridge CFDictionaryRef)(searchQuery), (__bridge CFDictionaryRef)(query)); }else if(status == errSecItemNotFound){//item not found, create it! @@ -60,10 +66,16 @@ - (BOOL)save:(NSError *__autoreleasing *)error { [query setObject:self.passwordData forKey:(__bridge id)kSecValueData]; #if __IPHONE_4_0 && TARGET_OS_IPHONE CFTypeRef accessibilityType = [SAMKeychain accessibilityType]; +#if SAMKEYCHAIN_ACCESS_CONTROL_AVAILABLE + if (accessibilityType && !self.accessControl) { // accessibilityType and accessControl are mutually exclusive + [query setObject:(__bridge id)accessibilityType forKey:(__bridge id)kSecAttrAccessible]; + } +#else if (accessibilityType) { [query setObject:(__bridge id)accessibilityType forKey:(__bridge id)kSecAttrAccessible]; } #endif +#endif #if __IPHONE_8_0 && TARGET_OS_IPHONE #pragma clang diagnostic push