Forráskód Böngészése

Merge pull request #451 from kishikawakatsumi/Marcocanc-contains-check

Marcocanc contains check
Kishikawa Katsumi 5 éve
szülő
commit
b3497d92cd
2 módosított fájl, 111 hozzáadás és 52 törlés
  1. 95 40
      Lib/KeychainAccess/Keychain.swift
  2. 16 12
      README.md

+ 95 - 40
Lib/KeychainAccess/Keychain.swift

@@ -157,44 +157,77 @@ public struct AuthenticationPolicy: OptionSet {
      have to be available or enrolled. Item is still accessible by Touch ID
      even if fingers are added or removed.
      */
-    @available(iOS 8.0, OSX 10.10, *)
-    @available(watchOS, unavailable)
+    @available(iOS 8.0, *)
+    @available(OSX 10.10, *)
+    @available(watchOS 2.0, *)
+    @available(tvOS 8.0, *)
     public static let userPresence = AuthenticationPolicy(rawValue: 1 << 0)
 
     /**
-     Constraint: Touch ID (any finger). Touch ID must be available and
-     at least one finger must be enrolled. Item is still accessible by
-     Touch ID even if fingers are added or removed.
+     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.
      */
-    @available(iOS 9.0, *)
-    @available(OSX, unavailable)
-    @available(watchOS, unavailable)
+    @available(iOS 11.3, *)
+    @available(OSX 10.13.4, *)
+    @available(watchOS 4.3, *)
+    @available(tvOS 11.3, *)
+    public static let biometryAny = AuthenticationPolicy(rawValue: 1 << 1)
+
+    /**
+     Deprecated, please use biometryAny instead.
+     */
+    @available(iOS, introduced: 9.0, deprecated: 11.3, renamed: "biometryAny")
+    @available(OSX, introduced: 10.12.1, deprecated: 10.13.4, renamed: "biometryAny")
+    @available(watchOS, introduced: 2.0, deprecated: 4.3, renamed: "biometryAny")
+    @available(tvOS, introduced: 9.0, deprecated: 11.3, renamed: "biometryAny")
     public static let touchIDAny = AuthenticationPolicy(rawValue: 1 << 1)
 
     /**
-     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.
+     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.
      */
-    @available(iOS 9.0, *)
-    @available(OSX, unavailable)
-    @available(watchOS, unavailable)
+    @available(iOS 11.3, *)
+    @available(OSX 10.13.4, *)
+    @available(watchOS 4.3, *)
+    @available(tvOS 11.3, *)
+    public static let biometryCurrentSet = AuthenticationPolicy(rawValue: 1 << 3)
+
+    /**
+     Deprecated, please use biometryCurrentSet instead.
+     */
+    @available(iOS, introduced: 9.0, deprecated: 11.3, renamed: "biometryCurrentSet")
+    @available(OSX, introduced: 10.12.1, deprecated: 10.13.4, renamed: "biometryCurrentSet")
+    @available(watchOS, introduced: 2.0, deprecated: 4.3, renamed: "biometryCurrentSet")
+    @available(tvOS, introduced: 9.0, deprecated: 11.3, renamed: "biometryCurrentSet")
     public static let touchIDCurrentSet = AuthenticationPolicy(rawValue: 1 << 3)
 
     /**
      Constraint: Device passcode
      */
-    @available(iOS 9.0, OSX 10.11, *)
-    @available(watchOS, unavailable)
+    @available(iOS 9.0, *)
+    @available(OSX 10.11, *)
+    @available(watchOS 2.0, *)
+    @available(tvOS 9.0, *)
     public static let devicePasscode = AuthenticationPolicy(rawValue: 1 << 4)
 
+    /**
+     Constraint: Watch
+     */
+    @available(iOS, unavailable)
+    @available(OSX 10.15, *)
+    @available(watchOS, unavailable)
+    @available(tvOS, unavailable)
+    public static let watch = AuthenticationPolicy(rawValue: 1 << 5)
+
     /**
      Constraint logic operation: when using more than one constraint,
      at least one of them must be satisfied.
      */
     @available(iOS 9.0, *)
-    @available(OSX, unavailable)
-    @available(watchOS, unavailable)
+    @available(OSX 10.12.1, *)
+    @available(watchOS 2.0, *)
+    @available(tvOS 9.0, *)
     public static let or = AuthenticationPolicy(rawValue: 1 << 14)
 
     /**
@@ -202,16 +235,18 @@ public struct AuthenticationPolicy: OptionSet {
      all must be satisfied.
      */
     @available(iOS 9.0, *)
-    @available(OSX, unavailable)
-    @available(watchOS, unavailable)
+    @available(OSX 10.12.1, *)
+    @available(watchOS 2.0, *)
+    @available(tvOS 9.0, *)
     public static let and = AuthenticationPolicy(rawValue: 1 << 15)
 
     /**
      Create access control for private key operations (i.e. sign operation)
      */
     @available(iOS 9.0, *)
-    @available(OSX, unavailable)
-    @available(watchOS, unavailable)
+    @available(OSX 10.12.1, *)
+    @available(watchOS 2.0, *)
+    @available(tvOS 9.0, *)
     public static let privateKeyUsage = AuthenticationPolicy(rawValue: 1 << 30)
 
     /**
@@ -219,8 +254,9 @@ public struct AuthenticationPolicy: OptionSet {
      This is not a constraint but additional item encryption mechanism.
      */
     @available(iOS 9.0, *)
-    @available(OSX, unavailable)
-    @available(watchOS, unavailable)
+    @available(OSX 10.12.1, *)
+    @available(watchOS 2.0, *)
+    @available(tvOS 9.0, *)
     public static let applicationPassword = AuthenticationPolicy(rawValue: 1 << 31)
 
     #if swift(>=2.3)
@@ -742,14 +778,35 @@ public final class Keychain {
 
     // MARK:
 
-    public func contains(_ key: String) throws -> Bool {
+    public func contains(_ key: String, withoutAuthenticationUI: Bool = false) throws -> Bool {
         var query = options.query()
         query[AttributeAccount] = key
 
+        if withoutAuthenticationUI {
+            #if os(iOS) || os(watchOS) || os(tvOS)
+            if #available(iOS 9.0, *) {
+                query[UseAuthenticationUI] = UseAuthenticationUIFail
+            } else {
+                query[UseNoAuthenticationUI] = kCFBooleanTrue
+            }
+            #else
+            if #available(OSX 10.11, *) {
+                query[UseAuthenticationUI] = UseAuthenticationUIFail
+            } else if #available(OSX 10.10, *) {
+                query[UseNoAuthenticationUI] = kCFBooleanTrue
+            }
+            #endif
+        }
+        
         let status = SecItemCopyMatching(query as CFDictionary, nil)
         switch status {
         case errSecSuccess:
-            return true
+                return true
+        case errSecInteractionNotAllowed:
+            if withoutAuthenticationUI {
+                return true
+            }
+            return false
         case errSecItemNotFound:
             return false
         default:
@@ -1060,7 +1117,9 @@ public final class Keychain {
     @discardableResult
     fileprivate class func securityError(status: OSStatus) -> Error {
         let error = Status(status: status)
-        print("OSStatus error:[\(error.errorCode)] \(error.description)")
+        if error != .userCanceled {
+            print("OSStatus error:[\(error.errorCode)] \(error.description)")
+        }
 
         return error
     }
@@ -1144,32 +1203,28 @@ private let ValueRef = String(kSecValueRef)
 private let ValuePersistentRef = String(kSecValuePersistentRef)
 
 /** Other Constants */
-@available(iOS 8.0, OSX 10.10, *)
+@available(iOS 8.0, OSX 10.10, tvOS 8.0, *)
 private let UseOperationPrompt = String(kSecUseOperationPrompt)
 
-#if os(iOS)
 @available(iOS, introduced: 8.0, deprecated: 9.0, message: "Use a UseAuthenticationUI instead.")
+@available(OSX, introduced: 10.10, deprecated: 10.11, message: "Use UseAuthenticationUI instead.")
+@available(watchOS, introduced: 2.0, deprecated: 2.0, message: "Use UseAuthenticationUI instead.")
+@available(tvOS, introduced: 8.0, deprecated: 9.0, message: "Use UseAuthenticationUI instead.")
 private let UseNoAuthenticationUI = String(kSecUseNoAuthenticationUI)
-#endif
 
-@available(iOS 9.0, OSX 10.11, *)
-@available(watchOS, unavailable)
+@available(iOS 9.0, OSX 10.11, watchOS 2.0, tvOS 9.0, *)
 private let UseAuthenticationUI = String(kSecUseAuthenticationUI)
 
-@available(iOS 9.0, OSX 10.11, *)
-@available(watchOS, unavailable)
+@available(iOS 9.0, OSX 10.11, watchOS 2.0, tvOS 9.0, *)
 private let UseAuthenticationContext = String(kSecUseAuthenticationContext)
 
-@available(iOS 9.0, OSX 10.11, *)
-@available(watchOS, unavailable)
+@available(iOS 9.0, OSX 10.11, watchOS 2.0, tvOS 9.0, *)
 private let UseAuthenticationUIAllow = String(kSecUseAuthenticationUIAllow)
 
-@available(iOS 9.0, OSX 10.11, *)
-@available(watchOS, unavailable)
+@available(iOS 9.0, OSX 10.11, watchOS 2.0, tvOS 9.0, *)
 private let UseAuthenticationUIFail = String(kSecUseAuthenticationUIFail)
 
-@available(iOS 9.0, OSX 10.11, *)
-@available(watchOS, unavailable)
+@available(iOS 9.0, OSX 10.11, watchOS 2.0, tvOS 9.0, *)
 private let UseAuthenticationUISkip = String(kSecUseAuthenticationUISkip)
 
 #if os(iOS)

+ 16 - 12
README.md

@@ -197,9 +197,9 @@ let creationDate = keychain[attributes: "kishikawakatsumi"]?.creationDate
 let keychain = Keychain()
 do {
     let attributes = try keychain.get("kishikawakatsumi") { $0 }
-    print(attributes.comment)
-    print(attributes.label)
-    print(attributes.creator)
+    print(attributes?.comment)
+    print(attributes?.label)
+    print(attributes?.creator)
     ...
 } catch let error {
     print("error: \(error)")
@@ -210,10 +210,11 @@ do {
 
 ```swift
 let keychain = Keychain()
-let attributes = keychain[attributes: "kishikawakatsumi"]
-print(attributes.comment)
-print(attributes.label)
-print(attributes.creator)
+if let attributes = keychain[attributes: "kishikawakatsumi"] {
+    print(attributes.comment)
+    print(attributes.label)
+    print(attributes.creator)
+}
 ```
 
 ### :key: Configuration (Accessibility, Sharing, iCloud Sync)
@@ -316,12 +317,15 @@ do {
 }
 ```
 
-### <a name="touch_id_integration"> :fu: Touch ID integration
+### <a name="touch_id_integration"> :fu: Touch ID (Face ID) integration
 
 **Any Operation that require authentication must be run in the background thread.**  
 **If you run in the main thread, UI thread will lock for the system to try to display the authentication dialog.**
 
-#### :closed_lock_with_key: Adding a Touch ID protected item
+
+**To use Face ID, add `NSFaceIDUsageDescription` key to your `Info.plist`**
+
+#### :closed_lock_with_key: Adding a Touch ID (Face ID) protected item
 
 If you want to store the Touch ID protected Keychain item, specify `accessibility` and `authenticationPolicy` attributes.  
 
@@ -340,7 +344,7 @@ DispatchQueue.global().async {
 }
 ```
 
-#### :closed_lock_with_key: Updating a Touch ID protected item
+#### :closed_lock_with_key: Updating a Touch ID (Face ID) protected item
 
 The same way as when adding.  
 
@@ -366,7 +370,7 @@ DispatchQueue.global().async {
 }
 ```
 
-#### :closed_lock_with_key: Obtaining a Touch ID protected item
+#### :closed_lock_with_key: Obtaining a Touch ID (Face ID) protected item
 
 The same way as when you get a normal item. It will be displayed automatically Touch ID or passcode authentication If the item you try to get is protected.  
 If you want to show custom authentication prompt message, specify an `authenticationPrompt` attribute.
@@ -388,7 +392,7 @@ DispatchQueue.global().async {
 }
 ```
 
-#### :closed_lock_with_key: Removing a Touch ID protected item
+#### :closed_lock_with_key: Removing a Touch ID (Face ID) protected item
 
 The same way as when you remove a normal item.
 There is no way to show Touch ID or passcode authentication when removing Keychain items.