Closed Sajjon closed 2 years ago
We could even make it easier to restore the SecureEnclave.P256.Signing.PrivateKey
by use of a new type KeyRestorationID
, being based on the HASH of the PublicKey, this way we can let developers store the KeyRestorationID
in UserDefaults
. We could even make it a CHECKSUMMED HASH! Similar to schemes like bech32m can be used for that, it is basically:
Human Readable Prefix (optional) || HASH(publicKey) || CHECKSUM
, the advantage of using a checksummed type is we developers can display an error to the user if she ever is involved with KeyRestorationID
, or if the app incorrectly stored the wrong KeyRestorationID
, e,g, the developer accident stored a substring of it, dropping a char in the beginning of the end, the initiliazer of KeyRestorationID
can validate that is is a checksummed (and thus probably correct) id!
As you rightly guessed, the best place to ask this question is the Developer Forum.
@FredericJacobs where questions seldomly gets answered...
@Lukasa might appreciate my proposed API improvement above, so please don't just ignore this 🙏
FWIW, you are missing something. An important note here is that the SecureEnclave
keys do not have an x963Representation
or similar, only a dataRepresentation
. Nothing says that this representation represents the unencrypted private key.
FWIW, you are missing something. An important note here is that the
SecureEnclave
keys do not have anx963Representation
or similar, only adataRepresentation
. Nothing says that this representation represents the unencrypted private key.
Perfect! Thank you! So dataRepresentation
property is an encrypted form of the private key!
Then why is this not documented? Why not name the property and the label of the matching initializer encryptedData
? This would change documentation from impossible to understand to clear as day!
Would also be good to add that a developer safely can store this encryptedData in e.g. UserDefaults
.
Cheers! 🥳
@Sajjon : See sample project (https://developer.apple.com/documentation/cryptokit/storing_cryptokit_keys_in_the_keychain)
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. You can adopt the same convertibility protocol to store the Secure Enclave’s encrypted data in the keychain as a generic password, and later allow the Secure Enclave to reconstruct the key on the same device:
extension SecureEnclave.P256.Signing.PrivateKey: GenericPasswordConvertible { init<D>(rawRepresentation data: D) throws where D: ContiguousBytes { try self.init(dataRepresentation: data.dataRepresentation) }
var rawRepresentation: Data {
return dataRepresentation // Contiguous bytes repackaged as a Data instance.
}
}
@FredericJacobs nice, thanks a lot, this is very helpful indeed! But still, dont you think it would be good to update the documentation? And make it clear in the documentation that it is an encrypted block of data?
I had completely missed that sample code repo... and I think it a very bad idea if APIs rely on sample projects to be easy to understand, documentation really should specify this. Don't you agree? 😄
@Lukasa Is there any special reason why we cannot import of already created P256.Signing.PrivateKey
into Secure Enclave? Would you ever be open to adding support for that? The use case is the Secure Enclave does not support hierarchical deterministic derivation. So for P256.Signing.PrivateKey
s that are derived with SLIP10, these cannot be imported by Secure Enclave.
The idea here is that it would be safer to use Secure Enclave for signing, than signing in our iOS apps memory.
Which, at the end of the day, makes our iPhone users and Apple users safer :)
That's a great question that sadly I'm not qualified to answer. I've passed the feedback request on to the relevant internal team, who are taking it into consideration. I also recommend filing a request via Feedback Assistant.
This question should probably go on Apple Developer Forum, however, I think users of this lib might find it useful and I also have an improvment proposal for CryptoKit, which might affect swift-crypto?
If I create a new
SecureEnclave.P256.Signing.PrivateKey
using this initializer (e.g. using default SecAccessControlafterFirstUnlockThisDeviceOnly
) -- let's call this newly created private keyKey0
-- it is my understanding that (assuming the iPhone supports it)Key0
lives in the secure memory of the Secure Enclave. And anysign
operations is performed in the Secure Enclave, meaning that this is a true hardware wallet, i.e. the private keyKey0
never ever leaves the memory of the secure enclave, is that correct?That raises the question, how do I re-access
Key0
upon restart of the app? It looks like I need to use the initializerdataRepresentation: Data
(and possibly with the optionalLAContext
), but then I need to pass in the Private Key as data! Which requires the user to have exported the private key, which means that the private key has left the secure enclave!!! I.e. we are forced to store the private key in keychain, which items are encrypted with an encryption key that lives in the secure enclave if I have understood it correctly?So I guess the ""only"" (still a big one) win of
SecureEnclave.P256.Signing.PrivateKey
vs non-secure enclave variantP256.Signing.PrivateKey
is that once initialized, it lives inside the memory of the secure enclave, but we still need to go through unsafe realm of memory outside of secure enclave to get there, for the path of re-accessing an earlier created key, e.g.Key0
??If my understanding is correct, then here comes a question and a possible improvement proposal!
Isn't
Key0
persisted (between iPhone/device restarts)? Because Secure Enclave holds my credit cards (if any) for Apple Pay etc, right? So it must support persistence! So it feels like we are missing an initializer forSecureEnclave.P256.Signing.PrivateKey
! The one where we want to get a handle to it by matching it against its public key! So the flow and code are:SecureEnclave.P256.Signing.PrivateKey
, let us once again call itKey0
, the app CoolCryptApp persists the publicKey of the private key, and it saves this public key in keychain (or possibly UserDefaults, since it is a public key)Key0
by use of a newstatic func
(or iniitalizer)SecureEnclave.P256.Signing.PrivateKey.restore(matching: P256.Signing.PublicKey) -> SecureEnclave.P256.Signing.PrivateKey?
which either is returning an Optional (or failing init) or throws.That way we can re-access
Key0
without the private key ever leaving the secure enclave!Am I missing something?
Cheers!