improve handling of handshake failure (#35549)
authorTomas Weinfurt <tweinfurt@yahoo.com>
Thu, 7 May 2020 20:46:43 +0000 (13:46 -0700)
committerGitHub <noreply@github.com>
Thu, 7 May 2020 20:46:43 +0000 (13:46 -0700)
* improve handling of handshake failure

* more cleanup

* remove console reference

* add quotes around parameter

* enable ServerAsyncAuthenticate_MismatchProtocols_Fails

* cleanup ssl2 test

* fix http2

* feedback from review

* add back two missing empty lines

Co-authored-by: Tomas Weinfurt <furt@Shining.local>
15 files changed:
src/libraries/System.Net.Security/src/Resources/Strings.resx
src/libraries/System.Net.Security/src/System.Net.Security.csproj
src/libraries/System.Net.Security/src/System/Net/Security/SecureChannel.cs
src/libraries/System.Net.Security/src/System/Net/Security/SniHelper.cs
src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs
src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.OSX.cs
src/libraries/System.Net.Security/src/System/Net/Security/TlsFrameHelper.cs [new file with mode: 0644]
src/libraries/System.Net.Security/tests/FunctionalTests/ClientAsyncAuthenticateTest.cs
src/libraries/System.Net.Security/tests/FunctionalTests/ClientDefaultEncryptionTest.cs
src/libraries/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs
src/libraries/System.Net.Security/tests/FunctionalTests/ServerNoEncryptionTest.cs
src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSniTest.cs
src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSystemDefaultsTest.cs
src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj
src/libraries/System.Net.Security/tests/FunctionalTests/TestConfiguration.cs

index 7270ea8..8af05eb 100644 (file)
   <data name="net_auth_eof" xml:space="preserve">
     <value>Authentication failed because the remote party has closed the transport stream.</value>
   </data>
+  <data name="net_auth_tls_alert" xml:space="preserve">
+    <value>Authentication failed because the remote party sent a TLS alert: '{0}'.</value>
+  </data>
   <data name="net_auth_alert" xml:space="preserve">
     <value>Authentication failed on the remote side (the stream might still be available for additional authentication attempts).</value>
   </data>
index 868fadd..067df9b 100644 (file)
@@ -34,6 +34,7 @@
     <Compile Include="System\Net\Security\StreamSizes.cs" />
     <Compile Include="System\Net\Security\TlsAlertType.cs" />
     <Compile Include="System\Net\Security\TlsAlertMessage.cs" />
+    <Compile Include="System\Net\Security\TlsFrameHelper.cs" />
     <Compile Include="System\Security\Authentication\AuthenticationException.cs" />
     <!-- NegotiateStream -->
     <Compile Include="System\Net\BufferAsyncResult.cs" />
index 82aab9f..b770ef2 100644 (file)
@@ -625,7 +625,7 @@ namespace System.Net.Security
         //
         // Acquire Server Side Certificate information and set it on the class.
         //
-        private bool AcquireServerCredentials(ref byte[]? thumbPrint, ReadOnlySpan<byte> clientHello)
+        private bool AcquireServerCredentials(ref byte[]? thumbPrint)
         {
             if (NetEventSource.IsEnabled)
                 NetEventSource.Enter(this);
@@ -639,13 +639,13 @@ namespace System.Net.Security
             // with .NET Framework), and if neither is set we fall back to using ServerCertificate.
             if (_sslAuthenticationOptions.ServerCertSelectionDelegate != null)
             {
-                string? serverIdentity = SniHelper.GetServerName(clientHello);
-                localCertificate = _sslAuthenticationOptions.ServerCertSelectionDelegate(serverIdentity);
-
+                localCertificate = _sslAuthenticationOptions.ServerCertSelectionDelegate(_sslAuthenticationOptions.TargetHost);
                 if (localCertificate == null)
                 {
                     throw new AuthenticationException(SR.net_ssl_io_no_server_cert);
                 }
+                if (NetEventSource.IsEnabled)
+                    NetEventSource.Info(this, "Use delegate selected Cert");
             }
             else if (_sslAuthenticationOptions.CertSelectionDelegate != null)
             {
@@ -784,7 +784,7 @@ namespace System.Net.Security
                     if (_refreshCredentialNeeded)
                     {
                         cachedCreds = _sslAuthenticationOptions.IsServer
-                                        ? AcquireServerCredentials(ref thumbPrint, inputBuffer)
+                                        ? AcquireServerCredentials(ref thumbPrint)
                                         : AcquireClientCredentials(ref thumbPrint);
                     }
 
index ffbf730..db68218 100644 (file)
@@ -26,13 +26,13 @@ namespace System.Net.Security
             //     opaque fragment[SSLPlaintext.length];
             // } SSLPlaintext;
             const int ContentTypeOffset = 0;
-            const int ProtocolVersionOffset = ContentTypeOffset + sizeof(ContentType);
+            const int ProtocolVersionOffset = ContentTypeOffset + sizeof(TlsContentType);
             const int LengthOffset = ProtocolVersionOffset + ProtocolVersionSize;
             const int HandshakeOffset = LengthOffset + sizeof(ushort);
 
             // SSL v2's ContentType has 0x80 bit set.
             // We do not care about SSL v2 here because it does not support client hello extensions
-            if (sslPlainText.Length < HandshakeOffset || (ContentType)sslPlainText[ContentTypeOffset] != ContentType.Handshake)
+            if (sslPlainText.Length < HandshakeOffset || (TlsContentType)sslPlainText[ContentTypeOffset] != TlsContentType.Handshake)
             {
                 return null;
             }
@@ -62,10 +62,10 @@ namespace System.Net.Security
             //     } body;
             // } Handshake;
             const int HandshakeTypeOffset = 0;
-            const int ClientHelloLengthOffset = HandshakeTypeOffset + sizeof(HandshakeType);
+            const int ClientHelloLengthOffset = HandshakeTypeOffset + sizeof(TlsHandshakeType);
             const int ClientHelloOffset = ClientHelloLengthOffset + UInt24Size;
 
-            if (sslHandshake.Length < ClientHelloOffset || (HandshakeType)sslHandshake[HandshakeTypeOffset] != HandshakeType.ClientHello)
+            if (sslHandshake.Length < ClientHelloOffset || (TlsHandshakeType)sslHandshake[HandshakeTypeOffset] != TlsHandshakeType.ClientHello)
             {
                 return null;
             }
@@ -363,16 +363,6 @@ namespace System.Net.Security
             return Encoding.GetEncoding("utf-8", new EncoderExceptionFallback(), new DecoderExceptionFallback());
         }
 
-        private enum ContentType : byte
-        {
-            Handshake = 0x16
-        }
-
-        private enum HandshakeType : byte
-        {
-            ClientHello = 0x01
-        }
-
         private enum ExtensionType : ushort
         {
             ServerName = 0x00
index 600163b..d9ff52d 100644 (file)
@@ -34,14 +34,8 @@ namespace System.Net.Security
         // This is set on the first packet to figure out the framing style.
         private Framing _framing = Framing.Unknown;
 
-        // SSL3/TLS protocol frames definitions.
-        private enum FrameType : byte
-        {
-            ChangeCipherSpec = 20,
-            Alert = 21,
-            Handshake = 22,
-            AppData = 23
-        }
+        private TlsAlertDescription _lastAlertDescription;
+        private TlsFrameHandshakeInfo _lastFrame;
 
         private readonly object _handshakeLock = new object();
         private volatile TaskCompletionSource<bool>? _handshakeWaiter;
@@ -274,7 +268,6 @@ namespace System.Net.Security
                 {
                     // get ready to receive first frame
                     _handshakeBuffer = new ArrayBuffer(InitialHandshakeBufferSize);
-                    _framing = Framing.Unknown;
                 }
 
                 while (!handshakeCompleted)
@@ -288,6 +281,19 @@ namespace System.Net.Security
 
                     if (message.Failed)
                     {
+                        if (_lastFrame.Header.Type == TlsContentType.Handshake && message.Size == 0)
+                        {
+                            // If we failed without OS sending out alert, inject one here to be consistent across platforms.
+                            byte[] alert = TlsFrameHelper.CreateAlertFrame(_lastFrame.Header.Version, TlsAlertDescription.ProtocolVersion);
+                            await adapter.WriteAsync(alert, 0, alert.Length).ConfigureAwait(false);
+                        }
+                        else if (_lastFrame.Header.Type == TlsContentType.Alert && _lastAlertDescription != TlsAlertDescription.CloseNotify &&
+                                 message.Status.ErrorCode == SecurityStatusPalErrorCode.IllegalMessage)
+                        {
+                            // Improve generic message and show details if we failed because of TLS Alert.
+                            throw new AuthenticationException(SR.Format(SR.net_auth_tls_alert, _lastAlertDescription.ToString()), message.GetException());
+                        }
+
                         throw new AuthenticationException(SR.net_auth_SSPI, message.GetException());
                     }
                     else if (message.Status.ErrorCode == SecurityStatusPalErrorCode.OK)
@@ -346,17 +352,49 @@ namespace System.Net.Security
                 _framing = DetectFraming(_handshakeBuffer.ActiveReadOnlySpan);
             }
 
-            int frameSize = GetFrameSize(_handshakeBuffer.ActiveReadOnlySpan);
-            if (frameSize < 0)
+            if (_framing == Framing.BeforeSSL3)
+            {
+#pragma warning disable 0618
+                _lastFrame.Header.Version = SslProtocols.Ssl2;
+#pragma warning restore 0618
+                _lastFrame.Header.Length = GetFrameSize(_handshakeBuffer.ActiveReadOnlySpan);
+            }
+            else
+            {
+                TlsFrameHelper.TryGetFrameHeader(_handshakeBuffer.ActiveReadOnlySpan, ref _lastFrame.Header);
+            }
+
+            if (_lastFrame.Header.Length < 0)
             {
                 throw new IOException(SR.net_frame_read_size);
             }
 
+            // Header length is content only so we must add header size as well.
+            int frameSize = _lastFrame.Header.Length + TlsFrameHelper.HeaderSize;
             if (_handshakeBuffer.ActiveLength < frameSize)
             {
                 await FillHandshakeBufferAsync(adapter, frameSize).ConfigureAwait(false);
             }
+
             // At this point, we have at least one TLS frame.
+            if (_lastFrame.Header.Type == TlsContentType.Alert)
+            {
+                TlsAlertLevel level = 0;
+                if (TlsFrameHelper.TryGetAlertInfo(_handshakeBuffer.ActiveReadOnlySpan, ref level, ref _lastAlertDescription))
+                {
+                    if (NetEventSource.IsEnabled && _lastAlertDescription != TlsAlertDescription.CloseNotify) NetEventSource.Fail(this, $"Received TLS alert {_lastAlertDescription}");
+                }
+            }
+            else if (_lastFrame.Header.Type == TlsContentType.Handshake)
+            {
+                if (_handshakeBuffer.ActiveReadOnlySpan[TlsFrameHelper.HeaderSize] == (byte)TlsHandshakeType.ClientHello &&
+                    _sslAuthenticationOptions!.ServerCertSelectionDelegate != null)
+                {
+                    // Process SNI from Client Hello message
+                    TlsFrameHelper.TryGetHandshakeInfo(_handshakeBuffer.ActiveReadOnlySpan, ref _lastFrame);
+                    _sslAuthenticationOptions.TargetHost = _lastFrame.TargetName;
+                }
+            }
 
             return ProcessBlob(frameSize);
         }
@@ -372,23 +410,24 @@ namespace System.Net.Security
             _handshakeBuffer.Discard(frameSize);
 
             // Often more TLS messages fit into same packet. Get as many complete frames as we can.
-            while (_handshakeBuffer.ActiveLength > SecureChannel.ReadHeaderSize)
+            while (_handshakeBuffer.ActiveLength > TlsFrameHelper.HeaderSize)
             {
-                ReadOnlySpan<byte> remainingData = _handshakeBuffer.ActiveReadOnlySpan;
-                if (remainingData[0] >= (int)FrameType.AppData)
+                TlsFrameHeader nextHeader = default;
+
+                if (!TlsFrameHelper.TryGetFrameHeader(_handshakeBuffer.ActiveReadOnlySpan, ref nextHeader))
                 {
                     break;
                 }
 
-                frameSize = GetFrameSize(remainingData);
-                if (_handshakeBuffer.ActiveLength >= frameSize)
+                frameSize = nextHeader.Length + TlsFrameHelper.HeaderSize;
+                if (nextHeader.Type == TlsContentType.AppData || frameSize > _handshakeBuffer.ActiveLength)
                 {
-                    chunkSize += frameSize;
-                    _handshakeBuffer.Discard(frameSize);
-                    continue;
+                    // We don't have full frame left or we already have app data which needs to be processed by decrypt.
+                    break;
                 }
 
-                break;
+                chunkSize += frameSize;
+                _handshakeBuffer.Discard(frameSize);
             }
 
             return _context!.NextMessage(availableData.Slice(0, chunkSize));
@@ -645,7 +684,7 @@ namespace System.Net.Security
                     Debug.Assert(_internalBufferCount >= SecureChannel.ReadHeaderSize);
 
                     // Parse the frame header to determine the payload size (which includes the header size).
-                    int payloadBytes = GetFrameSize(_internalBuffer.AsSpan(_internalOffset));
+                    int payloadBytes = TlsFrameHelper.GetFrameSize(_internalBuffer.AsSpan(_internalOffset));
                     if (payloadBytes < 0)
                     {
                         throw new IOException(SR.net_frame_read_size);
@@ -913,6 +952,7 @@ namespace System.Net.Security
                     Buffer.BlockCopy(saved, 0, buffer, 0, copyCount);
                 }
             }
+
             return buffer;
         }
 
@@ -1003,8 +1043,8 @@ namespace System.Net.Security
             }
 
             // If the first byte is SSL3 HandShake, then check if we have a SSLv3 Type3 client hello.
-            if (bytes[0] == (byte)FrameType.Handshake || bytes[0] == (byte)FrameType.AppData
-                || bytes[0] == (byte)FrameType.Alert)
+            if (bytes[0] == (byte)TlsContentType.Handshake || bytes[0] == (byte)TlsContentType.AppData
+                || bytes[0] == (byte)TlsContentType.Alert)
             {
                 if (bytes.Length < 3)
                 {
index b72e549..3cf26e7 100644 (file)
@@ -237,9 +237,8 @@ namespace System.Net.Security
                     sslContext = new SafeDeleteSslContext((credential as SafeFreeSslCredentials)!, sslAuthenticationOptions);
                     context = sslContext;
 
-                    if (!string.IsNullOrEmpty(sslAuthenticationOptions.TargetHost))
+                    if (!string.IsNullOrEmpty(sslAuthenticationOptions.TargetHost) && !sslAuthenticationOptions.IsServer)
                     {
-                        Debug.Assert(!sslAuthenticationOptions.IsServer, "targetName should not be set for server-side handshakes");
                         Interop.AppleCrypto.SslSetTargetName(sslContext.SslContext, sslAuthenticationOptions.TargetHost);
                     }
 
diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/TlsFrameHelper.cs b/src/libraries/System.Net.Security/src/System/Net/Security/TlsFrameHelper.cs
new file mode 100644 (file)
index 0000000..ec18be1
--- /dev/null
@@ -0,0 +1,235 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Diagnostics;
+using System.Security.Authentication;
+
+namespace System.Net.Security
+{
+    // SSL3/TLS protocol frames definitions.
+    internal enum TlsContentType : byte
+    {
+        ChangeCipherSpec = 20,
+        Alert = 21,
+        Handshake = 22,
+        AppData = 23
+    }
+
+    internal enum TlsHandshakeType : byte
+    {
+        HelloRequest = 0,
+        ClientHello = 1,
+        ServerHello = 2,
+        NewSessionTicket = 4,
+        EndOfEarlyData = 5,
+        EncryptedExtensions = 8,
+        Certificate = 11,
+        ServerKeyExchange = 12,
+        CertificateRequest = 13,
+        ServerHelloDone = 14,
+        CertificateVerify = 15,
+        ClientKeyExchange = 16,
+        Finished = 20,
+        KeyEpdate = 24,
+        MessageHash = 254
+    }
+
+    internal enum TlsAlertLevel : byte
+    {
+        Warning = 1,
+        Fatal = 2,
+    }
+
+    internal enum TlsAlertDescription : byte
+    {
+        CloseNotify = 0, // warning
+        UnexpectedMessage = 10, // error
+        BadRecordMac = 20, // error
+        DecryptionFailed = 21, // reserved
+        RecordOverflow = 22, // error
+        DecompressionFail = 30, // error
+        HandshakeFailure = 40, // error
+        BadCertificate = 42, // warning or error
+        UnsupportedCert = 43, // warning or error
+        CertificateRevoked = 44, // warning or error
+        CertificateExpired = 45, // warning or error
+        CertificateUnknown = 46, // warning or error
+        IllegalParameter = 47, // error
+        UnknownCA = 48, // error
+        AccessDenied = 49, // error
+        DecodeError = 50, // error
+        DecryptError = 51, // error
+        ExportRestriction = 60, // reserved
+        ProtocolVersion = 70, // error
+        InsuffientSecurity = 71, // error
+        InternalError = 80, // error
+        UserCanceled = 90, // warning or error
+        NoRenegotiation = 100, // warning
+        UnsupportedExt = 110, // error
+    }
+
+    internal struct TlsFrameHeader
+    {
+        public TlsContentType Type;
+        public SslProtocols Version;
+        public int Length;
+    }
+
+    internal struct TlsFrameHandshakeInfo
+    {
+        public TlsFrameHeader Header;
+        public TlsHandshakeType HandshakeType;
+        public SslProtocols SupportedVersions;
+        public string? TargetName;
+    }
+
+    internal class TlsFrameHelper
+    {
+        public const int HeaderSize = 5;
+
+        private static byte[] s_protocolMismatch13 = new byte[] { (byte)TlsContentType.Alert, 3, 4, 0, 2, 2, 70 };
+        private static byte[] s_protocolMismatch12 = new byte[] { (byte)TlsContentType.Alert, 3, 3, 0, 2, 2, 70 };
+        private static byte[] s_protocolMismatch11 = new byte[] { (byte)TlsContentType.Alert, 3, 2, 0, 2, 2, 70 };
+        private static byte[] s_protocolMismatch10 = new byte[] { (byte)TlsContentType.Alert, 3, 1, 0, 2, 2, 70 };
+
+        public static bool TryGetFrameHeader(ReadOnlySpan<byte> frame, ref TlsFrameHeader header)
+        {
+            bool result = frame.Length > 4;
+
+            if (frame.Length >= 1)
+            {
+                header.Type = (TlsContentType)frame[0];
+
+                if (frame.Length >= 3)
+                {
+                    // SSLv3, TLS or later
+                    if (frame[1] == 3)
+                    {
+                        if (frame.Length > 4)
+                        {
+                            header.Length = ((frame[3] << 8) | frame[4]);
+                        }
+
+                        switch (frame[2])
+                        {
+                            case 4:
+                                header.Version = SslProtocols.Tls13;
+                                break;
+                            case 3:
+                                header.Version = SslProtocols.Tls12;
+                                break;
+                            case 2:
+                                header.Version = SslProtocols.Tls11;
+                                break;
+                            case 1:
+                                header.Version = SslProtocols.Tls;
+                                break;
+                            case 0:
+#pragma warning disable 0618
+                                header.Version = SslProtocols.Ssl3;
+#pragma warning restore 0618
+                                break;
+                            default:
+                                header.Version = SslProtocols.None;
+                                break;
+                        }
+                    }
+                    else
+                    {
+                        header.Length = -1;
+                        header.Version = SslProtocols.None;
+                    }
+                }
+            }
+
+            return result;
+        }
+
+        // Returns frame size e.g. header + content
+        public static int GetFrameSize(ReadOnlySpan<byte> frame)
+        {
+            if (frame.Length < 5 || frame[1] < 3)
+            {
+                return - 1;
+            }
+
+            return ((frame[3] << 8) | frame[4]) + HeaderSize;
+        }
+
+        public static bool TryGetHandshakeInfo(ReadOnlySpan<byte> frame, ref TlsFrameHandshakeInfo info)
+        {
+            if (frame.Length < 6 || frame[0] != (byte)TlsContentType.Handshake)
+            {
+                return false;
+            }
+
+            // This will not fail since we have enough data.
+            bool gotHeader = TryGetFrameHeader(frame, ref info.Header);
+            Debug.Assert(gotHeader);
+
+            info.SupportedVersions = info.Header.Version;
+
+            info.HandshakeType = (TlsHandshakeType)frame[5];
+
+            if (info.HandshakeType == TlsHandshakeType.ClientHello)
+            {
+                info.TargetName = SniHelper.GetServerName(frame);
+            }
+
+            return true;
+        }
+
+        public static bool TryGetAlertInfo(ReadOnlySpan<byte> frame, ref TlsAlertLevel level, ref TlsAlertDescription description)
+        {
+            if (frame.Length < 7 || frame[0] != (byte)TlsContentType.Alert)
+            {
+                return false;
+            }
+
+            level = (TlsAlertLevel)frame[5];
+            description = (TlsAlertDescription)frame[6];
+
+            return true;
+        }
+
+        private static byte[] CreateProtocolVersionAlert(SslProtocols version) =>
+            version switch
+            {
+                SslProtocols.Tls13 => s_protocolMismatch13,
+                SslProtocols.Tls12 => s_protocolMismatch12,
+                SslProtocols.Tls11 => s_protocolMismatch11,
+                SslProtocols.Tls => s_protocolMismatch10,
+                _ => Array.Empty<byte>(),
+            };
+
+        public static byte[] CreateAlertFrame(SslProtocols version, TlsAlertDescription reason)
+        {
+            if (reason == TlsAlertDescription.ProtocolVersion)
+            {
+                return CreateProtocolVersionAlert(version);
+            }
+            else if ((int)version > (int)SslProtocols.Tls)
+            {
+                // Create TLS1.2 alert
+                byte[] buffer = new byte[] { (byte)TlsContentType.Alert, 3, 3, 0, 2, 2, (byte)reason };
+                switch (version)
+                {
+                    case SslProtocols.Tls13:
+                        buffer[2] = 4;
+                        break;
+                    case SslProtocols.Tls11:
+                        buffer[2] = 2;
+                        break;
+                    case SslProtocols.Tls:
+                        buffer[2] = 1;
+                        break;
+                }
+
+                return buffer;
+            }
+
+            return Array.Empty<byte>();
+        }
+    }
+}
index 69c9673..5c22719 100644 (file)
@@ -44,11 +44,7 @@ namespace System.Net.Security.Tests
         public async Task ClientAsyncAuthenticate_ServerNoEncryption_NoConnect()
         {
             // Don't use Tls13 since we are trying to use NullEncryption
-            Type expectedExceptionType = TestConfiguration.SupportsHandshakeAlerts && TestConfiguration.SupportsNullEncryption ?
-                typeof(AuthenticationException) :
-                typeof(IOException);
-
-            await Assert.ThrowsAsync(expectedExceptionType,
+            await Assert.ThrowsAsync<AuthenticationException>(
                 () => ClientAsyncSslHelper(
                     EncryptionPolicy.NoEncryption,
                     SslProtocolSupport.DefaultSslProtocols,  SslProtocols.Tls | SslProtocols.Tls11 |  SslProtocols.Tls12 ));
@@ -120,12 +116,12 @@ namespace System.Net.Security.Tests
             yield return new object[] { SslProtocols.Ssl2, SslProtocols.Tls12, typeof(Exception) };
             yield return new object[] { SslProtocols.Ssl3, SslProtocols.Tls12, typeof(Exception) };
 #pragma warning restore 0618
-            yield return new object[] { SslProtocols.Tls, SslProtocols.Tls11, TestConfiguration.SupportsVersionAlerts ? typeof(AuthenticationException) : typeof(IOException) };
-            yield return new object[] { SslProtocols.Tls, SslProtocols.Tls12, TestConfiguration.SupportsVersionAlerts ? typeof(AuthenticationException) : typeof(IOException) };
+            yield return new object[] { SslProtocols.Tls, SslProtocols.Tls11, typeof(AuthenticationException) };
+            yield return new object[] { SslProtocols.Tls, SslProtocols.Tls12, typeof(AuthenticationException) };
             yield return new object[] { SslProtocols.Tls11, SslProtocols.Tls, typeof(AuthenticationException) };
             yield return new object[] { SslProtocols.Tls12, SslProtocols.Tls, typeof(AuthenticationException) };
             yield return new object[] { SslProtocols.Tls12, SslProtocols.Tls11, typeof(AuthenticationException) };
-            yield return new object[] { SslProtocols.Tls11, SslProtocols.Tls12, TestConfiguration.SupportsVersionAlerts ? typeof(AuthenticationException) : typeof(IOException) };
+            yield return new object[] { SslProtocols.Tls11, SslProtocols.Tls12, typeof(AuthenticationException) };
         }
 
         #region Helpers
index 4a57ca9..ed3a2c6 100644 (file)
@@ -84,7 +84,7 @@ namespace System.Net.Security.Tests
 
                 using (var sslStream = new SslStream(client.GetStream(), false, AllowAnyServerCertificate, null))
                 {
-                    await Assert.ThrowsAsync(TestConfiguration.SupportsHandshakeAlerts ? typeof(AuthenticationException) : typeof(IOException), () =>
+                    await Assert.ThrowsAsync<AuthenticationException>(() =>
                         sslStream.AuthenticateAsClientAsync("localhost", null, SslProtocolSupport.DefaultSslProtocols, false));
                 }
             }
index 5ecb21d..9bf0afb 100644 (file)
@@ -44,7 +44,6 @@ namespace System.Net.Security.Tests
 
         [Theory]
         [MemberData(nameof(ProtocolMismatchData))]
-        [ActiveIssue("https://github.com/dotnet/runtime/issues/29642")]
         public async Task ServerAsyncAuthenticate_MismatchProtocols_Fails(
             SslProtocols serverProtocol,
             SslProtocols clientProtocol,
@@ -80,10 +79,10 @@ namespace System.Net.Security.Tests
 #pragma warning restore 0618
             yield return new object[] { SslProtocols.Tls, SslProtocols.Tls11, typeof(AuthenticationException) };
             yield return new object[] { SslProtocols.Tls, SslProtocols.Tls12, typeof(AuthenticationException) };
-            yield return new object[] { SslProtocols.Tls11, SslProtocols.Tls, TestConfiguration.SupportsVersionAlerts ? typeof(AuthenticationException) : typeof(TimeoutException) };
+            yield return new object[] { SslProtocols.Tls11, SslProtocols.Tls, typeof(AuthenticationException) };
             yield return new object[] { SslProtocols.Tls11, SslProtocols.Tls12, typeof(AuthenticationException) };
-            yield return new object[] { SslProtocols.Tls12, SslProtocols.Tls, TestConfiguration.SupportsVersionAlerts ? typeof(AuthenticationException) : typeof(TimeoutException) };
-            yield return new object[] { SslProtocols.Tls12, SslProtocols.Tls11, TestConfiguration.SupportsVersionAlerts ? typeof(AuthenticationException) : typeof(TimeoutException) };
+            yield return new object[] { SslProtocols.Tls12, SslProtocols.Tls, typeof(AuthenticationException) };
+            yield return new object[] { SslProtocols.Tls12, SslProtocols.Tls11, typeof(AuthenticationException) };
         }
 
         #region Helpers
index 65efb78..d062fac 100644 (file)
@@ -43,7 +43,7 @@ namespace System.Net.Security.Tests
 
                 using (var sslStream = new SslStream(client.GetStream(), false, AllowAnyServerCertificate, null, EncryptionPolicy.RequireEncryption))
                 {
-                    await Assert.ThrowsAsync(TestConfiguration.SupportsHandshakeAlerts ? typeof(AuthenticationException) : typeof(IOException), () =>
+                    await Assert.ThrowsAsync<AuthenticationException>(() =>
                         sslStream.AuthenticateAsClientAsync("localhost", null, SslProtocolSupport.DefaultSslProtocols, false));
                 }
             }
index a546241..b98a9ea 100644 (file)
@@ -3,6 +3,7 @@
 // See the LICENSE file in the project root for more information.
 
 using System.Collections.Generic;
+using System.IO;
 using System.Net.Test.Common;
 using System.Security.Authentication;
 using System.Security.Cryptography.X509Certificates;
@@ -201,9 +202,7 @@ namespace System.Net.Security.Tests
 
         private async Task WithVirtualConnection(Func<SslStream, SslStream, Task> serverClientConnection, RemoteCertificateValidationCallback clientCertValidate)
         {
-            VirtualNetwork vn = new VirtualNetwork();
-            using (VirtualNetworkStream serverStream = new VirtualNetworkStream(vn, isServer: true),
-                                        clientStream = new VirtualNetworkStream(vn, isServer: false))
+            (Stream clientStream, Stream serverStream) = TestHelper.GetConnectedStreams();
             using (SslStream server = new SslStream(serverStream, leaveInnerStreamOpen: false),
                              client = new SslStream(clientStream, leaveInnerStreamOpen: false, clientCertValidate))
             {
index 4943401..bc704fa 100644 (file)
@@ -2,6 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
+using System.IO;
 using System.Net.Http;
 using System.Net.Test.Common;
 using System.Security.Cryptography.X509Certificates;
@@ -21,10 +22,7 @@ namespace System.Net.Security.Tests
 
         public SslStreamSystemDefaultTest()
         {
-            var network = new VirtualNetwork();
-            var clientNet = new VirtualNetworkStream(network, isServer:false);
-            var serverNet = new VirtualNetworkStream(network, isServer: true);
-
+            (Stream clientNet, Stream serverNet) = TestHelper.GetConnectedTcpStreams();
             _clientStream = new SslStream(clientNet, false, ClientCertCallback);
             _serverStream = new SslStream(serverNet, false, ServerCertCallback);
         }
index 8a245e8..4ad9475 100644 (file)
@@ -66,6 +66,8 @@
              Link="ProductionCode\Common\System\Threading\Tasks\TaskToApm.cs" />
     <Compile Include="..\..\src\System\Net\Security\SniHelper.cs"
              Link="src\SniHelper.cs" />
+    <Compile Include="..\..\src\System\Net\Security\TlsFrameHelper.cs"
+             Link="src\TlsFrameHelper.cs" />
     <Compile Include="SniHelperTest.cs" />
     <Compile Include="SslAuthenticationOptionsTest.cs" />
     <Compile Include="SslStreamAlertsTest.cs" />
index 9769590..577d2ce 100644 (file)
@@ -32,9 +32,7 @@ namespace System.Net.Security.Tests
 
         public static bool SupportsHandshakeAlerts { get { return RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.Windows); } }
 
-        public static bool SupportsAlpnAlerts { get { return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)  && PlatformDetection.OpenSslVersion.CompareTo(new Version(1,1,0)) >= 0); } }
-
-        public static bool SupportsVersionAlerts { get { return RuntimeInformation.IsOSPlatform(OSPlatform.Linux)  && PlatformDetection.OpenSslVersion.CompareTo(new Version(1,1,0)) >= 0; } }
+        public static bool SupportsAlpnAlerts { get { return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && PlatformDetection.OpenSslVersion.CompareTo(new Version(1,0,2)) >= 0); } }
 
         public static Task WhenAllOrAnyFailedWithTimeout(params Task[] tasks)
             => tasks.WhenAllOrAnyFailed(PassingTestTimeoutMilliseconds);