Why failed to add or get key in secure enclave on iPhone with iOS 12.x when running in loop

I have a project that use secure enclave to encrypt my data, this project minimum supports from iOS 11.

To make sure the stability of my project, I run test, it has a loop to add, get, delete key in secure enclave for 100 iterations.

I found that the secure enclave always failed to add/get key from secure enclave when using iOS device that is iOS 12.x.

It returns error message - The operation couldn’t be completed. (OSStatus error -26276 - Key generation failed, error -26276)

error code -26276 is errSecInternal - An internal error occured in the Security framework.

I tested 23 devices, there are 5 out of 6 failed on iOS 12.x devices, the remaining devices with iOS 11.x, 13.x, 14.x, 15.x do not face this issue.

Why it only happens on iOS 12.x device?

Accepted Reply

Keys that are protected by the Secure Enclave (SE) are not actually stored in the SE. Rather, they are stored in the standard keychain [1] and then protected by the SE. That is, the bytes stored in the keychain are scrambled by the SE in a way that only the SE can unscramble them in order to work with the key.

When the system goes to use such a key, it passes the scrambled key and the data to operate on to the SE, which does that operation and returns the result. That requires a communication channel between iOS and the SE. There’s a limit to the number of these channels. So, when working with SE-protected keys its important to get a SecKey object from the keychain, use it, and then release it promptly.

I suspect that your loop is missing the last bit and that’s why you’re running into problems. I can see two possibilities there:

  • If you’re not using ARC, you may be leaking the key.

  • Some could be adding the key to the autorelease pool. In that case, adding an autorelease pool drain inside your loop will fix the issue.

Share and Enjoy

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

[1] Or they can be ephemeral but it sounds like that’s not your concern here.

Replies

Keys that are protected by the Secure Enclave (SE) are not actually stored in the SE. Rather, they are stored in the standard keychain [1] and then protected by the SE. That is, the bytes stored in the keychain are scrambled by the SE in a way that only the SE can unscramble them in order to work with the key.

When the system goes to use such a key, it passes the scrambled key and the data to operate on to the SE, which does that operation and returns the result. That requires a communication channel between iOS and the SE. There’s a limit to the number of these channels. So, when working with SE-protected keys its important to get a SecKey object from the keychain, use it, and then release it promptly.

I suspect that your loop is missing the last bit and that’s why you’re running into problems. I can see two possibilities there:

  • If you’re not using ARC, you may be leaking the key.

  • Some could be adding the key to the autorelease pool. In that case, adding an autorelease pool drain inside your loop will fix the issue.

Share and Enjoy

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

[1] Or they can be ephemeral but it sounds like that’s not your concern here.

We create a 200 iterations for-loop to do add key and delete key. By using on iPhone 5s (iOS 12.5.5) to run this test. When using CFRelease to release key, it will failed on 150+ iteration, failed on add or delete key. When using autorelease pool drain to release key, it will failed on 70+ iteration, failed on add or delete key. Is it an iOS 12.x bug? Because it only happens on iOS 12.x devices.

Is it an iOS 12.x bug?

Quite possibly.

If you repeat this test outside of a tight loop, does that improve things? For example, add a timer to your app so that every second it runs a single iteration of your test. Does that fail after 70 seconds?

Share and Enjoy

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

After several tried out, the problem is resolved. :) Keep CFRelease and adding autorelease pool drain in all functions that relates to secure enclave, tested with iPhone 5s (iOS 12.5.5) can run till 1000 iterations. Thanks for your help :)