Hi,
I've been playing a little with CryptoKit, and I'm confused by the fact that you can convert the SecureEnclave private keys to data and then create keys from that data. For example:
let signingKey = try! SecureEnclave.P256.Signing.PrivateKey()
print("Signing key data: \(signingKey.dataRepresentation.base64EncodedString())")And then on another device you can "import" it:
let importedKey = Data(base64Encoded: "BIIBHzGCARswGgwCZWQxFDASDANhY2wxCzAJDARkYWNsAQEBMIH8DAJyazGB9TAHDAJiYwIBCjALDANiaWQEBOqZTJcwFwwDa2lkBBBFzglTIUNGbZ5MBD7cUr2wMAcMAmt0AgEEMAcMAmt2AgEBMEgMA3B1YgRBBKd1FjDccEJwDkCKtEQoUb321YA5NEwBobllMP/rkvRMXpy97YBKqQr3YtpHMU1plIVXkziI91r7+4afKYT2XGwwJwwDcmttBCBIJACijmlKv5GDMlHGzziP/EZF0MFj61ST5RaiksP4ITAPDANya28CCIAAAAAAAAAAMC4MAndrBCgw/AdDczvX7RShPFQqQJIavDny6KCMznht870F2fPph5NTh/UVZ83n")!
let importedSigningKey = try! SecureEnclave.P256.Signing.PrivateKey(dataRepresentation: importedKey)
print("Imported signing key data: \(importedSigningKey.dataRepresentation.base64EncodedString())")This prints the same dataRepresentation as on the original device. However it appears that you can't actually use those private keys for any signing or encryption operations.
If you do this on the same device and then use the key for signing, it works, items signed with both the original signing key and the copied signing key will validate correctly:
let signingKeyBase64 = signingKey.dataRepresentation.base64EncodedString()
let copiedKeyData = Data(base64Encoded: signingKeyBase64)!
let copiedKey = try! SecureEnclave.P256.Signing.PrivateKey(dataRepresentation: copiedKeyData)
let copySignature = try! copiedKey.signature(for: "Hello, world!".data(using: .utf8)!)
assert(copiedKey.publicKey.isValidSignature(copySignature, for: "Hello, world!".data(using: .utf8)!))
assert(signingKey.publicKey.isValidSignature(copySignature, for: "Hello, world!".data(using: .utf8)!))I'm guessing what's really happening is that the data representation is some cunning reference to a SecureEnclave key which is being stored in the keychain, and "importing" that data just binds it in some fashion to that existing key. It fails on the other device because there is not a private key with that encoded reference.
Have I missed some documentation that covers all this? What's the purpose of the data representation here - is that something that could be stored as a value in, say, a shared keychain for use between apps? And can someone verify that SecureEnclave private keys remain un-extractable?
Thanks,
Dave
Along with iOS 13 Beta 5, the CryptoKit developers have posted a documentation update that addresses this:
https://developer.apple.com/documentation/cryptokit/storing_cryptokit_keys_in_the_keychain
About 25% into the document, they note:
"Keys that you store in the Secure Enclave expose a raw representation as well, but in this case the data isn’t the raw key. Instead, the Secure Enclave exports an encrypted block that only the same Secure Enclave can later use to restore the key."
So, as I suspected, the .data represents a wrapped object with the real private key (and presumably use / authorization policy) encrypted by a wrapping key unique to the individual hardware secure element.