Illustration of a person sitting cross-legged with a mobile phone, next to a large mobile screen displaying a sign-in form. The text reads 'Securely Managing User Details in the Keychain on iOS,' with the 200OK Solutions logo and website URL at the bottom

Securely Managing User Details in the Keychain on iOS: Part 3 

Share this post on:

In the previous blog, we explored how to save, delete, update, and retrieve values in the Keychain. Now, we’ll demonstrate another example, focusing on the SecAccessControl API within the Security framework. 

Key Roles of SecAccessControl: 

  1. Define Access Conditions: 
  • Allows developers to set conditions like passcode, biometric authentication, or device unlock for accessing Keychain items. 
  • Supports requirements such as user presence, passcode entry, or biometrics like Face ID or Touch ID. 
  1. Enhance Data Security: 
  • Ensures data security by requiring user authentication before access, protecting sensitive information even if the device is compromised. 
  1. Customizable Access Control: 
  • Offers various flags to customize security levels based on data sensitivity, such as restricting access only when the device is unlocked or the correct biometric is provided. 
  1. Protection Against Unauthorized Access: 
  • Adds a layer of protection by controlling how and when data can be accessed, reducing the risk of unauthorized access. 

Common Use Cases: 

  • Storing Passwords: 
  • Requires biometrics or passcode before accessing stored passwords, ensuring security even if the device is stolen. 
  • Encrypting Data: 
  • Stores encryption keys with conditions that prevent use unless the user is authenticated. 
  • Restricting Access to Sensitive Information: 
  • Limits access based on user login status, device unlock state, or other security conditions. 

Here’s a brief overview of how to use SecAccessControlCreateWithFlags in Swift: 

Function Declaration 

    func SecAccessControlCreateWithFlags( 

        _ allocator: CFAllocator?, 

        _ protection: SecAccessControlCreateFlags, 

        _ flags: SecAccessControlCreateFlags, 

        _ accessControl: UnsafeMutablePointer<SecAccessControl?>? 

    ) -> OSStatus 

Parameters 

  • allocator: A Core Foundation allocator to use to allocate the SecAccessControl object. Pass nil to use the default allocator. 
  • protection: Specifies the type of protection the access control should provide. This is usually a combination of SecAccessControlCreateFlags values. 
  • flags: A bitmask of SecAccessControlCreateFlags that specifies the type of access control you want. 
  • accessControl: A pointer to a SecAccessControl object that will be created. This will be set by the function. 

Example Usage 

Here’s a simple example of creating a SecAccessControl object with specific protection and flags: 

func createAccessControl() -> SecAccessControl? { 

        var error: Unmanaged<CFError>? // This will capture the error, if any 

        // Define the protection level and flags 

        let flag: SecAccessControlCreateFlags = [.userPresence] 

        let protection:AnyObject!             = kSecAttrAccessibleWhenUnlocked 

        // Create the SecAccessControl object 

        if let accessControl = SecAccessControlCreateWithFlags( 

            nil,                            // Default allocator 

            protection,                      // Protection level (bitmask of flags) 

            flag,                      // Additional flags 

            &error                            // Error pointer 

        ) { 

            // Successfully created SecAccessControl object 

            print("SecAccessControl object created successfully") 

            return accessControl 

        } else { 

            // Handle error 

            if let error = error { 

                let errorDescription = CFErrorCopyDescription(error.takeRetainedValue()) as String 

                print("Error creating SecAccessControl object: \(errorDescription)") 

            } else { 

                print("Unknown error occurred") 

            } 

            return nil 

        } 

    }

Key Components: 

  • Define Security Flags: 
  • let flag: SecAccessControlCreateFlags = [.userPresence]: Specifies that user presence (e.g., biometric authentication or passcode entry) is required to access the Keychain item. 
  • let protection: AnyObject! = kSecAttrAccessibleWhenUnlocked: Defines the accessibility level, meaning the item can be accessed only when the device is unlocked. 
  • Create SecAccessControl Object: 
  • SecAccessControlCreateWithFlags: This function attempts to create a SecAccessControl object with the specified protection level and flags. It uses the default memory allocator (nil) and passes the error pointer to capture any issues. 

The function attempts to create a security policy (SecAccessControl) for accessing a Keychain item, ensuring that user presence is required. If successful, it returns the created object; otherwise, it handles and logs the error. 

func saveUserDetails(username: String, password: String) -> Bool { 

        // Create the access control object 

            guard let accessControl = createAccessControl() else { 

                print("Failed to create access control") 

                return false 

            } 

        let userDetails: [String: Any] = [ 

               kSecClass as String: kSecClassGenericPassword, 

               kSecAttrAccount as String: username, 

               kSecAttrService as String: "com.example.myapp", 

               kSecValueData as String: password.data(using: .utf8)!, 

               kSecAttrAccessControl as String: accessControl 

           ] 

        let addStatus = SecItemAdd(userDetails as CFDictionary, nil) 

        if addStatus == errSecSuccess { 

            print("User details saved successfully") 

            return true; 

        } else if addStatus == errSecDuplicateItem { 

            print("Item already exists") 

        } else { 

            print("Error saving user details: \(addStatus)") 

        } 

        return false; 

    }

 

Key Points:

  1. kSecClass
  • Always specify kSecClass to define the item type (e.g., kSecClassGenericPassword). 
  1. kSecAttrAccessControl
  • Ensure that createAccessControl() returns a valid SecAccessControl object. If it returns nil, the keychain operation will fail. 
  1. Error Handling
  • Handle the case where createAccessControl() might fail and return nil, providing appropriate feedback. 

Below function searches the Keychain for a password associated with the given username and returns it if found; otherwise, it returns nil. 

func fetchUserDetails(username: String) -> String? { 

        let query: [String: Any] = [ 

            kSecClass as String: kSecClassGenericPassword, 

            kSecAttrAccount as String: username, 

            kSecAttrService as String: "com.example.myapp", 

            kSecReturnData as String: kCFBooleanTrue!, 

            kSecMatchLimit as String: kSecMatchLimitOne, 

        ] 

        var item: CFTypeRef? 

        let status = SecItemCopyMatching(query as CFDictionary, &item) 

        if status == errSecSuccess { 

            if let passwordData = item as? Data, 

               let password = String(data: passwordData, encoding: .utf8) { 

                return password 

            } 

        } else { 

            print("Error fetching user details: \(status)") 

        } 

        return nil 

    }

You can find similar examples on GitHub that demonstrate how to store user details using SecAccessControl. Check out this repository: KeychainWrapper

Conclusion 

SecAccessControl is a crucial component of the iOS and macOS security framework that enhances the protection of sensitive data stored in the Keychain. It allows developers to set specific conditions that must be met to access Keychain items, such as requiring biometric authentication (Face ID, Touch ID) or device passcode entry. 

Reference Links 

  1. Apple Developer Documentation – SecAccessControl: 
  1. Apple Developer Documentation – Keychain Services: 

Related Posts:

Securely Managing User Details in the Keychain on iOS: Part I

Secure User Details in the Keychain on iOS