-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
Conversation
Tagging subscribers to this area: @dotnet/ncl, @bartonjs, @vcsjones |
@@ -0,0 +1,429 @@ | |||
// Licensed to the .NET Foundation under one or more agreements. | |||
// The .NET Foundation licenses this file to you under the MIT license. | |||
|
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.
This file looks like C with some AppleClang extensions, rather than objective C
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.
This is Objective-C
e.g. .m
vs .c
file type.
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.
Looks interesting so far, but it feels like a substantial change which we are unlikely to get into 9.0
|
||
[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); |
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.
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
?
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.
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))
@@ -251,11 +253,13 @@ private async Task RenegotiateAsync<TIOAdapter>(CancellationToken cancellationTo | |||
} | |||
|
|||
// reAuthenticationData is only used on Windows in case of renegotiation. | |||
private async Task ForceAuthenticationAsync<TIOAdapter>(bool receiveFirst, byte[]? reAuthenticationData, CancellationToken cancellationToken) | |||
private async Task ForceAuthenticationAsync<TIOAdapter>(bool receiveFirst, byte[]? reAuthenticationData, CancellationToken cancellationToken, bool isAsync = true) |
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 extra isAsync
parameter is a bit weird, since the information should already be apparent from the TIOAdapter. Is this expected to stay? So far the usages of this inside the function seem debug-only or WIP code.
int toWrite = (int)length; | ||
var inputBuffer = new ReadOnlySpan<byte>(data, toWrite); | ||
|
||
if (context.UseNwFramework) |
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.
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.
@@ -34,6 +34,7 @@ internal enum SecurityStatusPalErrorCode | |||
Renegotiate, | |||
TryAgain, | |||
HandshakeStarted, | |||
ContinuePendig, |
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.
ContinuePendig, | |
ContinuePending, |
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. |
|
||
internal static unsafe int NwSetTlsOptions(SafeSslHandle sslHandle, nint gcHandle, string targetName, List<SslApplicationProtocol>? applicationProtocols, SslProtocols minTlsVersion, SslProtocols maxTlsVersion) | ||
{ | ||
int alpnLength = GetAlpnProtocolListSerializedLength(applicationProtocols); |
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.
nit: indentation
[LibraryImport(Interop.Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_NwCancelConnection")] | ||
internal static partial int NwCancelConnection(SafeSslHandle sslHandle); | ||
|
||
|
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.
return; | ||
} | ||
|
||
Debug.Assert(GetAlpnProtocolListSerializedLength(applicationProtocols) == buffer.Length, |
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.
FWIW, you no longer need this message. The exact same message will now be injected for you,
@@ -53,6 +54,7 @@ public ArrayBuffer(byte[] buffer) | |||
|
|||
public void Dispose() | |||
{ | |||
_disposed = true; |
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.
nit: indenting
@@ -2864,7 +2865,7 @@ public virtual async Task UseWrappedAfterClose_Success() | |||
readable.ReadByte(); | |||
} | |||
} | |||
|
|||
*/ |
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.
?
@@ -355,12 +620,32 @@ internal int ReadPendingWrites(byte[] buf, int offset, int count) | |||
SslProtocols.Tls12 | |||
}; | |||
|
|||
private static void SetProtocols(SafeSslHandle sslContext, SslProtocols protocols) | |||
private static readonly SslProtocols[] s_orderedSslProtocols13 = new SslProtocols[6] |
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.
Can this be:
private static ReadOnlySpan<SslProtocols> OrderedSslProtocols13 =>
[
#pragma warning disable 0618
SslProtocols.Ssl2,
SslProtocols.Ssl3,
#pragma warning restore 0618
#pragma warning disable SYSLIB0039 // TLS 1.0 and 1.1 are obsolete
SslProtocols.Tls,
SslProtocols.Tls11,
#pragma warning restore SYSLIB0039
SslProtocols.Tls12,
SslProtocols.Tls13
];
?
// if we have background task eat any exceptions | ||
_frameTask?.ContinueWith(t => { | ||
_ = t.Exception; | ||
_buffer.ReturnBuffer(); |
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 prevents the buffer from being returned twice?
// This is optimization to consume and report alters insteads of throwing IO excceoption as | ||
// the peer would typically close connection afterwards. | ||
// We don't want to thorw here, taht would be done later if needed | ||
Task.WaitAny(new Task[] { handshakeTask }, cancellationToken); |
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.
Why is this always synchronous regardless of whether isSync?
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.
Also, the cancellation token here will cancel the waiting, but it won't affect handshakeTask... that's ok? Will handshakeTask be canceled separately in response to cancellationToken?
if (isSync) | ||
{ | ||
Task[] tasks = new Task[] { handshakeTask, _frameTask }; | ||
int index = Task.WaitAny(tasks, cancellationToken); |
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.
Why is synchronous waiting being canceled with the token but asynchronous waiting isn't?
// 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)); |
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.
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.
Draft Pull Request was automatically closed for 30 days of inactivity. Please let us know if you'd like to reopen it. |
Draft Pull Request was automatically closed for 30 days of inactivity. Please let us know if you'd like to reopen it. |
Draft Pull Request was automatically closed for 30 days of inactivity. Please let us know if you'd like to reopen it. |
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 synchronous
WhenAny` 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:
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.