Biometry protection in iOS

Dmitrijs Beloborodovs
Citadele Bank Developers
4 min readSep 25, 2020
Photo by George Prentzas on Unsplash

Biometrics are body measurements and calculations related to human characteristics. Biometrics authentication (or realistic authentication) is used in computer science as a form of identification and access control. It is also used to identify individuals in groups that are under surveillance.

You might not even know this term yet, there is a huge science behind it which saves you from entering 6 digits number every time to check an email, yet which you have to change every 3 month and not repeat last 3 combinations… Most likely you know it as Touch ID / Face ID.

Simple Touch ID

It is very simple to use Touch ID API in iOS projects.

  • import LocalAuthentication
  • check it Touch ID enabled on device
  • unlock secret with Touch ID

LocalAuthentication is a framework providing simple API for Touch ID.

import LocalAuthentication

It is very important to check if Touch ID is enabled and configured on device and provide valid feedback if not to user. There are plenty of reasons why it might fail:

  • user has not enabled Touch ID
  • it is enabled, yet no fingers added
  • it is enabled, at least one finger added, yet user failed to auth with it and it’s temporary blocked until user auth with code
let context = LAContext()
var error: NSError?
if context
.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
// proceed with revealing secret} else { // fail gracefully}

Although it is vital to check code on real device, Simulator also provide availability to check Touch ID logic. Just enable it in Simulator’s menu Features -> Touch ID -> Enrolled

Both matching and non-matching scenarios available.

Simple Face ID

Same information applies to Face ID. But! And it’s very important to avoid crash, a NSFaceIDUsageDescription key should be added to Info.plist for every target in projects.

<key>NSFaceIDUsageDescription</key>
<string>$(PRODUCT_NAME) wants to use Face ID</string>

Touch ID math

Although I was not able to find Apple’s official documentation, it is known, you may add up to 5 fingers to use with Touch ID. Yet those fingers may belong to different persons. And there is also this trick (bug???) that allows you add multiple (we were able once add 6!!!) different fingerprints per one “finger”, check video.

Working with local authentication context is really simple, yet not cover all use cases of real life. And here we come to…

Biometry and Keychain

Working with a Keychain was never easy on iOS. I believe it has great legacy from time when people were coding Fortran and dinosaurs were walking down the street. To simplify, think of Keychain as set of records with different attributes. We specifically interested access attributes, because it is where we specify we want “lock” record with biometry.

Adding an item is easy as executing a query

let status = SecItemAdd(query as CFDictionary, nil)guard status == errSecSuccess else { return }

Let’s prepare this query:

let secretData = "top secret".data(using: .utf8)let query: [String: Any] =[kSecClass as String: kSecClassGenericPassword,kSecAttrAccessControl as String: access as Any,kSecValueData as String: secretData]

Minimum information required is “class of item”, we use kSecClassGenericPassword. Specifying item to store as Data. And providing access control level kSecAttrAccessControl. Which we prepare as

let access =SecAccessControlCreateWithFlags(nil,kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,.biometryCurrentSet,nil)

Ignore first and fourth parameters for now, set nil. Second one, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, means we want to store item in Keychain only when device is protected with passcode. And keep this record on this device only: no backup/restore/transfer or any iCloud sync. This is the most paranoiac, but other options available. The third parameter, .biometryCurrentSet, is one of many available flags. This one means unlock item only with current set of fingers (or face) configured. Once user add/delete finger or reconfigure face, item is no longer available. Again, this is paranoiac, yet prevents from attack when hacker knows device password and add his fingerprint or face.

Reading stored item is easy enough

var data: CFTypeRef?let status = SecItemCopyMatching(query as CFDictionary, &data)guard status == errSecSuccess else { return }guard let secretData = data as? Data,let secret = String(data: secretData, encoding: .utf8) else { return }

Again, we form a query, then read Data and transform into string. The query may be as simple as

let prompt = "Auth with biometry"let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword,kSecReturnData as String: true,kSecUseOperationPrompt as String: prompt]

Worth mentioning that API provides a prompt to show user when biometry ID system dialog pop up. Yet it is not showing for Face ID.

Working with Keychain status code is another challenge. Here is a good resource to inspect code https://www.osstatus.com/ as code is the only output you get once problem occures.

--

--