-
Notifications
You must be signed in to change notification settings - Fork 4.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use key services and data keys where possible for faster EC operations. #38101
Conversation
I guess what I am looking to get input on:
|
0f9cca1
to
ad615e6
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Things seem.... OK... though I'm confused now about what the difference is between new and old, and am curious to see if things can collapse (when I looked previously I thought they were completely parallel key types, not both SecKeyRef).
If the big difference is that now (vs when I wrote all of this) keys work without being stored in the keychain, but PFX export fails unless it's in a keychain, then I'd hope for an approach like:
- KeyGen makes only the purely ephemeral keys ("data keys"?)
- I assume creating a SecIdentityRef still requires a keychain, so CopyWithPrivateKey would need to find out that the private key doesn't have a keychain and deal with that then.
- The Get*PublicKey methods on a cert should just export the keychain-based key and return a new ephemeral key, it looks like that's always guaranteed to make the first public key usage faster, by a couple orders of magnitude.
- The Get*PrivateKey methods can opportunistically export the keychain-based key and make it into an ephemeral keypair. If export fails, use an ephemeral public key along with a keychain private key.
Assuming that all keys can go through the simpler-looking API, we'd just route them all there. If the new API isn't available on iOS, but the old one is, we can hopefully use the one signature and make it use the new API when available and the old API when required. Then all the complexities are around key->cert and cert->key, but once we have a key everything makes sense.
{ | ||
Success => dataKey, | ||
kErrorSeeError => throw CreateExceptionForCFError(errorHandle), | ||
_ => throw new CryptographicException { HResult = result } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The unexpected error codes should Debug.Fail, then probably just throw a default CryptographicException (consistency with the existing paths).
SecKeyPair keys = GetKeys(); | ||
byte[] formattedSignature = AsymmetricAlgorithmHelpers.ConvertIeee1363ToDer(signature); | ||
|
||
if (keys.PublicDataKey != null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would there ever be a situation where we can't get a public data key?
|
||
private SecKeyPair(SafeSecKeyRefHandle publicKey, SafeSecKeyRefHandle? privateKey) | ||
private SecKeyPair(SafeSecKeyRefHandle publicKey, SafeSecKeyRefHandle? privateKey, SafeSecKeyRefHandle? publicDataKey, SafeSecKeyRefHandle? privateDataKey) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the SecKeyRef version of the public key needed for (abstractly)? It seems like all public keys are exportable, all exportable keys can be made into data keys, and data keys are faster.
Can this pair type be a triplet instead of a quadret? PublicDataKey / PrivateDataKey / PrivateKey
|
||
private SecKeyPair(SafeSecKeyRefHandle publicKey, SafeSecKeyRefHandle? privateKey) | ||
private SecKeyPair(SafeSecKeyRefHandle publicKey, SafeSecKeyRefHandle? privateKey, SafeSecKeyRefHandle? publicDataKey, SafeSecKeyRefHandle? privateDataKey) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, now I'm confused. If the existing keys were SecKeyRefs, and the new keys are SecKeyRefs, what's the difference, and why don't they interoperate?
I'm not finding the data in the docs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's... hard to articulate and compounded by I think there is at least one bug in MacOS that I am trying to get chased down. I'll experiment a bit and come back with a more thorough explanation of things.
Now that I have Big Sur working and runtime building on it (surprise, nothing in S.S.C broke so far) I wonder if I will still hit quirks.
Going to close this for now, will make a new PR when it makes sense. |
This is an attempt at improving asymmetric cryptographic operations on macOS, starting with ECDSA.
The makes use of Apple's newer Trust / Key Services APIs instead of using security transforms. In order to gain performance increases, two things needed to happen. One, the key needed to be a "data" key, not attached to any keychain, not even an ephemeral one. Second, use the
SecKey*
APIs.Though the APIs still work off of
SecKeyRef
s, many of the legacy APIs cannot operate on keys that are not in a keychain, ephemeral or not. An example of this isSecItemExport
for ECDSA data keys. To keep the existing code paths working and the changes more minimal, and allow for more gradual adoption of key services where possible, both key handle types are used. When data keys are present, those are preferred for signing and verification operations.In this PR data keys exist in two scenarios. The first is
Create
orGenerateKey
. The second isImportParameters
and any other import methods that defer to it. Other means of obtaining a key, like from a certificate in a keychain or importing a PFX, will not have data keys and will use security transforms.The data keys are obtained by exporting the keys to
ECParameters
and importing them again through keychain services APIs. This means that although signing and verifying ECDSA operations are much faster, importing and key generation take a performance hit since they have more work to do.Some native support for RSA exists here. Though none of the managed implementation uses it yet, as this is solely for ECDSA so far.
Contributes to #36107
Benchmarks:
All sign / verify benches use a generated key.
Code is here: https://github.com/vcsjones/DotNetCryptoBenches