Unknown CryptoTokenKit error encountered during SecKeyCreateSignature() with Secure Enclave private key

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

  1. 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).

  1. 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
]
  1. Create a signature of a CFData by with the key 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.

Accepted Reply

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"

Replies

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"

Thank you for the quick response, Quinn.

We will collect sysdiagnose logs from the customer's device in the coming weeks and file a DTS tech support incident with that data.

In the meantime, can you please provide any more insight about the scenarios you expect the SecKeyCreateSignature(…) function to return the CryptoTokenKit -3 error (TKErrorCodeCorruptedData) with message "<sepk:p256 kid=1214c04d05261ee3>: unable to sign digest"? Also, is the "corruption" terminology referring to the dataToSign input to SecKeyCreateSignature(…), or is it referring to a different kind of data corruption? Any additional information here would be helpful for us to investigate and potentially redesign the system around.

Unfortunately re-enrolling via key deletion and recreation is not currently a tenable solution for this app, in which the Secure Enclave private key signing has an essential role.

is it referring to a different kind of data corruption?

I think it’s unlikely to be related to your input data. The error you posted contains this AKSError=-536362999. In this context AKS means Apple key store, a kernel component involved in… well… key storage (-: The error, -536362999, is actually better rendered as 0xe007c009. I took a quick look at that and it seems to translate to ‘policy invalid’. I’ve no idea what that means, alas, and I’m out of time to dig further in the context of DevForums.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Just chiming in to say that we have been getting reports the same issue from our users, key-pairs that's been working for several months suddenly started failing with "The operation couldn’t be completed. (CryptoTokenKit error -3.)". Also affects iPadOS.

We also encountered this problem, which appeared in version 14.5-15.0. Is there any progress on this issue? What should we do to avoid this problem?

Error Domain=CryptoTokenKit Code=-3 "<sepk:p256 kid=09457140cef6eace>: unable to sign digest" UserInfo={NSDebugDescription=<sepk:p256 kid=09457140cef6eace>: unable to sign digest, AKSError=-536362999}

I have the same problem. It occurs when:

  1. User has stored key pair in the keychain / secure enclave
  2. User changes his biometrics settings (add a fingerprint / set up alternative appearance / reset face id).

We've encountered the same problem. @spindel, have feedback/DTS helped you find the root cause of the issue and if so do you mind sharing your findings here?

We've encountered the same problem.

Do you use .biometryCurrentSet.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thanks @eskimo, we used to use .biometryCurrentSet (for internal users in the beta phase long before making the app available in AppStore), but stopped when "Face ID with a Mask" was introduced as it was invalidating the enrollments.

Current set of flags:

let access = SecAccessControlCreateWithFlags(
    kCFAllocatorDefault,
    kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
    [.privateKeyUsage, .biometryAny],
    nil
)

So this 0xe007c009 error is easy to reproduce, and expected, if you have .biometryCurrentSet set. If you don’t have that set then this likely indicates that something has gone wrong internally to iOS. The last time I looked at this:

  • There was no way to recover the affected key. It’s basically gone, in much the same way as it’d be gone if you had .biometryCurrentSet set and the user changed their biometrics.

  • The way to get around the problem is to delete the affected key and then run the user through the process to generate a new key.

We believe that the underlying cause of this problem is a bug in the OS but it’s proved to be hard to find and fix. As I mentioned above, this seems to only show up on a tiny fraction of devices. Efforts in this space are ongoing (r. 82829487).

We have plenty of bugs about this already, so there’s no need to file more.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Many thanks @eskimo for the very insightful and honest answer. In the latest 30 days the issue affected ~0.05% of our users, it's a fraction of the whole population, but still a significant number. We will try to add some workaround to help users to re-generate and re-enroll the key if we see this issue happening, currently it's treated as a transient error so users can retry, but as you mentioned, it will never work again. Do you have enough data i.e. sysdiagnose reports to debug it? I can help to get more reports. We get info about this issue almost in real time and have infrastructure/processes in place to follow up with users and collect diagnostics (including sysdiagnose) if needed.

We now encounter the same problem. Last occurrence was with an update from iOS 15.5.0 to 15.6.1 on an iPhone 12. Is there already a statement from apple?

Is the keytype kSecAttrKeyType: kSecAttrKeyTypeRSA also affected?

Is the keytype … kSecAttrKeyTypeRSA also affected?

The Secure Enclave can only protect NIST P-256 keys.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Is a solution to the problem available?