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

add support of TLS 1.3 on macOS POC #104835

Draft
wants to merge 35 commits into
base: main
Choose a base branch
from
Draft

add support of TLS 1.3 on macOS POC #104835

wants to merge 35 commits into from

Conversation

wfurt
Copy link
Member

@wfurt wfurt commented Jul 12, 2024

This contributes to #1979 that is long due. The API we currently use is deprecated since 10.5 Catalina and can possibly go at any time. Also with recent security push there may be more TLS 1.3 only servers and there really no good workaround for macOS and iOS so those platforms will simply fail to connect.

There are conceptually two challenges.

The new API does not provide capability to work on top of buffers like SslStream expects. To over come it we use Framer approach pretending we are implementing custom protocol. Something @filipnavara demonstrated here: https://gist.github.com/filipnavara/d5fb55bdb5edcceb1981f73078b855c4

The second is that there is no synchronize API. We basically call void function and we provide lambda that would be called once in the future. While that may be generally handy and similar to our 'async` it creates significant complication and platform difference.

To do that both handshake and decrypt have new Task that deliver events. I tried to structure it in a way that it has no impact on other platform. There can perhaps be improvements here. Certainly the "decrypt in place is a lieand we can possibly avoid some copies and memory use by refactoring. Original naive implementation was hitting assets for synchronous API - something I fixed by tracking sync vs async now and using synchronousWhenAny` when needed.

This PR is trying to close the gap by addressing both. It is still in rough shape but I would like to solicit early feedback before spending too much time polishing. I can do basic operations and pass most of the test locally:

=== TEST EXECUTION SUMMARY ===
     System.Net.Security.Tests  Total: 577, Errors: 0, Failed: 9, Skipped: 16, Time: 405.069s

Since the implementation is not complete I keep old one around as well and I use new one only when seems appropriate.
I also envision some possible compat switch so user can opt-in or opt-out as needed. The focus is on client for developers talking to server they cannot control. For simple HTTP(s) it should just work.

From failing tests, the new API is more picky about non ASCIS in SNI name. Something we saw on Android, but it should not matter for HTTP as we puny-code IDN.

Some tests (particularly around cipher suites) are confused as we cannot handshake TLS 1.3 in tests but the client side is capable of doing it.

And there are still some loose ends around closing, disposing and cleanup.

it would be great to get feedback, particularly from
@jkotas for inter
@stephentoub for async
@filipnavara for objective-C and macOS API use
@rzikm for integration with SslStream

and @dotnet/ncl for all the above. Note that the Network Framework can also do Quic so this may be option for use in the distant future.

Copy link
Contributor

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

Copy link
Member

@rzikm rzikm left a comment

Choose a reason for hiding this comment

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

Looks interesting so far, but it feels like a substantial change which we are unlikely to get into 9.0

Comment on lines 77 to +82

[LibraryImport(Interop.Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_NwCreateContext")]
internal static partial System.Net.SafeSslHandle NwCreateContext(int isServer);

[LibraryImport(Interop.Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_NwInit")]
internal static unsafe partial int NwInit(delegate* unmanaged<IntPtr, PAL_NwStatusUpdates, IntPtr, IntPtr, int> statusCallback, delegate* unmanaged<IntPtr, byte*, void**, int> readCallback, delegate* unmanaged<IntPtr, byte*, void**, int> writeCallback);
Copy link
Member

Choose a reason for hiding this comment

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

I assume Nw* and Ssl* functions can't be used interchangeably, would it make sense to move it to its own class like Interop.NetworkFramework?

Copy link
Member

@liveans liveans Jul 15, 2024

Choose a reason for hiding this comment

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

This would be helpful, especially for the possible future implementations/experiments. (QUIC API is there and can be used with couple of more interop additions to this. (there are some bugs))

int toWrite = (int)length;
var inputBuffer = new ReadOnlySpan<byte>(data, toWrite);

if (context.UseNwFramework)
Copy link
Member

Choose a reason for hiding this comment

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

There are now lots of such branches all over the codebase, It might be a lot of work but reshuffling the code s.t code-paths which uses NetworkFramework is in it's own class (SafeDeleteNwSslContext maybe?) would help readability IMO.

@wfurt
Copy link
Member Author

wfurt commented Jul 15, 2024

Looks interesting so far, but it feels like a substantial change which we are unlikely to get into 9.0

My goal would be to make it op-in feature like we did for managed NTLM. e.g. make minimal impact by default but provide something for users who would be stuck otherwise.

src/libraries/Common/src/System/Net/ArrayBuffer.cs Outdated Show resolved Hide resolved
// if we have background task eat any exceptions
_frameTask?.ContinueWith(t => {
_ = t.Exception;
_buffer.ReturnBuffer();
Copy link
Member

Choose a reason for hiding this comment

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

What prevents the buffer from being returned twice?

// We grab reference to prevent disposal while handshake is pending.
this.DangerousAddRef(ref add);
ObjectDisposedException.ThrowIf(_disposed, this);
Interop.AppleCrypto.NwStartHandshake(SslContext, GCHandle.ToIntPtr(gcHandle));
Copy link
Member

Choose a reason for hiding this comment

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

Seems like you might need some GC.KeepAlives in various places. This gcHandle is a weak handle, which means it's not keeping this alive, which means if nothing after the call to this instance method is guaranteed to be keeping the instance alive, it's possible it could be collected at a bad time around here.

Copy link
Contributor

Draft Pull Request was automatically closed for 30 days of inactivity. Please let us know if you'd like to reopen it.

@wfurt wfurt reopened this Aug 19, 2024
Copy link
Contributor

Draft Pull Request was automatically closed for 30 days of inactivity. Please let us know if you'd like to reopen it.

@filipnavara filipnavara reopened this Sep 18, 2024
Copy link
Contributor

Draft Pull Request was automatically closed for 30 days of inactivity. Please let us know if you'd like to reopen it.

@liveans liveans reopened this Nov 21, 2024
Copy link
Contributor

Draft Pull Request was automatically closed for 30 days of inactivity. Please let us know if you'd like to reopen it.

Copy link
Contributor

Draft Pull Request was automatically closed for 30 days of inactivity. Please let us know if you'd like to reopen it.

@liveans liveans reopened this Jan 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants