Hello. We have encountered a failure that we haven't seen before regarding use of Secure Enclave private keys and creating cryptographic signatures. We've used this code on thousands of iOS devices (from iOS 11.2 to iOS 14.6) without issue, and recently saw an error that we were not able to find documentation for. We are hoping to find out more details about the failure so that we can avoid it in the future.
Steps to Reproduce
- On an iPhone 11 Pro running iOS 14.4.2, generate a private key in the Secure Enclave via
SecKeyCreateRandomKey()
with the following parameters.
[
kSecAttrTokenID: kSecAttrTokenIDSecureEnclave,
kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom,
kSecAttrKeySizeInBits: 256,
kSecPrivateKeyAttrs: [
kSecAttrAccessControl: SecAccessControlCreateWithFlags(
kCFAllocatorDefault,
kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
[.touchIDAny, .privateKeyUsage],
nil
)!,
kSecAttrIsPermanent: true
],
kSecAttrApplicationLabel: "unique label" // a customer identifier
]
(Note that app is using deployment target of iOS 11.2, thus the use of .touchIDAny
).
- Fetch the aformentioned key with
SecItemCopyMatching(…)
with the following parameters:
[
kSecClass: kSecClassKey,
kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom,
kSecAttrKeySizeInBits: 256,
kSecReturnRef: true,
kSecUseOperationPrompt: "Verify your identity",
kSecAttrApplicationLabel: "unique label" // a customer identifier
]
- Create a signature of a
CFData
by with thekey
from step #2:
var error: Unmanaged<CFError>?
let signature = SecKeyCreateSignature(key, .ecdsaSignatureMessageX962SHA256, data, &error)
Expected Result
The customer is prompted for Face ID, passes, and SecKeyCreateSignature(…)
successfully returns a signature.
Note that this method successfully works for us on thousands of devices, from iOS 11.2 to iOS 14.6.
Actual Result
In a rare isolated case, we are seeing the SecItemCopyMatching(…)
succeed and then the SecKeyCreateSignature(…)
call fails to display the Face ID prompt. Instead, SecKeyCreateSignature(…)
immediately fails and populates an error with the following information:
domain: CryptoTokenKit
code: -3
localizedDescription: The operation couldn’t be completed. (CryptoTokenKit error -3.)
description: "<sepk:p256 kid=1214c04d05261ee3>: unable to sign digest" UserInfo={NSDebugDescription=<sepk:p256 kid=1214c04d05261ee3>: unable to sign digest, AKSError=-536362999}
On this particular iPhone 11 Pro device, the customer did not have any issues with this code around 6 months prior to the failure. The customer has more recently encountered the failure, and we have confirmed the device fail to create signatures 100% of the time with the above error. We have asked the customer to reboot the device to no avail, and we have confirmed that Face ID does indeed successfully work on the device's lock screen. The failure still continues.
Additional Notes
We are not able to find any information about this specific failure from the documentation or additional research on the web. We were able to deduce that that CryptoTokenKit error -3 maps to TKErrorCodeCorruptedData
. In the documentation of TKErrorCodeCorruptedData
, it is unclear if the corruption is referring to the private key or or to the dataToSign
parameter of SecKeyCreateSignature()
. Do you have any insight into why/when this error is returned, and how might we avoid it in the future? Thank you.
On this particular iPhone 11 Pro device
I’ve worked with a number of developers with problems like this, where things work just fine in general but fail mysteriously on a tiny fraction of devices. I’m happy to dig into the details but this takes way more time than I have available for DevForums.
To start, I recommend that you get a bug on file about this. For that bug to be actionable it’ll need to include a sysdiagnose log that was captured shortly after your app triggered this error. You can either ask the device owner to file the bug directly, or have them send you the sysdiagnose log and then you can file it on their behalf.
For more information about sysdiagnose logs, see our Bug Reporting > Profiles and Logs page.
Please post the bug number, just for the record.
After that, you can open a DTS tech support incident, which will allow me to take a more in-depth look. Make sure to reference your bug and this DevForums thread.
Alternatively, my experience from working with other developers is that you can clear the problem by deleting the key and creating a new one. If that sort of re-enroll workflow is suitable for your app — that is, you can delete the key without losing a tonne of user data — you could add it and then send a test build to the user to see if that helps in their specific caes.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"