Sunday, September 14, 2014

TouchID and Keychain, iOS8 best friends

Last blog, we saw how to store sensitive data in iOS keychain and the different problems when switching from passcode on to passcode off set. Default access to keychain WhenUnlocked does not prevent access to keychain items when the device is not protected by passcode any more. In iOS8 with the new attribute kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, when trying to access keychain items, we end up with an error message if the device configuration set to passcode off. In this post, we'll go a step further in securing access to our sensitive data every times you access them. Welcome to TouchId.

TouchID: What is it?

It's a fingerprint recognition feature and is only available on the iPhone 5S and plus. Fingerprint data is stored on the secure enclave of the Apple A7 processor that is inside the device itself. To read more the existing and mysterious topic of secure enclave check apple security paper here.

If the user's phone has been rebooted, or has not been unlocked for 48 hours, only the user's passcode, not a fingerprint, can be used to unlock the phone.

Since iOS8, you can programatically use touchID APIs to:
  • either do local authentication,
  • or store in Keychain
  • Let's revisit our previous example hosted on github. We had the option to add an item in Keychain, update and read it. But we want to make sure every times the app reads the item the user is required to authenticate via touchID.

    Keychain Access with TouchID

    The existing ACL attributes on keychain:
  • kSecAttrAccessGroup: is used for WHAT are the apps which can access it. If you want the new keychain item to be shared among multiple applications, include the kSecAttrAccessGroup key
  • kSecAttrAccessible: expresses WHEN the user can access it. Among the different options: WhenUnlocked and the newbie WhenPascodeSet.
  • kSecAttrAccessControl: is for GRANTing. This is a new iOS8 attribute. It allows you to define a fine-grained access control. You use it with the method SecAccessControlCreateWithFlags. For now the enum contains only one value (but there will be room for more configuration) "UserPresence".
  • Let's code it, we're going to replace the generic createQuery by a more specific createQueryForAddItemWithTouchID for adding an item and createQueryForReadItemWithTouchID for retrieving it:
    func createQueryForAddItemWithTouchID(# key: String, value: String? = nil) -> NSMutableDictionary {
            var dataFromString: NSData? = value?.dataUsingEncoding(NSUTF8StringEncoding)
            var error:  Unmanaged?
            var sac: Unmanaged
            sac = SecAccessControlCreateWithFlags(kCFAllocatorDefault,
                        kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, .UserPresence, &error)
            let retrievedData = Unmanaged.fromOpaque(sac.toOpaque()).takeUnretainedValue()
            
            var keychainQuery = NSMutableDictionary()
            keychainQuery[kSecClass] = kSecClassGenericPassword
            keychainQuery[kSecAttrService] = self.serviceIdentifier
            keychainQuery[kSecAttrAccount] = key
            keychainQuery[kSecAttrAccessControl] = retrievedData
            keychainQuery[kSecUseNoAuthenticationUI] = true
            if let unwrapped = dataFromString {
                keychainQuery[kSecValueData] = unwrapped
            }
            return keychainQuery
        }
    
    Line 4, we create an AccessControl object with the policy set to kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly. Note the flag item is set to the only possible UserPresence. we set the attribute kSecUseNoAuthenticationUI because we don't want to be prompted on Add.

    Read Keychain with TouchID popping up

    For the read, we only need to customize the pop-up window content with the attribute kSecUseOperationPrompt.
    func createQueryForReadItemWithTouchID(# key: String, value: String? = nil) -> NSMutableDictionary {
            var dataFromString: NSData? = value?.dataUsingEncoding(NSUTF8StringEncoding)
    
            
            var keychainQuery = NSMutableDictionary()
            keychainQuery[kSecClass] = kSecClassGenericPassword
            keychainQuery[kSecAttrService] = self.serviceIdentifier
            keychainQuery[kSecAttrAccount] = key
    
            keychainQuery[kSecUseOperationPrompt] = "Do you really want to access the item?"
            keychainQuery[kSecReturnData] = true
            
            return keychainQuery
        }
    
    Things to remember

    Accessibility and AccessControl work together. One key implication of using TouchID and Keychain is: the user has to authenticate using standard UI, therefore the app must be in foreground. Be aware that broad queries on Keychain may request items that need user auth. Last but not least, the keychains items stored using kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly are not synchronised or back-up on iCloud.

    That's all for today, next blog post we can see how to use TouchID for LocalAuthentication and how to fallback when touchID is not available. Stay tuned!

    Happy iOS8, Happy Swift!

    No comments:

    Post a Comment

    Note: Only a member of this blog may post a comment.