Logo
RSS Feed

iOS Keychain

Created: 02.10.2020

In this article I’m trying to study how keychain works.

Metaphor

There once lived a monkey 🐒 George. He was a nice fellow, but his memory suck too much and caused him a lot of trouble. He also was very absent-minded and has lost some of his secret keys. He met a bird 🐔 Marvin and said: “Marvin, my memory is no good, here are all my keys 🔑🔑🔑🔑 , I will just retain this small key-card 🎴 which I will show you to prove it’s really me. Whenever I need a key, I’ll show you this card. You give me the key 🔑 temporary and then take it back after I used it 🔐 .”. Marvin was a very responsible guy and he agreed.

securityd daemon is working hard.

It’s like Marvin 🐔 : deciding who is entitled to get what 🔑. It requires at least two out of three access cards to decide: keychain-access-groups🎫 , application-identifier 🎴, and application-group🗂️.

Below is the Apple’s diagram:

ios-keychain-api

kSecAttrAccessible

Keychain is a database which is accessed via special API. Upon item creation you decide when this item is going to be used by your application via kSecAttrAccessible attribute.

var query: [String: Any] = [kSecClass as String: kSecClassInternetPassword,
                            kSecAttrAccount as String: account,
                            kSecAttrServer as String: server,
                            kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlocked,
                            kSecValueData as String: password]

There are several possible options. I would devide them in two main groups: backed up and not backed up. They distinguish by the ThisDeviceOnly at the end of the name of an attribute. These are not backed up since they don’t have ThisDeviceOnly postfix:

  1. [deprecated since iOS 12]kSecAttrAccessibleAlways 👐 - data can be accessed even when the device is locked.
  2. kSecAttrAccessibleAfterFirstUnlock 🔓 - After the first unlock, the data remains accessible until the next restart. Useful for background processes.
  3. kSecAttrAccessibleWhenUnlocked 🔐- data can be accessed only while unlocked.

These are backed up, since they have ThisDeviceOnly postfix. They are pretty much the same, but they do not migrate to a new device since they are not backed up thus after restoring from a backup 💼 of a different device, these items will not be present:

  1. [deprecated since iOS 12] kSecAttrAccessibleAlwaysThisDeviceOnly 👐 💼

  2. kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly - 🔓💼

  3. kSecAttrAccessibleWhenUnlockedThisDeviceOnly - 🔐💼

  4. kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly - same as kSecAttrAccessibleWhenUnlockedThisDeviceOnly (previous) but available only if a passcode is set on the device. ❗ Keep in mind, that key will be deleted when the device passcode has been removed. ❓ Private key is stored in SecureEnclave 🛡️

AccessControlFlags

Appart from mode of operation, developers define how this item must be accessed. 🛡️ indicated that when using this attribute it’s stored in Secure Enclave therefore the data itself is not stored in Keychain, only token is present there:

  1. kSecAccessControlDevicePasscode - access fith a passcode.
  2. kSecAccessControlBiometryAny 🛡️ - access with a fingerprint. If you adding or removing a fingerprint, the item still can be accessed.
  3. kSecAccessControlBiometryCurrentSet 🛡️- same, but adding or removing a fingerprint will deem the item unaccessible.
  4. kSecAccessControlUserPresence - either passcode or a fingerprint.

Security concerns

Keychain is retained when application is uninstalled. So, it’s generally a bad idea to store something very valuable there.

As far as I understand, you can’t just store any rubbish in Secure Enclave. It supports only 256-bit elliptic curve private keys.

https://developer.apple.com/documentation/security/ksecattrtokenidsecureenclave

The only keychain items supported by the Secure Enclave are 256-bit elliptic curve private keys (those that have key type kSecAttrKeyTypeEC). Such keys must be generated directly on the Secure Enclave using the SecKeyGeneratePair(_:_:_:) function with the kSecAttrTokenID key set to kSecAttrTokenIDSecureEnclave in the parameters dictionary.

Important

It is not possible to import pre-existing keys into the Secure Enclave.

So, same as with Apple in general, in that not even Apple knows your super-duper secret device key and therefore can’t decrypt the data without a passcode, the same is here: not even the application itself know the key, it only asks to generate one and then subsequently asks to decrypt something or sign.

Generating keys example:

// private key parameters
let privateKeyParams = [
    kSecAttrLabel as String: "privateLabel",
    kSecAttrIsPermanent as String: true, 
    kSecAttrApplicationTag as String: "applicationTag",
] as CFDictionary

// public key parameters
let publicKeyParams = [
    kSecAttrLabel as String: "publicLabel",
    kSecAttrIsPermanent as String: false,
    kSecAttrApplicationTag as String: "applicationTag",
] as CFDictionary

// global parameters
let parameters = [
    kSecAttrKeyType as String: kSecAttrKeyTypeEC,
    kSecAttrKeySizeInBits as String: 256,
    kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave,
    kSecPublicKeyAttrs as String: publicKeyParams,
    kSecPrivateKeyAttrs as String: privateKeyParams,
] as CFDictionary

var pubKey, privKey: SecKey?
let status = SecKeyGeneratePair(parameters, &pubKey, &privKey)

if status != errSecSuccess {
    // Keys created successfully
}

From the presentation of David Linder “Don’t Touch Me That Way” - ios-keychain-david-linder

ios-keychain-david-linder-2

kSecAccessControlUserPresence

kSecAccessControlApplicationPassword

kSecAccessControlDevicePasscode

kSecAccessControlPrivateKeyUsage

kSecAccessControlTouchIDAny

kSecAccessControlTouchIDCurrentSet

Sharing KeyChain

Sandboxing on iOS is accomplished via entitlements, but this sandboxing environment can be loosen a bit. The same developer can share KeyChain items and Shared Preferences with other applications. This does violate “sandboxing”, but this allows different applications share information, including credentials. As usual, there is always balancing between usability and security.

TouchID and Secure Enclave

💡 Good artifact is Keychain. Dump it with ./keychain_dumper and you can see saved WiFi passwords.