Apple, iOS 13, CryptoKit, Secure Enclave - Enforce biometric authentication ahead of private key usage
Asked Answered
M

1

7

I am working with Apple's new cryptokit library and am trying to get a basic use case to work.

Goal: I would like to create a private key in the secure enclave via the cryptokit, store the key's reference in the iOS device's key chain and ensure that the key can only be reinitialized in the secure enclave after the user has authenticated himself via some biometric authentication method.

Current state: So far, I am able to initialize a private key in the secure enclave via the following code:

var privateKeyReference = try CryptoKit.SecureEnclave.P256.KeyAgreement.PrivateKey.init();

Furthermore, I can store and retrieve the corresponding private key's reference from the key chain. After retrieving the reference, I can reinitialize the private key in the secure enclave with the following code:

var privateKeyReference = getPrivateKeyReferenceFromKeyChain();
var privateKey = try CryptoKit.SecureEnclave.P256.KeyAgreement.PrivateKey.init(
   dataRepresentation: privateKeyReference
);

So far everything works as expected and all cryptographic operations with the private key succeed.

Now, as far as I understand the spare documentation by Apple, I should be able to modify the first initialization of the private key to something as follows.

let authContext = LAContext();
let accessCtrl = SecAccessControlCreateWithFlags(
   kCFAllocatorDefault,
   kSecAttrAccesibleWhenUnlockedThisDeviceOnly,
   [.privateKeyUsage, .userPresence, .biometryCurrentSet],
   nil
);
var privateKeyReference = try CryptoKit.SecureEnclave.P256.KeyAgreement.PrivateKey.init(
   accessControl: accessCtrl!,
   authenticationContext: authContext
);

Thereby, ensuring that the private key can only be reinitialized, when the user authenticates himself via some biometric authentication method. The initial initialization stil works without any errors.

Problem: However, adding the previous code, I do not get any biometric authentication prompt and can not use the private key at all after reinitialization. The following error is logged whenever I try to execute some cryptographic operation with the reinitialized key, here for example some signing:

Error Domain=CryptoTokenKit Code=-9 "setoken: unable to sign digest" UserInfo={NSLocalizedDescription=setoken: unable to sign digest})

As far as I could guess from here, I think that Code=-9 refers to the "authenticationNeeded" error.

Question: Can someone point me to some documentation or tutorial how to achieve what I am looking for or explain to me what I am missing?

Thanks!

Cross-Post: https://forums.developer.apple.com/message/387746

Mordvin answered 25/9, 2019 at 16:0 Comment(7)
when adding permission have you enter permission description too in info plist?Septuple
Did you specify access control?Gwenngwenneth
@AbuUlHassan: Yes, I added face id permissions to Info.plist since I can use face id via some auth context standalone.Mordvin
@Woodstock: As far as I understand your article, it does not refer to key creation with the cryptokit, does it? However, as written in my post, I tried to initially initiate the private key with an access control object and a authentication context. Is that what you meant?Mordvin
search for biomatric verification guide by raywanderlilch he have prepared a well defined tutorial in detail.Septuple
I guess you are referring to the following tutorial: raywenderlich.com/… ? This tutorial is not related to the cryptokit library, which I am talking about.Mordvin
@Mordvin Is it possible for you to share the code responsible for storing and retrieving the corresponding private key's reference from the key chain. I'm trying to achieve a similar functionality. Thank you in adavance!Tapping
M
3

After a couple of days of patience I was able to obtain an answer from the Apple development support. They suggested the following method which only differs a little bit from my approach:

var error: Unmanaged<CFError>? = nil;
let accessCtrl = SecAccessControlCreateWithFlags(
   nil,
   kSecAttrAccesibleAfterFirstUnlockThisDeviceOnly,
   [.privateKeyUsage, .biometryCurrentSet],
   &error
);
var privateKeyReference = try CryptoKit.SecureEnclave.P256.KeyAgreement.PrivateKey.init(
   accessControl: accessCtrl
);

Additionally, in the meantime iOS version 13.1.3 was released and, after upgrading my device, the above code started working. So either there is a subtle difference between mine and Apple's code or it is related to the update. Nevertheless, it is working now.

Mordvin answered 20/10, 2019 at 13:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.