Skip to content
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

[macOS] Implement RSA, ECC through new macOS 10.12 APIs #51620

Merged
merged 5 commits into from
Apr 27, 2021

Conversation

filipnavara
Copy link
Member

@filipnavara filipnavara commented Apr 21, 2021

  • Key generation is updated to use SecKeyCreateRandomKey API to avoid need for temporary keychains
  • RSA encryption is converted to use SecKeyCreateEncryptedData
  • RSA decryption is converted to use SecKeyCreateDecryptedData

The new APIs are also available on iOS so the conditional blocks around the reimplemented APIs are removed. The implementation is not tested on iOS and may require some tweaks but it compiles at least.

@ghost
Copy link

ghost commented Apr 21, 2021

Tagging subscribers to this area: @bartonjs, @vcsjones, @krwq, @GrabYourPitchforks
See info in area-owners.md if you want to be subscribed.

Issue Details

…ta and bypass keychains

NOT READY FOR REVIEW

Author: filipnavara
Assignees: -
Labels:

area-System.Security

Milestone: -

@filipnavara
Copy link
Member Author

Related to #38101 which I will likely try to revive at some point in some form.

@vcsjones
Copy link
Member

Maybe thing's have gotten better since I last tried in macOS world (or maybe I did a bunch of things wrong 😄 ) but the problem with "data" keys as I call them is they don't seem to work fully in place of keychain-backed keys. e.g. CopyWithPrivateKey on X509Certificate2 I think was one place that I hit.

Oddly, if my recollection serves, ECDSA was more problematic than RSA.

@filipnavara
Copy link
Member Author

Maybe thing's have gotten better since I last tried in macOS world (or maybe I did a bunch of things wrong 😄 ) but the problem with "data" keys as I call them is they don't seem to work fully in place of keychain-backed keys. e.g. CopyWithPrivateKey on X509Certificate2 I think was one place that I hit.

I fully expect that to be a problem, just scratching the unit test surface now to decide on a strategy. On iOS the keychain-backed APIs just don't exist so even if this works on a limited API surface it's still better than nothing.

@bartonjs
Copy link
Member

I expect, given @vcsjones' previous investigation, that instead of editing the current APIs what'll be needed is parallel API for RSA macOS and RSA iOS.

@filipnavara
Copy link
Member Author

filipnavara commented Apr 21, 2021

There's still a lot of code that can be shared. For example, this PR passed the macOS tests with new APIs introduced in macOS 10.12 that exist on iOS too. I'm not necessarily saying the same approach will work everywhere but at least part of the code seems sharable.

Locally the same change for key generation works for ECC too. I expect the key imports to be somewhat more problematic but let's see. Worst case I will have to split the implementations.

@bartonjs
Copy link
Member

For example, this PR passed the macOS tests with new APIs introduced in macOS 10.12 that exist on iOS too.

Huh. I saw they were still in progress, hadn't noticed that the OSX legs had already finished. Since I'm surprised by this (vaguely recalling the issues that @vcsjones ran into), I'll trigger an OuterLoop run to see if it's only some more complex tests that fail.

@bartonjs
Copy link
Member

/azp run runtime-libraries-coreclr outerloop osx

@azure-pipelines
Copy link

No pipelines are associated with this pull request.

@bartonjs
Copy link
Member

/azp run runtime-libraries-coreclr outerloop

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@filipnavara
Copy link
Member Author

It's not that surprising that this part worked. It's still creating the old-style keys (as opposed to the iOS / data keys). The difference is that SecCreateRandomKey doesn't enforce permanent keys while SecKeyGenerateKeyPair does:

https://github.com/Apple-FOSS-Mirror/Security/blob/5bcad85836c8bbb383f660aaf25b555a805a48e4/OSX/libsecurity_keychain/lib/SecKey.cpp#L1667
https://github.com/Apple-FOSS-Mirror/Security/blob/5bcad85836c8bbb383f660aaf25b555a805a48e4/OSX/libsecurity_keychain/lib/SecKey.cpp#L1640-L1643

@filipnavara filipnavara changed the title [macOS/iOS] Implement RSA through SecKeyCreate[Decrypted/Encrypted]Da… [macOS] Implement RSA, ECC through new macOS 10.12 APIs Apr 22, 2021
@filipnavara
Copy link
Member Author

filipnavara commented Apr 22, 2021

The outerloop tests on CI have timed out. I tried to run them locally with .dotnet/dotnet build src/libraries/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj /t:Test /p:XUnitOptions="-noclass System.Security.Cryptography.Algorithms.Tests.AesGcmTests -noclass System.Security.Cryptography.Algorithms.Tests.AesCcmTests" /p:Outerloop=true but it seems to run only two more tests in comparison to the inner loop. Nevertheless all the tests succeeded locally.

UPD: .dotnet/dotnet build src/libraries/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj /t:Test /p:Outerloop=true: System.Security.Cryptography.X509Certificates.Tests Total: 1190, Errors: 0, Failed: 0, Skipped: 0, Time: 112.783s

@filipnavara filipnavara marked this pull request as ready for review April 22, 2021 05:14
@marek-safar
Copy link
Contributor

/cc @MaximLipnin

@MaximLipnin
Copy link
Contributor

#51098

@filipnavara
Copy link
Member Author

It contributes to #51098 but doesn't quite solve it. I have another PR in the pipeline to enable import of ephemeral RSA keys using the iOS-compatible APIs. Note that I am focusing now on the part that can be shared with macOS to simplify testing. Some of the APIs have slightly different semantics on iOS which may need specific tweaks but this should lay down the groundwork to make that happen.

@filipnavara
Copy link
Member Author

Maybe thing's have gotten better since I last tried in macOS world (or maybe I did a bunch of things wrong 😄 ) but the problem with "data" keys as I call them is they don't seem to work fully in place of keychain-backed keys. e.g. CopyWithPrivateKey on X509Certificate2 I think was one place that I hit.

I was playing with it today and it's a mixed bag situation.

First of all, the SecKeyCreateWithData API documentation states that it requires kSecAttrKeySizeInBits which is not true. The implementation doesn't look at it, never looked at it (as far as the published sources go), and even omits it in many of their own regression tests. It's much simpler to use when this is not required. I filed an Apple Feedback to clarify the situation.

Second, it does seem to work for RSA keys even with the item export. On ECC though it hits the problematic code paths in the native code which explain the issues you've hit.

I'll submit a PR later to convert the RSA code paths to use it.

@filipnavara
Copy link
Member Author

filipnavara commented Apr 22, 2021

I got the ECC somewhat working locally without duplicating the keys. The trick is to understand what is going on. All the RSA/ECC keys work with the new SecKeyCreateSignature/SecKeyVerifySignature APIs no matter how they were created. What is problematic is the import and export of the keys.

Private ECC keys created with SecKeyCreateWithData cannot be exported with SecItemExport. They can be exported with SecKeyCopyExternalRepresentation which can also avoid some re-parsing.

Private ECC keys created with SecItemImport cannot be exported with SecKeyCopyExternalRepresentation. It returns errSecPassphraseRequired error.

Public ECC keys created with either SecKeyCreateWithData or SecItemImport can be exported with SecKeyCopyExternalRepresentation.

I've come up with a code that converts most of the imports from ECParameters and other formats to use SecKeyCreateWithData. On export I try to use SecKeyCopyExternalRepresentation and fall back to SecItemExport if the errSecPassphraseRequired error is encountered. This approach seems to pass the unit tests on my machine. It's also easy to extend it to iOS by just disabling some of the code paths that rely on the old APIs.

I'll run the benchmarks from earlier Kevin's PR to see whether there is even significant benefit in using the SecKeyCreateWithData keys or whether it's the SecKeyCreateSignature/SecKeyVerifySignature APIs that make the difference.

@bartonjs
Copy link
Member

/azp run runtime-libraries-coreclr outerloop

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@filipnavara
Copy link
Member Author

The failed test on the outer loop is likely unrelated:

    System.Net.Http.Functional.Tests.SocketsHttpHandlerTest_Http2.SocketSendQueueFull_RequestCanceled_ThrowsOperationCanceled [FAIL]
      System.TimeoutException : The operation has timed out.
      Stack Trace:
        /_/src/libraries/Common/tests/System/Threading/Tasks/TaskTimeoutExtensions.cs(49,0): at System.Threading.Tasks.TaskTimeoutExtensions.WhenAllOrAnyFailed(Task[] tasks, Int32 millisecondsTimeout)
        /_/src/libraries/Common/tests/System/Net/Http/Http2LoopbackServer.cs(172,0): at System.Net.Test.Common.Http2LoopbackServer.CreateClientAndServerAsync(Func`2 clientFunc, Func`2 serverFunc, Http2Options http2Options, Int32 timeout)
        /_/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http2.cs(3451,0): at System.Net.Http.Functional.Tests.HttpClientHandlerTest_Http2.SocketSendQueueFull_RequestCanceled_ThrowsOperationCanceled()
        --- End of stack trace from previous location ---
    System.Net.Http.Functional.Tests.SocketsHttpHandlerTest_Http2.GetAsync_ServerDelaysSendingSettingsThenSetsLowerMaxConcurrentStreamsLimitThenIncreaseIt_ClientAppliesEachLimitChangeProperly [SKIP]

Comment on lines +127 to +128
SafeSecKeyRefHandle keychainPublic;
SafeSecKeyRefHandle keychainPrivate;
Copy link
Member

@bartonjs bartonjs Apr 27, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since these are not keychains (or even bound to them) the variables could probably use a better name.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. I will fix it up in a follow up PR.

@bartonjs
Copy link
Member

Since the variable renames are the only thing I saw, and CI takes a long time, I wouldn't bother with them.

Thanks for spearheading this, @filipnavara!

@marek-safar marek-safar merged commit c8c3963 into dotnet:main Apr 27, 2021
@karelz karelz added this to the 6.0.0 milestone May 20, 2021
@ghost ghost locked as resolved and limited conversation to collaborators Jun 19, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants