< Summary

Line coverage
8%
Covered lines: 178
Uncovered lines: 1947
Coverable lines: 2125
Total lines: 3989
Line coverage: 8.3%
Branch coverage
6%
Covered branches: 53
Total branches: 831
Branch coverage: 6.3%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
File 1: .cctor()100%11100%
File 1: .ctor(...)50%8881.81%
File 1: .ctor()100%11100%
File 1: DecryptedReadOnlySpanSliced(...)100%110%
File 1: EncryptedSpanSliced(...)100%110%
File 1: Commit(...)100%11100%
File 1: EnsureAvailableSpace(...)100%11100%
File 1: Discard(...)0%220%
File 1: DiscardEncrypted(...)100%110%
File 1: OnDecrypted(...)0%220%
File 1: ReturnBuffer()100%11100%
File 1: .ctor(...)100%11100%
File 1: .ctor(...)100%110%
File 1: .ctor(...)100%110%
File 1: .ctor(...)100%11100%
File 1: BeginAuthenticateAsClient(...)100%110%
File 1: BeginAuthenticateAsClient(...)100%110%
File 1: BeginAuthenticateAsClient(...)0%220%
File 1: BeginAuthenticateAsClient(...)100%110%
File 1: EndAuthenticateAsClient(...)100%110%
File 1: BeginAuthenticateAsServer(...)100%110%
File 1: BeginAuthenticateAsServer(...)100%110%
File 1: BeginAuthenticateAsServer(...)0%220%
File 1: BeginAuthenticateAsServer(...)100%110%
File 1: EndAuthenticateAsServer(...)100%110%
File 1: AuthenticateAsClient(...)100%110%
File 1: AuthenticateAsClient(...)100%110%
File 1: AuthenticateAsClient(...)0%220%
File 1: AuthenticateAsClient(...)100%110%
File 1: AuthenticateAsServer(...)100%110%
File 1: AuthenticateAsServer(...)100%110%
File 1: AuthenticateAsServer(...)0%220%
File 1: AuthenticateAsServer(...)100%110%
File 1: AuthenticateAsClientAsync(...)100%110%
File 1: AuthenticateAsClientAsync(...)100%110%
File 1: AuthenticateAsClientAsync(...)0%220%
File 1: AuthenticateAsClientAsync(...)100%110%
File 1: AuthenticateAsServerAsync(...)100%110%
File 1: AuthenticateAsServerAsync(...)0%220%
File 1: AuthenticateAsServerAsync(...)0%220%
File 1: AuthenticateAsServerAsync(...)100%110%
File 1: AuthenticateAsServerAsync(...)100%11100%
File 1: ShutdownAsync()0%440%
File 1: GetSslProtocolInternal()0%14140%
File 1: SetLength(...)100%110%
File 1: Seek(...)100%110%
File 1: Flush()100%110%
File 1: FlushAsync(...)100%110%
File 1: NegotiateClientCertificateAsync(...)0%220%
File 1: Dispose(...)100%11100%
File 1: DisposeAsync()0%440%
File 1: RentPointerMemoryManager(...)0%220%
File 1: ReturnPointerMemoryManager(...)100%110%
File 1: ReadByte()0%880%
File 1: Read(...)100%110%
File 1: Read(...)100%110%
File 1: WriteByte(...)100%110%
File 1: Write(...)100%110%
File 1: Write(...)100%110%
File 1: Write(...)100%110%
File 1: BeginRead(...)100%110%
File 1: EndRead(...)100%110%
File 1: BeginWrite(...)100%110%
File 1: EndWrite(...)100%110%
File 1: WriteAsync(...)100%110%
File 1: WriteAsync(...)100%110%
File 1: ReadAsync(...)100%110%
File 1: ReadAsync(...)100%110%
File 1: ThrowIfExceptional()50%2257.14%
File 1: ThrowExceptional(System.Runtime.ExceptionServices.ExceptionDispatchInfo)100%110%
File 1: ThrowIfExceptionalOrNotAuthenticated()0%220%
File 1: ThrowIfExceptionalOrNotHandshake()0%220%
File 1: ThrowIfExceptionalOrNotAuthenticatedOrShutdown()0%440%
File 1: ThrowAlreadyShutdown()100%110%
File 1: ThrowNotAuthenticated()100%110%
File 1: Dispose(...)100%110%
File 1: Reset(...)100%110%
File 1: GetSpan()100%110%
File 1: Pin(...)100%110%
File 1: Unpin()100%110%
File 2: .ctor(...)100%11100%
File 2: SetException(...)0%220%
File 2: CloseInternal()40%101070%
File 2: ProcessAuthenticationAsync(...)50%4480%
File 2: ProcessAuthenticationWithTelemetryAsync(...)0%880%
File 2: ReplyOnReAuthenticationAsync(...)100%110%
File 2: RenegotiateAsync(...)0%20200%
File 2: ForceAuthenticationAsync(...)15.38%525227.65%
File 2: ReceiveHandshakeFrameAsync(...)25.92%545424.63%
File 2: ProcessTlsFrame(...)0%14140%
File 2: SendAuthResetSignal(...)0%220%
File 2: CompleteHandshake(...)0%880%
File 2: CompleteHandshake(...)0%220%
File 2: CreateCertificateValidationException(...)0%660%
File 2: WriteAsyncChunked(...)0%220%
File 2: WriteSingleChunk(...)0%10100%
File 2: WaitAndWriteAsync(System.ReadOnlyMemory`1<System.Byte>,System.Threading.Tasks.Task,System.Threading.CancellationToken)0%440%
File 2: CompleteWriteAsync(System.Threading.Tasks.ValueTask,System.Net.Security.ProtocolToken)100%110%
File 2: Finalize()100%110%
File 2: ReturnReadBufferIfEmpty()0%220%
File 2: HaveFullTlsFrame(...)100%11100%
File 2: EnsureFullTlsFrameAsync()71.42%141488.88%
File 2: ReadAsyncInternal()0%48480%
File 2: WriteAsyncInternal(...)0%14140%
File 2: CopyDecryptedData(...)0%220%
File 2: GetFrameSize(...)83.33%66100%
File 3: .ctor(...)100%11100%
File 3: .cctor()100%11100%
File 3: GetChannelBinding(...)0%220%
File 3: CloseContext()50%88100%
File 3: ReleaseCachedCredentials()50%22100%
File 3: FindCertificateWithPrivateKey(...)0%20200%
File 3: MakeEx(...)0%440%
File 3: GetRequestCertificateAuthorities()0%220%
File 3: SelectClientCertificate()0%94940%
File 3: AcquireClientCredentials(...)0%22220%
File 3: UpdateCertificateContext(System.Security.Cryptography.X509Certificates.X509Certificate2)0%440%
File 3: EnsureInitialized(...)0%220%
File 3: AcquireServerCredentials(...)0%30300%
File 3: AcquireCredentialsHandle(...)0%660%
File 3: GetExpiryTimestamp(System.Net.Security.SslStreamCertificateContext)0%440%
File 3: NextMessage(...)0%12120%
File 3: GenerateToken(...)0%36360%
File 3: Renegotiate()100%110%
File 3: ProcessHandshakeSuccess()0%220%
File 3: EncryptData(...)0%880%
File 3: DecryptData(...)0%16160%
File 3: VerifyRemoteCertificate(...)0%14140%
File 3: VerifyRemoteCertificateCore(...)0%62620%
File 3: CreateFatalHandshakeAlertToken(...)0%13130%
File 3: CreateShutdownToken()0%660%
File 3: GenerateAlertToken()100%110%
File 3: GetAlertMessageFromChain(...)0%14140%
File 3: LogCertificateValidation(...)0%18180%
File 4: EnsureTlsSession()0%220%
File 4: TryNextMessageViaTlsSession(...)0%30300%

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Buffers;
 5using System.Diagnostics;
 6using System.IO;
 7using System.Runtime.CompilerServices;
 8using System.Runtime.ExceptionServices;
 9using System.Runtime.InteropServices;
 10using System.Runtime.Versioning;
 11using System.Security.Authentication;
 12using System.Security.Cryptography.X509Certificates;
 13using System.Threading;
 14using System.Threading.Tasks;
 15
 16namespace System.Net.Security
 17{
 18    public enum EncryptionPolicy
 19    {
 20        // Prohibit null ciphers (current system defaults)
 21        RequireEncryption = 0,
 22
 23        // Add null ciphers to current system defaults
 24        [System.ObsoleteAttribute(Obsoletions.EncryptionPolicyMessage, DiagnosticId = Obsoletions.EncryptionPolicyDiagId
 25        AllowNoEncryption,
 26
 27        // Request null ciphers only
 28        [System.ObsoleteAttribute(Obsoletions.EncryptionPolicyMessage, DiagnosticId = Obsoletions.EncryptionPolicyDiagId
 29        NoEncryption
 30    }
 31
 32    // A user delegate used to verify remote SSL certificate.
 33    public delegate bool RemoteCertificateValidationCallback(object sender, X509Certificate? certificate, X509Chain? cha
 34
 35    // A user delegate used to select local SSL certificate.
 36    public delegate X509Certificate? LocalCertificateSelectionCallback(object sender, string targetHost, X509Certificate
 37
 38    public delegate X509Certificate ServerCertificateSelectionCallback(object sender, string? hostName);
 39
 40    public delegate ValueTask<SslServerAuthenticationOptions> ServerOptionsSelectionCallback(SslStream stream, SslClient
 41
 42    public partial class SslStream : AuthenticatedStream
 43    {
 44        /// <summary>Set as the _exception when the instance is disposed.</summary>
 145        private static readonly ExceptionDispatchInfo s_disposedSentinel = ExceptionDispatchInfo.Capture(new ObjectDispo
 46
 47        private ExceptionDispatchInfo? _exception;
 48        private bool _shutdown;
 49        private bool _handshakeCompleted;
 50
 51        // FrameOverhead = 5 byte header + HMAC trailer + padding (if block cipher)
 52        // HMAC: 32 bytes for SHA-256 or 20 bytes for SHA-1 or 16 bytes for the MD5
 53        private const int FrameOverhead = 64;
 54        private const int InitialHandshakeBufferSize = 4096 + FrameOverhead; // try to fit at least 4K ServerCertificate
 55        private const int ReadBufferSize = 4096 * 4 + FrameOverhead;         // We read in 16K chunks + headers.
 56
 3157        private SslBuffer _buffer = new();
 58
 59        // internal buffer for storing incoming data. Wrapper around ArrayBuffer which adds
 60        // separation between decrypted and still encrypted part of the active region.
 61        //   - Encrypted: Contains incoming TLS frames, the last such frame may be incomplete
 62        //   - Decrypted: Contains decrypted data from *one* TLS frame which have not been read by the user yet.
 63        private struct SslBuffer
 64        {
 65            private ArrayBuffer _buffer;
 66            private int _decryptedLength;
 67
 68            // padding between decrypted part of the active memory and following undecrypted TLS frame.
 69            private int _decryptedPadding;
 70
 71            // Indicates whether the _buffer currently holds a rented buffer.
 72            private bool _isValid;
 73
 74            public SslBuffer()
 3175            {
 3176                _buffer = new ArrayBuffer(initialSize: 0, usePool: true);
 3177                _decryptedLength = 0;
 3178                _decryptedPadding = 0;
 3179                _isValid = false;
 3180            }
 81
 3182            public bool IsValid => _isValid;
 83
 084            public Span<byte> DecryptedSpan => _buffer.ActiveSpan.Slice(0, _decryptedLength);
 85
 86            public ReadOnlySpan<byte> DecryptedReadOnlySpanSliced(int length)
 087            {
 088                Debug.Assert(length <= DecryptedLength);
 089                return _buffer.ActiveSpan.Slice(0, length);
 090            }
 91
 092            public int DecryptedLength => _decryptedLength;
 93
 094            public int ActiveLength => _buffer.ActiveLength;
 95
 096            public Span<byte> EncryptedSpanSliced(int length) => _buffer.ActiveSpan.Slice(_decryptedLength + _decryptedP
 97
 6898            public ReadOnlySpan<byte> EncryptedReadOnlySpan => _buffer.ActiveSpan.Slice(_decryptedLength + _decryptedPad
 99
 153100            public int EncryptedLength => _buffer.ActiveLength - _decryptedPadding - _decryptedLength;
 101
 45102            public Memory<byte> AvailableMemory => _buffer.AvailableMemory;
 103
 45104            public int AvailableLength => _buffer.AvailableLength;
 105
 106            public int Capacity => _buffer.Capacity;
 107
 31108            public void Commit(int byteCount) => _buffer.Commit(byteCount);
 109
 110            public void EnsureAvailableSpace(int byteCount)
 85111            {
 85112                _isValid = true;
 85113                _buffer.EnsureAvailableSpace(byteCount);
 85114            }
 115
 116            public void Discard(int byteCount)
 0117            {
 0118                Debug.Assert(byteCount <= _decryptedLength, "byteCount <= _decryptedBytes");
 119
 0120                _buffer.Discard(byteCount);
 0121                _decryptedLength -= byteCount;
 122
 123                // if drained all decrypted data, discard also the tail of the frame so that only
 124                // encrypted part of the active memory of the _buffer remains
 0125                if (_decryptedLength == 0)
 0126                {
 0127                    _buffer.Discard(_decryptedPadding);
 0128                    _decryptedPadding = 0;
 0129                }
 0130            }
 131
 132            public void DiscardEncrypted(int byteCount)
 0133            {
 134                // should be called only during handshake -> no pending decrypted data
 0135                Debug.Assert(_decryptedLength == 0, "_decryptedBytes == 0");
 0136                Debug.Assert(_decryptedPadding == 0, "_encryptedOffset == 0");
 137
 0138                _buffer.Discard(byteCount);
 0139            }
 140
 141            public void OnDecrypted(int decryptedOffset, int decryptedCount, int frameSize)
 0142            {
 0143                Debug.Assert(_decryptedLength == 0, "_decryptedBytes == 0");
 0144                Debug.Assert(_decryptedPadding == 0, "_encryptedOffset == 0");
 145
 0146                if (decryptedCount > 0)
 0147                {
 148                    // discard padding before decrypted contents
 0149                    _buffer.Discard(decryptedOffset);
 150
 0151                    _decryptedPadding = frameSize - decryptedOffset - decryptedCount;
 0152                    _decryptedLength = decryptedCount;
 0153                }
 154                else
 0155                {
 156                    // No user data available, discard entire frame
 0157                    _buffer.Discard(frameSize);
 0158                }
 0159            }
 160
 161            public void ReturnBuffer()
 31162            {
 31163                _buffer.ClearAndReturnBuffer();
 31164                _decryptedLength = 0;
 31165                _decryptedPadding = 0;
 31166                _isValid = false;
 31167            }
 168        }
 169
 170        private enum NestedState
 171        {
 172            StreamNotInUse = 0,
 173            StreamInUse = 1,
 174            StreamDisposed = 2,
 175        }
 176
 177        private NestedState _nestedWrite;
 178        private NestedState _nestedRead;
 179
 180        private PoolingPointerMemoryManager? _readPointerMemoryManager;
 181        private PoolingPointerMemoryManager? _writePointerMemoryManager;
 182
 183        public SslStream(Stream innerStream)
 31184                : this(innerStream, false, null, null)
 31185        {
 31186        }
 187
 188        public SslStream(Stream innerStream, bool leaveInnerStreamOpen)
 0189                : this(innerStream, leaveInnerStreamOpen, null, null, EncryptionPolicy.RequireEncryption)
 0190        {
 0191        }
 192
 193        public SslStream(Stream innerStream, bool leaveInnerStreamOpen, RemoteCertificateValidationCallback? userCertifi
 0194                : this(innerStream, leaveInnerStreamOpen, userCertificateValidationCallback, null, EncryptionPolicy.Requ
 0195        {
 0196        }
 197
 198        public SslStream(Stream innerStream, bool leaveInnerStreamOpen, RemoteCertificateValidationCallback? userCertifi
 199            LocalCertificateSelectionCallback? userCertificateSelectionCallback)
 31200                : this(innerStream, leaveInnerStreamOpen, userCertificateValidationCallback, userCertificateSelectionCal
 31201        {
 31202        }
 203
 204        public SslStream(Stream innerStream, bool leaveInnerStreamOpen, RemoteCertificateValidationCallback? userCertifi
 205            LocalCertificateSelectionCallback? userCertificateSelectionCallback, EncryptionPolicy encryptionPolicy)
 31206            : base(innerStream, leaveInnerStreamOpen)
 31207        {
 208#pragma warning disable SYSLIB0040 // NoEncryption and AllowNoEncryption are obsolete
 31209            if (encryptionPolicy != EncryptionPolicy.RequireEncryption && encryptionPolicy != EncryptionPolicy.AllowNoEn
 0210            {
 0211                throw new ArgumentException(SR.Format(SR.net_invalid_enum, "EncryptionPolicy"), nameof(encryptionPolicy)
 212            }
 213#pragma warning restore SYSLIB0040
 214
 31215            _sslAuthenticationOptions.EncryptionPolicy = encryptionPolicy;
 31216            _sslAuthenticationOptions.CertValidationDelegate = userCertificateValidationCallback;
 31217            _sslAuthenticationOptions.CertSelectionDelegate = userCertificateSelectionCallback;
 218
 219#if TARGET_ANDROID
 220            _sslAuthenticationOptions.SslStreamProxy = new SslStream.JavaProxy(sslStream: this);
 221#endif
 222
 223#if !TARGET_WINDOWS && !SYSNETSECURITY_NO_OPENSSL
 224            _sslAuthenticationOptions.SslStream = this;
 225            _sslAuthenticationOptions.RemoteCertificateValidator = VerifyRemoteCertificate;
 226#endif
 227
 31228            if (NetEventSource.Log.IsEnabled()) NetEventSource.Log.SslStreamCtor(this, innerStream);
 31229        }
 230
 231        //
 232        // Client side auth.
 233        //
 234        public virtual IAsyncResult BeginAuthenticateAsClient(string targetHost, AsyncCallback? asyncCallback, object? a
 0235        {
 0236            return BeginAuthenticateAsClient(targetHost, null, SslProtocols.None, false,
 0237                                           asyncCallback, asyncState);
 0238        }
 239
 240        public virtual IAsyncResult BeginAuthenticateAsClient(string targetHost, X509CertificateCollection? clientCertif
 241                                                            bool checkCertificateRevocation, AsyncCallback? asyncCallbac
 0242        {
 0243            return BeginAuthenticateAsClient(targetHost, clientCertificates, SslProtocols.None, checkCertificateRevocati
 0244        }
 245
 246        public virtual IAsyncResult BeginAuthenticateAsClient(string targetHost, X509CertificateCollection? clientCertif
 247                                                            SslProtocols enabledSslProtocols, bool checkCertificateRevoc
 248                                                            AsyncCallback? asyncCallback, object? asyncState)
 0249        {
 0250            SslClientAuthenticationOptions options = new SslClientAuthenticationOptions
 0251            {
 0252                TargetHost = targetHost,
 0253                ClientCertificates = clientCertificates,
 0254                EnabledSslProtocols = enabledSslProtocols,
 0255                CertificateRevocationCheckMode = checkCertificateRevocation ? X509RevocationMode.Online : X509Revocation
 0256                EncryptionPolicy = _sslAuthenticationOptions.EncryptionPolicy,
 0257            };
 258
 0259            return BeginAuthenticateAsClient(options, CancellationToken.None, asyncCallback, asyncState);
 0260        }
 261
 262        internal IAsyncResult BeginAuthenticateAsClient(SslClientAuthenticationOptions sslClientAuthenticationOptions, C
 0263            TaskToAsyncResult.Begin(AuthenticateAsClientAsync(sslClientAuthenticationOptions, cancellationToken)!, async
 264
 0265        public virtual void EndAuthenticateAsClient(IAsyncResult asyncResult) => TaskToAsyncResult.End(asyncResult);
 266
 267        //
 268        // Server side auth.
 269        //
 270        public virtual IAsyncResult BeginAuthenticateAsServer(X509Certificate serverCertificate, AsyncCallback? asyncCal
 271
 0272        {
 0273            return BeginAuthenticateAsServer(serverCertificate, false, SslProtocols.None, false,
 0274                                                          asyncCallback,
 0275                                                          asyncState);
 0276        }
 277
 278        public virtual IAsyncResult BeginAuthenticateAsServer(X509Certificate serverCertificate, bool clientCertificateR
 279                                                            bool checkCertificateRevocation, AsyncCallback? asyncCallbac
 0280        {
 0281            return BeginAuthenticateAsServer(serverCertificate, clientCertificateRequired, SslProtocols.None, checkCerti
 0282        }
 283
 284        public virtual IAsyncResult BeginAuthenticateAsServer(X509Certificate serverCertificate, bool clientCertificateR
 285                                                            SslProtocols enabledSslProtocols, bool checkCertificateRevoc
 286                                                            AsyncCallback? asyncCallback,
 287                                                            object? asyncState)
 0288        {
 0289            SslServerAuthenticationOptions options = new SslServerAuthenticationOptions
 0290            {
 0291                ServerCertificate = serverCertificate,
 0292                ClientCertificateRequired = clientCertificateRequired,
 0293                EnabledSslProtocols = enabledSslProtocols,
 0294                CertificateRevocationCheckMode = checkCertificateRevocation ? X509RevocationMode.Online : X509Revocation
 0295                EncryptionPolicy = _sslAuthenticationOptions.EncryptionPolicy,
 0296            };
 297
 0298            return BeginAuthenticateAsServer(options, CancellationToken.None, asyncCallback, asyncState);
 0299        }
 300
 301        private IAsyncResult BeginAuthenticateAsServer(SslServerAuthenticationOptions sslServerAuthenticationOptions, Ca
 0302            TaskToAsyncResult.Begin(AuthenticateAsServerAsync(sslServerAuthenticationOptions, cancellationToken)!, async
 303
 0304        public virtual void EndAuthenticateAsServer(IAsyncResult asyncResult) => TaskToAsyncResult.End(asyncResult);
 305
 306        internal IAsyncResult BeginShutdown(AsyncCallback? asyncCallback, object? asyncState) => TaskToAsyncResult.Begin
 307
 308        internal static void EndShutdown(IAsyncResult asyncResult) => TaskToAsyncResult.End(asyncResult);
 309
 0310        public TransportContext TransportContext => new SslStreamContext(this);
 311
 312        #region Synchronous methods
 313        public virtual void AuthenticateAsClient(string targetHost)
 0314        {
 0315            AuthenticateAsClient(targetHost, null, SslProtocols.None, false);
 0316        }
 317
 318        public virtual void AuthenticateAsClient(string targetHost, X509CertificateCollection? clientCertificates, bool 
 0319        {
 0320            AuthenticateAsClient(targetHost, clientCertificates, SslProtocols.None, checkCertificateRevocation);
 0321        }
 322
 323        public virtual void AuthenticateAsClient(string targetHost, X509CertificateCollection? clientCertificates, SslPr
 0324        {
 0325            SslClientAuthenticationOptions options = new SslClientAuthenticationOptions
 0326            {
 0327                TargetHost = targetHost,
 0328                ClientCertificates = clientCertificates,
 0329                EnabledSslProtocols = enabledSslProtocols,
 0330                CertificateRevocationCheckMode = checkCertificateRevocation ? X509RevocationMode.Online : X509Revocation
 0331                EncryptionPolicy = _sslAuthenticationOptions.EncryptionPolicy,
 0332            };
 333
 0334            AuthenticateAsClient(options);
 0335        }
 336
 337        public void AuthenticateAsClient(SslClientAuthenticationOptions sslClientAuthenticationOptions)
 0338        {
 0339            ArgumentNullException.ThrowIfNull(sslClientAuthenticationOptions);
 340
 0341            ThrowIfExceptional();
 342
 0343            _sslAuthenticationOptions.UpdateOptions(sslClientAuthenticationOptions);
 0344            ProcessAuthenticationAsync().GetAwaiter().GetResult();
 0345        }
 346
 347        public virtual void AuthenticateAsServer(X509Certificate serverCertificate)
 0348        {
 0349            AuthenticateAsServer(serverCertificate, false, SslProtocols.None, false);
 0350        }
 351
 352        public virtual void AuthenticateAsServer(X509Certificate serverCertificate, bool clientCertificateRequired, bool
 0353        {
 0354            AuthenticateAsServer(serverCertificate, clientCertificateRequired, SslProtocols.None, checkCertificateRevoca
 0355        }
 356
 357        public virtual void AuthenticateAsServer(X509Certificate serverCertificate, bool clientCertificateRequired, SslP
 0358        {
 0359            SslServerAuthenticationOptions options = new SslServerAuthenticationOptions
 0360            {
 0361                ServerCertificate = serverCertificate,
 0362                ClientCertificateRequired = clientCertificateRequired,
 0363                EnabledSslProtocols = enabledSslProtocols,
 0364                CertificateRevocationCheckMode = checkCertificateRevocation ? X509RevocationMode.Online : X509Revocation
 0365                EncryptionPolicy = _sslAuthenticationOptions.EncryptionPolicy,
 0366            };
 367
 0368            AuthenticateAsServer(options);
 0369        }
 370
 371        public void AuthenticateAsServer(SslServerAuthenticationOptions sslServerAuthenticationOptions)
 0372        {
 0373            ArgumentNullException.ThrowIfNull(sslServerAuthenticationOptions);
 374
 0375            _sslAuthenticationOptions.UpdateOptions(sslServerAuthenticationOptions);
 0376            ProcessAuthenticationAsync().GetAwaiter().GetResult();
 0377        }
 378        #endregion
 379
 380        #region Task-based async public methods
 0381        public virtual Task AuthenticateAsClientAsync(string targetHost) => AuthenticateAsClientAsync(targetHost, null, 
 382
 0383        public virtual Task AuthenticateAsClientAsync(string targetHost, X509CertificateCollection? clientCertificates, 
 384
 385        public virtual Task AuthenticateAsClientAsync(string targetHost, X509CertificateCollection? clientCertificates, 
 0386        {
 0387            SslClientAuthenticationOptions options = new SslClientAuthenticationOptions()
 0388            {
 0389                TargetHost = targetHost,
 0390                ClientCertificates = clientCertificates,
 0391                EnabledSslProtocols = enabledSslProtocols,
 0392                CertificateRevocationCheckMode = checkCertificateRevocation ? X509RevocationMode.Online : X509Revocation
 0393                EncryptionPolicy = _sslAuthenticationOptions.EncryptionPolicy,
 0394            };
 395
 0396            return AuthenticateAsClientAsync(options);
 0397        }
 398
 399        public Task AuthenticateAsClientAsync(SslClientAuthenticationOptions sslClientAuthenticationOptions, Cancellatio
 0400        {
 0401            ArgumentNullException.ThrowIfNull(sslClientAuthenticationOptions);
 402
 0403            ThrowIfExceptional();
 0404            _sslAuthenticationOptions.UpdateOptions(sslClientAuthenticationOptions);
 0405            return ProcessAuthenticationAsync(isAsync: true, cancellationToken);
 0406        }
 407
 408        public virtual Task AuthenticateAsServerAsync(X509Certificate serverCertificate) =>
 0409            AuthenticateAsServerAsync(serverCertificate, false, SslProtocols.None, false);
 410
 411        public virtual Task AuthenticateAsServerAsync(X509Certificate serverCertificate, bool clientCertificateRequired,
 0412        {
 0413            SslServerAuthenticationOptions options = new SslServerAuthenticationOptions
 0414            {
 0415                ServerCertificate = serverCertificate,
 0416                ClientCertificateRequired = clientCertificateRequired,
 0417                CertificateRevocationCheckMode = checkCertificateRevocation ? X509RevocationMode.Online : X509Revocation
 0418                EncryptionPolicy = _sslAuthenticationOptions.EncryptionPolicy,
 0419            };
 420
 0421            return AuthenticateAsServerAsync(options);
 0422        }
 423
 424        public virtual Task AuthenticateAsServerAsync(X509Certificate serverCertificate, bool clientCertificateRequired,
 0425        {
 0426            SslServerAuthenticationOptions options = new SslServerAuthenticationOptions
 0427            {
 0428                ServerCertificate = serverCertificate,
 0429                ClientCertificateRequired = clientCertificateRequired,
 0430                EnabledSslProtocols = enabledSslProtocols,
 0431                CertificateRevocationCheckMode = checkCertificateRevocation ? X509RevocationMode.Online : X509Revocation
 0432                EncryptionPolicy = _sslAuthenticationOptions.EncryptionPolicy,
 0433            };
 434
 0435            return AuthenticateAsServerAsync(options);
 0436        }
 437
 438        public Task AuthenticateAsServerAsync(SslServerAuthenticationOptions sslServerAuthenticationOptions, Cancellatio
 0439        {
 0440            ArgumentNullException.ThrowIfNull(sslServerAuthenticationOptions);
 0441            _sslAuthenticationOptions.UpdateOptions(sslServerAuthenticationOptions);
 0442            return ProcessAuthenticationAsync(isAsync: true, cancellationToken);
 0443        }
 444
 445        public Task AuthenticateAsServerAsync(ServerOptionsSelectionCallback optionsCallback, object? state, Cancellatio
 31446        {
 31447            _sslAuthenticationOptions.UpdateOptions(optionsCallback, state);
 448
 31449            return ProcessAuthenticationAsync(isAsync: true, cancellationToken);
 31450        }
 451
 452        public virtual Task ShutdownAsync()
 0453        {
 0454            ThrowIfExceptionalOrNotAuthenticatedOrShutdown();
 455
 0456            ProtocolToken token = CreateShutdownToken();
 0457            _shutdown = true;
 0458            if (token.Size > 0 && token.Payload != null)
 0459            {
 0460                return InnerStream.WriteAsync(new ReadOnlyMemory<byte>(token.Payload, 0, token.Size), default).AsTask();
 461            }
 462
 0463            return Task.CompletedTask;
 0464        }
 465        #endregion
 466
 0467        public override bool IsAuthenticated => IsValidContext && _exception == null && _handshakeCompleted;
 468
 469        public override bool IsMutuallyAuthenticated
 470        {
 471            get
 0472            {
 0473                return
 0474                    IsAuthenticated &&
 0475                    (IsServer ? LocalServerCertificate : LocalClientCertificate) != null &&
 0476                    IsRemoteCertificateAvailable; /* does not work: Context.IsMutualAuthFlag;*/
 0477            }
 478        }
 479
 0480        public override bool IsEncrypted => IsAuthenticated;
 481
 0482        public override bool IsSigned => IsAuthenticated;
 483
 31484        public override bool IsServer => _sslAuthenticationOptions.IsServer;
 485
 486        public virtual SslProtocols SslProtocol
 487        {
 488            get
 0489            {
 0490                ThrowIfExceptionalOrNotHandshake();
 0491                return GetSslProtocolInternal();
 0492            }
 493        }
 494
 495        // Skips the ThrowIfExceptionalOrNotHandshake() check
 496        internal SslProtocols GetSslProtocolInternal()
 0497        {
 0498            if (_connectionInfo.Protocol == 0)
 0499            {
 0500                return SslProtocols.None;
 501            }
 502
 0503            SslProtocols proto = (SslProtocols)_connectionInfo.Protocol;
 0504            SslProtocols ret = SslProtocols.None;
 505
 506#pragma warning disable 0618 // Ssl2, Ssl3 are deprecated.
 507            // Restore client/server bits so the result maps exactly on published constants.
 0508            if ((proto & SslProtocols.Ssl2) != 0)
 0509            {
 0510                ret |= SslProtocols.Ssl2;
 0511            }
 512
 0513            if ((proto & SslProtocols.Ssl3) != 0)
 0514            {
 0515                ret |= SslProtocols.Ssl3;
 0516            }
 517#pragma warning restore
 518
 519#pragma warning disable SYSLIB0039 // TLS 1.0 and 1.1 are obsolete
 0520            if ((proto & SslProtocols.Tls) != 0)
 0521            {
 0522                ret |= SslProtocols.Tls;
 0523            }
 524
 0525            if ((proto & SslProtocols.Tls11) != 0)
 0526            {
 0527                ret |= SslProtocols.Tls11;
 0528            }
 529#pragma warning restore SYSLIB0039
 530
 0531            if ((proto & SslProtocols.Tls12) != 0)
 0532            {
 0533                ret |= SslProtocols.Tls12;
 0534            }
 535
 0536            if ((proto & SslProtocols.Tls13) != 0)
 0537            {
 0538                ret |= SslProtocols.Tls13;
 0539            }
 540
 0541            return ret;
 0542        }
 543
 0544        public virtual bool CheckCertRevocationStatus => _sslAuthenticationOptions.CertificateRevocationCheckMode != X50
 545
 546        //
 547        // This will return selected local cert for both client/server streams
 548        //
 549        public virtual X509Certificate? LocalCertificate
 550        {
 551            get
 0552            {
 0553                ThrowIfExceptionalOrNotAuthenticated();
 0554                return IsServer ? LocalServerCertificate : LocalClientCertificate;
 0555            }
 556        }
 557
 558
 559        public virtual X509Certificate? RemoteCertificate
 560        {
 561            get
 0562            {
 0563                ThrowIfExceptionalOrNotAuthenticated();
 0564                _remoteCertificateExposed = true;
 0565                return _remoteCertificate;
 0566            }
 567        }
 568
 569        public SslApplicationProtocol NegotiatedApplicationProtocol
 570        {
 571            get
 0572            {
 0573                ThrowIfExceptionalOrNotHandshake();
 0574                return _connectionInfo.ApplicationProtocol != null ? new SslApplicationProtocol(_connectionInfo.Applicat
 0575            }
 576        }
 577
 578        [CLSCompliant(false)]
 579        public virtual TlsCipherSuite NegotiatedCipherSuite
 580        {
 581            get
 0582            {
 0583                ThrowIfExceptionalOrNotHandshake();
 0584                return _connectionInfo.TlsCipherSuite;
 0585            }
 586        }
 587
 588        [Obsolete(Obsoletions.TlsCipherAlgorithmEnumsMessage, DiagnosticId = Obsoletions.TlsCipherAlgorithmEnumsDiagId, 
 589        public virtual CipherAlgorithmType CipherAlgorithm
 590        {
 591            get
 0592            {
 0593                ThrowIfExceptionalOrNotHandshake();
 0594                return (CipherAlgorithmType)_connectionInfo.DataCipherAlg;
 0595            }
 596        }
 597
 598        [Obsolete(Obsoletions.TlsCipherAlgorithmEnumsMessage, DiagnosticId = Obsoletions.TlsCipherAlgorithmEnumsDiagId, 
 599        public virtual int CipherStrength
 600        {
 601            get
 0602            {
 0603                ThrowIfExceptionalOrNotHandshake();
 0604                return _connectionInfo.DataKeySize;
 0605            }
 606        }
 607
 608        [Obsolete(Obsoletions.TlsCipherAlgorithmEnumsMessage, DiagnosticId = Obsoletions.TlsCipherAlgorithmEnumsDiagId, 
 609        public virtual HashAlgorithmType HashAlgorithm
 610        {
 611            get
 0612            {
 0613                ThrowIfExceptionalOrNotHandshake();
 0614                return (HashAlgorithmType)_connectionInfo.DataHashAlg;
 0615            }
 616        }
 617
 618        [Obsolete(Obsoletions.TlsCipherAlgorithmEnumsMessage, DiagnosticId = Obsoletions.TlsCipherAlgorithmEnumsDiagId, 
 619        public virtual int HashStrength
 620        {
 621            get
 0622            {
 0623                ThrowIfExceptionalOrNotHandshake();
 0624                return _connectionInfo.DataHashKeySize;
 0625            }
 626        }
 627
 628        [Obsolete(Obsoletions.TlsCipherAlgorithmEnumsMessage, DiagnosticId = Obsoletions.TlsCipherAlgorithmEnumsDiagId, 
 629        public virtual ExchangeAlgorithmType KeyExchangeAlgorithm
 630        {
 631            get
 0632            {
 0633                ThrowIfExceptionalOrNotHandshake();
 0634                return (ExchangeAlgorithmType)_connectionInfo.KeyExchangeAlg;
 0635            }
 636        }
 637
 638        [Obsolete(Obsoletions.TlsCipherAlgorithmEnumsMessage, DiagnosticId = Obsoletions.TlsCipherAlgorithmEnumsDiagId, 
 639        public virtual int KeyExchangeStrength
 640        {
 641            get
 0642            {
 0643                ThrowIfExceptionalOrNotHandshake();
 0644                return _connectionInfo.KeyExchKeySize;
 0645            }
 646        }
 647
 648        public string TargetHostName
 649        {
 650            get
 0651            {
 0652                return _sslAuthenticationOptions.TargetHost;
 0653            }
 654        }
 655
 656        //
 657        // Stream contract implementation.
 658        //
 0659        public override bool CanSeek => false;
 660
 0661        public override bool CanRead => IsAuthenticated && InnerStream.CanRead;
 662
 0663        public override bool CanTimeout => InnerStream.CanTimeout;
 664
 0665        public override bool CanWrite => IsAuthenticated && InnerStream.CanWrite && !_shutdown;
 666
 667        public override int ReadTimeout
 668        {
 0669            get => InnerStream.ReadTimeout;
 0670            set => InnerStream.ReadTimeout = value;
 671        }
 672
 673        public override int WriteTimeout
 674        {
 0675            get => InnerStream.WriteTimeout;
 0676            set => InnerStream.WriteTimeout = value;
 677        }
 678
 0679        public override long Length => InnerStream.Length;
 680
 681        public override long Position
 682        {
 0683            get => InnerStream.Position;
 0684            set => throw new NotSupportedException(SR.net_noseek);
 685        }
 686
 0687        public override void SetLength(long value) => InnerStream.SetLength(value);
 688
 0689        public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException(SR.net_noseek);
 690
 0691        public override void Flush() => InnerStream.Flush();
 692
 0693        public override Task FlushAsync(CancellationToken cancellationToken) => InnerStream.FlushAsync(cancellationToken
 694
 695        [SupportedOSPlatform("linux")]
 696        [SupportedOSPlatform("windows")]
 697        [SupportedOSPlatform("freebsd")]
 698        public virtual Task NegotiateClientCertificateAsync(CancellationToken cancellationToken = default)
 0699        {
 0700            ThrowIfExceptionalOrNotAuthenticated();
 0701            if (RemoteCertificate != null)
 0702            {
 0703                throw new InvalidOperationException(SR.net_ssl_certificate_exist);
 704            }
 705
 0706            return RenegotiateAsync<AsyncReadWriteAdapter>(cancellationToken);
 0707        }
 708
 709        protected override void Dispose(bool disposing)
 31710        {
 711            try
 31712            {
 31713                CloseInternal();
 31714            }
 715            finally
 31716            {
 31717                base.Dispose(disposing);
 31718            }
 31719        }
 720
 721        public override async ValueTask DisposeAsync()
 0722        {
 723            try
 0724            {
 0725                CloseInternal();
 0726            }
 727            finally
 0728            {
 0729                await base.DisposeAsync().ConfigureAwait(false);
 0730            }
 0731        }
 732
 733        private static unsafe PoolingPointerMemoryManager RentPointerMemoryManager(ref PoolingPointerMemoryManager? fiel
 0734        {
 735            // we get null when called for the first-time, or concurrent read or write operation
 0736            var manager = Interlocked.Exchange(ref field, null) ?? new PoolingPointerMemoryManager();
 737
 0738            manager.Reset(pointer, length);
 0739            return manager;
 0740        }
 741
 742        private static unsafe void ReturnPointerMemoryManager(ref PoolingPointerMemoryManager? field, PoolingPointerMemo
 0743        {
 0744            manager.Reset(null, 0);
 0745            field = manager;
 0746        }
 747
 748        public override int ReadByte()
 0749        {
 0750            ThrowIfExceptionalOrNotAuthenticated();
 0751            if (Interlocked.Exchange(ref _nestedRead, NestedState.StreamInUse) == NestedState.StreamInUse)
 0752            {
 0753                throw new NotSupportedException(SR.Format(SR.net_io_invalidnestedcall, "read"));
 754            }
 755
 756            // If there's any data in the buffer, take one byte, and we're done.
 757            try
 0758            {
 0759                if (_buffer.DecryptedLength > 0)
 0760                {
 0761                    int b = _buffer.DecryptedSpan[0];
 0762                    _buffer.Discard(1);
 0763                    ReturnReadBufferIfEmpty();
 0764                    return b;
 765                }
 0766            }
 767            finally
 0768            {
 769                // Regardless of whether we were able to read a byte from the buffer,
 770                // reset the read tracking.  If we weren't able to read a byte, the
 771                // subsequent call to Read will set the flag again.
 0772                _nestedRead = NestedState.StreamNotInUse;
 0773            }
 774
 775            // Otherwise, fall back to reading a byte via Read, the same way Stream.ReadByte does.
 776            // This allocation is unfortunate but should be relatively rare, as it'll only occur once
 777            // per buffer fill internally by Read.
 0778            byte oneByte = default;
 0779            int bytesRead = Read(new Span<byte>(ref oneByte));
 0780            Debug.Assert(bytesRead == 0 || bytesRead == 1);
 0781            return bytesRead == 1 ? oneByte : -1;
 0782        }
 783
 784        /// <inheritdoc />
 785        public override unsafe int Read(Span<byte> buffer)
 0786        {
 0787            ThrowIfExceptionalOrNotAuthenticated();
 788
 0789            fixed (byte* ptr = &MemoryMarshal.GetReference(buffer))
 0790            {
 0791                PoolingPointerMemoryManager memoryManager = RentPointerMemoryManager(ref _readPointerMemoryManager, ptr,
 792
 793                try
 0794                {
 0795                    ValueTask<int> vt = ReadAsyncInternal<SyncReadWriteAdapter>(memoryManager.Memory, default(Cancellati
 0796                    Debug.Assert(vt.IsCompleted, "Sync operation must have completed synchronously");
 0797                    return vt.GetAwaiter().GetResult();
 798                }
 799                finally
 0800                {
 0801                    ReturnPointerMemoryManager(ref _readPointerMemoryManager, memoryManager);
 0802                }
 803            }
 0804        }
 805
 806        /// <inheritdoc />
 807        public override int Read(byte[] buffer, int offset, int count)
 0808        {
 0809            ThrowIfExceptionalOrNotAuthenticated();
 0810            ValidateBufferArguments(buffer, offset, count);
 0811            ValueTask<int> vt = ReadAsyncInternal<SyncReadWriteAdapter>(new Memory<byte>(buffer, offset, count), default
 0812            Debug.Assert(vt.IsCompleted, "Sync operation must have completed synchronously");
 0813            return vt.GetAwaiter().GetResult();
 0814        }
 815
 816        /// <inheritdoc />
 0817        public override void WriteByte(byte value) => Write(new ReadOnlySpan<byte>(ref value));
 818
 819        /// <inheritdoc />
 820        public override unsafe void Write(ReadOnlySpan<byte> buffer)
 0821        {
 0822            ThrowIfExceptionalOrNotAuthenticated();
 823
 0824            fixed (byte* ptr = &MemoryMarshal.GetReference(buffer))
 0825            {
 0826                PoolingPointerMemoryManager memoryManager = RentPointerMemoryManager(ref _writePointerMemoryManager, ptr
 827
 828                try
 0829                {
 0830                    ValueTask vt = WriteAsyncInternal<SyncReadWriteAdapter>(memoryManager.Memory, default(CancellationTo
 0831                    Debug.Assert(vt.IsCompleted, "Sync operation must have completed synchronously");
 0832                    vt.GetAwaiter().GetResult();
 0833                }
 834                finally
 0835                {
 0836                    ReturnPointerMemoryManager(ref _writePointerMemoryManager, memoryManager);
 0837                }
 0838            }
 0839        }
 840
 0841        public void Write(byte[] buffer) => Write(buffer, 0, buffer.Length);
 842
 843        /// <inheritdoc />
 844        public override void Write(byte[] buffer, int offset, int count)
 0845        {
 0846            ThrowIfExceptionalOrNotAuthenticated();
 0847            ValidateBufferArguments(buffer, offset, count);
 848
 0849            ValueTask vt = WriteAsyncInternal<SyncReadWriteAdapter>(new ReadOnlyMemory<byte>(buffer, offset, count), def
 0850            Debug.Assert(vt.IsCompleted, "Sync operation must have completed synchronously");
 0851            vt.GetAwaiter().GetResult();
 0852        }
 853
 854        public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? asyncCallback, objec
 0855        {
 0856            ThrowIfExceptionalOrNotAuthenticated();
 0857            return TaskToAsyncResult.Begin(ReadAsync(buffer, offset, count, CancellationToken.None), asyncCallback, asyn
 0858        }
 859
 860        public override int EndRead(IAsyncResult asyncResult)
 0861        {
 0862            ThrowIfExceptionalOrNotAuthenticated();
 0863            return TaskToAsyncResult.End<int>(asyncResult);
 0864        }
 865
 866        public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? asyncCallback, obje
 0867        {
 0868            ThrowIfExceptionalOrNotAuthenticated();
 0869            return TaskToAsyncResult.Begin(WriteAsync(buffer, offset, count, CancellationToken.None), asyncCallback, asy
 0870        }
 871
 872        public override void EndWrite(IAsyncResult asyncResult)
 0873        {
 0874            ThrowIfExceptionalOrNotAuthenticated();
 0875            TaskToAsyncResult.End(asyncResult);
 0876        }
 877
 878        public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
 0879        {
 0880            ThrowIfExceptionalOrNotAuthenticated();
 0881            ValidateBufferArguments(buffer, offset, count);
 0882            return WriteAsyncInternal<AsyncReadWriteAdapter>(new ReadOnlyMemory<byte>(buffer, offset, count), cancellati
 0883        }
 884
 885        public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
 0886        {
 0887            ThrowIfExceptionalOrNotAuthenticated();
 0888            return WriteAsyncInternal<AsyncReadWriteAdapter>(buffer, cancellationToken);
 0889        }
 890
 891        public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
 0892        {
 0893            ThrowIfExceptionalOrNotAuthenticated();
 0894            ValidateBufferArguments(buffer, offset, count);
 0895            return ReadAsyncInternal<AsyncReadWriteAdapter>(new Memory<byte>(buffer, offset, count), cancellationToken).
 0896        }
 897
 898        public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
 0899        {
 0900            ThrowIfExceptionalOrNotAuthenticated();
 0901            return ReadAsyncInternal<AsyncReadWriteAdapter>(buffer, cancellationToken);
 0902        }
 903
 904        private void ThrowIfExceptional()
 31905        {
 31906            ExceptionDispatchInfo? e = _exception;
 31907            if (e != null)
 0908            {
 0909                ThrowExceptional(e);
 0910            }
 911
 912            // Local function to make the check method more inline friendly.
 913            void ThrowExceptional(ExceptionDispatchInfo e)
 0914            {
 915                // If the stored exception just indicates disposal, throw a new ODE rather than the stored one,
 916                // so as to not continually build onto the shared exception's stack.
 0917                ObjectDisposedException.ThrowIf(ReferenceEquals(e, s_disposedSentinel), this);
 918
 919                // Throw the stored exception.
 0920                e.Throw();
 921            }
 31922        }
 923
 924        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 925        private void ThrowIfExceptionalOrNotAuthenticated()
 0926        {
 0927            ThrowIfExceptional();
 928
 0929            if (!IsAuthenticated)
 0930            {
 0931                ThrowNotAuthenticated();
 0932            }
 0933        }
 934
 935        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 936        private void ThrowIfExceptionalOrNotHandshake()
 0937        {
 0938            ThrowIfExceptional();
 939
 940            // if the Protocol is set then rest of the ConnectionInfo is valid
 0941            if (_connectionInfo.Protocol == 0)
 0942            {
 0943                ThrowNotAuthenticated();
 0944            }
 0945        }
 946
 947        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 948        private void ThrowIfExceptionalOrNotAuthenticatedOrShutdown()
 0949        {
 0950            ThrowIfExceptional();
 951
 0952            if (!IsAuthenticated)
 0953            {
 0954                ThrowNotAuthenticated();
 0955            }
 956
 0957            if (_shutdown)
 0958            {
 0959                ThrowAlreadyShutdown();
 0960            }
 961
 962            // Local function to make the check method more inline friendly.
 963            static void ThrowAlreadyShutdown()
 0964            {
 0965                throw new InvalidOperationException(SR.net_ssl_io_already_shutdown);
 966            }
 0967        }
 968
 969        // Static non-returning throw method to make the check methods more inline friendly.
 970        private static void ThrowNotAuthenticated()
 0971        {
 0972            throw new InvalidOperationException(SR.net_auth_noauth);
 973        }
 974
 975        // (non-generic) copy of the PointerMemoryManager<T> which supports resetting the stored
 976        // pointer to allow pooling its instances instead of allocating a new one per Read/Write call.
 977        // The memory ponted to by the intenal poiner is assumed to be externally pinned (or naive memory).
 978        internal sealed unsafe class PoolingPointerMemoryManager : MemoryManager<byte>
 979        {
 980            private byte* _pointer;
 981            private int _length;
 982
 983            protected override void Dispose(bool disposing)
 0984            {
 0985            }
 986
 987            public void Reset(byte* pointer, int length)
 0988            {
 0989                _pointer = pointer;
 0990                _length = length;
 0991            }
 992
 993            public override Span<byte> GetSpan()
 0994            {
 0995                return new Span<byte>(_pointer, _length);
 0996            }
 997
 998            public override MemoryHandle Pin(int elementIndex = 0)
 0999            {
 1000                // memory assumed to be pinned already
 01001                return new MemoryHandle(_pointer + elementIndex, default, null);
 01002            }
 1003
 1004            public override void Unpin()
 01005            {
 1006                // nop
 01007            }
 1008        }
 1009    }
 1010}
 1011

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Buffers;
 5using System.Diagnostics;
 6using System.IO;
 7using System.Runtime.CompilerServices;
 8using System.Runtime.ExceptionServices;
 9using System.Security.Authentication;
 10using System.Security.Cryptography.X509Certificates;
 11using System.Threading;
 12using System.Threading.Tasks;
 13
 14namespace System.Net.Security
 15{
 16    public partial class SslStream
 17    {
 3118        internal readonly SslAuthenticationOptions _sslAuthenticationOptions = new SslAuthenticationOptions();
 7619        internal new Stream InnerStream => base.InnerStream;
 20        private NestedState _nestedAuth;
 21        private bool _isRenego;
 22        private bool _isReAuthentication;
 23
 24        private TlsFrameHelper.TlsFrameInfo _lastFrame;
 25
 026        private object _handshakeLock => _sslAuthenticationOptions;
 27        private volatile TaskCompletionSource<bool>? _handshakeWaiter;
 28
 29        private const int HandshakeTypeOffsetSsl2 = 2;                       // Offset of HelloType in Sslv2 and Unified
 30        private const int HandshakeTypeOffsetTls = 5;                        // Offset of HelloType in Sslv3 and TLS fra
 31
 32        private const int UnknownTlsFrameLength = int.MaxValue;              // frame too short to determine length
 33
 34        private bool _receivedEOF;
 35
 36        // Used by Telemetry to ensure we log connection close exactly once
 37        private enum ConnectionStatus
 38        {
 39            NoHandshake = 0,
 40            HandshakeCompleted = 1, // connection opened
 41            Disposed = 2, // connection closed
 42        }
 43
 44        private ConnectionStatus _connectionOpenedStatus;
 45
 46        private void SetException(Exception e)
 047        {
 048            Debug.Assert(e != null, $"Expected non-null Exception to be passed to {nameof(SetException)}");
 49
 050            _exception ??= ExceptionDispatchInfo.Capture(e);
 51
 052            CloseContext();
 053        }
 54
 55        //
 56        // This is to not depend on GC&SafeHandle class if the context is not needed anymore.
 57        //
 58        private void CloseInternal()
 3159        {
 3160            _exception = s_disposedSentinel;
 3161            CloseContext();
 62
 63            // Ensure a Read or Auth operation is not in progress,
 64            // block potential future read and auth operations since SslStream is disposing.
 65            // This leaves the _nestedRead = StreamDisposed and _nestedAuth = StreamDisposed, but that's ok, since
 66            // subsequent operations check the _exception sentinel first
 3167            if (Interlocked.Exchange(ref _nestedRead, NestedState.StreamDisposed) == NestedState.StreamNotInUse &&
 3168                Interlocked.Exchange(ref _nestedAuth, NestedState.StreamDisposed) == NestedState.StreamNotInUse)
 3169            {
 3170                _buffer.ReturnBuffer();
 3171            }
 72
 3173            if (!_buffer.IsValid)
 3174            {
 75                // Suppress finalizer since the read buffer was returned.
 3176                GC.SuppressFinalize(this);
 3177            }
 78
 3179            if (NetSecurityTelemetry.Log.IsEnabled())
 080            {
 81                // Set the status to disposed. If it was opened before, log ConnectionClosed
 082                if (Interlocked.Exchange(ref _connectionOpenedStatus, ConnectionStatus.Disposed) == ConnectionStatus.Han
 083                {
 084                    NetSecurityTelemetry.Log.ConnectionClosed(GetSslProtocolInternal());
 085                }
 086            }
 3187        }
 88
 89        //
 90        // This method assumes that a SSPI context is already in a good shape.
 91        // For example it is either a fresh context or already authenticated context that needs renegotiation.
 92        //
 93        private Task ProcessAuthenticationAsync(bool isAsync = false, CancellationToken cancellationToken = default)
 3194        {
 3195            ThrowIfExceptional();
 96
 3197            if (NetSecurityTelemetry.AnyTelemetryEnabled())
 098            {
 099                return ProcessAuthenticationWithTelemetryAsync(isAsync, cancellationToken);
 100            }
 101            else
 31102            {
 31103                return isAsync ?
 31104                    ForceAuthenticationAsync<AsyncReadWriteAdapter>(IsServer, null, cancellationToken) :
 31105                    ForceAuthenticationAsync<SyncReadWriteAdapter>(IsServer, null, cancellationToken);
 106            }
 31107        }
 108
 109        private async Task ProcessAuthenticationWithTelemetryAsync(bool isAsync, CancellationToken cancellationToken)
 0110        {
 111            long startingTimestamp;
 0112            if (NetSecurityTelemetry.Log.IsEnabled())
 0113            {
 0114                NetSecurityTelemetry.Log.HandshakeStart(IsServer, _sslAuthenticationOptions.TargetHost);
 0115                startingTimestamp = Stopwatch.GetTimestamp();
 0116            }
 117            else
 0118            {
 0119                startingTimestamp = 0;
 0120            }
 121
 0122            Activity? activity = NetSecurityTelemetry.StartActivity(this);
 0123            Exception? exception = null;
 124            try
 0125            {
 0126                Task task = isAsync ?
 0127                    ForceAuthenticationAsync<AsyncReadWriteAdapter>(IsServer, null, cancellationToken) :
 0128                    ForceAuthenticationAsync<SyncReadWriteAdapter>(IsServer, null, cancellationToken);
 129
 0130                await task.ConfigureAwait(false);
 131
 0132                if (startingTimestamp is not 0)
 0133                {
 134                    // SslStream could already have been disposed at this point, in which case _connectionOpenedStatus =
 135                    // Make sure that we increment the open connection counter only if it is guaranteed to be decremente
 0136                    bool connectionOpen = Interlocked.CompareExchange(ref _connectionOpenedStatus, ConnectionStatus.Hand
 0137                    SslProtocols protocol = GetSslProtocolInternal();
 0138                    NetSecurityTelemetry.Log.HandshakeCompleted(protocol, startingTimestamp, connectionOpen);
 0139                }
 0140            }
 0141            catch (Exception ex)
 0142            {
 0143                exception = ex;
 0144                if (startingTimestamp is not 0)
 0145                {
 0146                    NetSecurityTelemetry.Log.HandshakeFailed(IsServer, startingTimestamp, ex.Message);
 0147                }
 148
 0149                throw;
 150            }
 151            finally
 0152            {
 0153                NetSecurityTelemetry.StopActivity(activity, exception, this);
 0154            }
 0155        }
 156
 157        //
 158        // This is used to reply on re-handshake when received SEC_I_RENEGOTIATE on Read().
 159        //
 160        private async Task ReplyOnReAuthenticationAsync<TIOAdapter>(byte[]? buffer, CancellationToken cancellationToken)
 161            where TIOAdapter : IReadWriteAdapter
 0162        {
 0163            _isReAuthentication = true;
 164            try
 0165            {
 0166                await ForceAuthenticationAsync<TIOAdapter>(receiveFirst: false, buffer, cancellationToken).ConfigureAwai
 0167            }
 168            finally
 0169            {
 0170                _isReAuthentication = false;
 0171                _handshakeWaiter!.SetResult(true);
 0172                _handshakeWaiter = null;
 0173            }
 0174        }
 175
 176        // This will initiate renegotiation or PHA for Tls1.3
 177        private async Task RenegotiateAsync<TIOAdapter>(CancellationToken cancellationToken)
 178            where TIOAdapter : IReadWriteAdapter
 0179        {
 0180            if (Interlocked.CompareExchange(ref _nestedAuth, NestedState.StreamInUse, NestedState.StreamNotInUse) != Nes
 0181            {
 0182                ObjectDisposedException.ThrowIf(_nestedAuth == NestedState.StreamDisposed, this);
 0183                throw new InvalidOperationException(SR.Format(SR.net_io_invalidnestedcall, "authenticate"));
 184            }
 185
 0186            if (Interlocked.CompareExchange(ref _nestedRead, NestedState.StreamInUse, NestedState.StreamNotInUse) != Nes
 0187            {
 0188                ObjectDisposedException.ThrowIf(_nestedRead == NestedState.StreamDisposed, this);
 0189                throw new NotSupportedException(SR.Format(SR.net_io_invalidnestedcall, "read"));
 190            }
 191
 192            // Write is different since we do not do anything special in Dispose
 0193            if (Interlocked.Exchange(ref _nestedWrite, NestedState.StreamInUse) != NestedState.StreamNotInUse)
 0194            {
 0195                _nestedRead = NestedState.StreamNotInUse;
 0196                throw new NotSupportedException(SR.Format(SR.net_io_invalidnestedcall, "write"));
 197            }
 198
 0199            ProtocolToken token = default;
 0200            token.RentBuffer = true;
 201            try
 0202            {
 0203                if (_buffer.ActiveLength > 0)
 0204                {
 0205                    throw new InvalidOperationException(SR.net_ssl_renegotiate_buffer);
 206                }
 207
 0208                _sslAuthenticationOptions.RemoteCertRequired = true;
 0209                _isRenego = true;
 210
 211
 0212                token = Renegotiate();
 213
 0214                if (token.Size > 0)
 0215                {
 0216                    await TIOAdapter.WriteAsync(InnerStream, token.AsMemory(), cancellationToken).ConfigureAwait(false);
 0217                    await TIOAdapter.FlushAsync(InnerStream, cancellationToken).ConfigureAwait(false);
 0218                }
 219
 0220                token.ReleasePayload();
 221
 0222                if (token.Status.ErrorCode != SecurityStatusPalErrorCode.OK)
 0223                {
 0224                    if (token.Status.ErrorCode == SecurityStatusPalErrorCode.NoRenegotiation)
 0225                    {
 226                        // Peer does not want to renegotiate. That should keep session usable.
 0227                        return;
 228                    }
 229
 0230                    throw SslStreamPal.GetException(token.Status);
 231                }
 232
 233                do
 0234                {
 0235                    int frameSize = await ReceiveHandshakeFrameAsync<TIOAdapter>(cancellationToken).ConfigureAwait(false
 0236                    token = ProcessTlsFrame(frameSize);
 237
 0238                    if (token.Size > 0)
 0239                    {
 0240                        await TIOAdapter.WriteAsync(InnerStream, token.AsMemory(), cancellationToken).ConfigureAwait(fal
 0241                        await TIOAdapter.FlushAsync(InnerStream, cancellationToken).ConfigureAwait(false);
 0242                    }
 0243                    token.ReleasePayload();
 0244                }
 0245                while (token.Status.ErrorCode == SecurityStatusPalErrorCode.ContinueNeeded);
 246
 0247                CompleteHandshake(_sslAuthenticationOptions);
 0248            }
 249            finally
 0250            {
 0251                if (_buffer.ActiveLength == 0)
 0252                {
 0253                    _buffer.ReturnBuffer();
 0254                }
 255
 0256                token.ReleasePayload();
 257
 0258                _nestedRead = NestedState.StreamNotInUse;
 0259                _nestedWrite = NestedState.StreamNotInUse;
 0260                _isRenego = false;
 261                // We will not release _nestedAuth at this point to prevent another renegotiation attempt.
 0262            }
 0263        }
 264
 265        // reAuthenticationData is only used on Windows in case of renegotiation.
 266        private async Task ForceAuthenticationAsync<TIOAdapter>(bool receiveFirst, byte[]? reAuthenticationData, Cancell
 267            where TIOAdapter : IReadWriteAdapter
 31268        {
 31269            bool handshakeCompleted = false;
 31270            ProtocolToken token = default;
 271
 31272            token.RentBuffer = true;
 273
 31274            if (reAuthenticationData == null)
 31275            {
 276                // prevent nesting only when authentication functions are called explicitly. e.g. handle renegotiation t
 31277                if (Interlocked.Exchange(ref _nestedAuth, NestedState.StreamInUse) == NestedState.StreamInUse)
 0278                {
 0279                    throw new InvalidOperationException(SR.Format(SR.net_io_invalidnestedcall, "authenticate"));
 280                }
 31281            }
 282            try
 31283            {
 284#if TARGET_APPLE
 285                if (SslStreamPal.ShouldUseAsyncSecurityContext(_sslAuthenticationOptions))
 286                {
 287                    byte[]? dummy = null;
 288                    if (_sslAuthenticationOptions.IsClient)
 289                    {
 290                        AcquireClientCredentials(ref dummy, true);
 291                    }
 292                    else if (_sslAuthenticationOptions.ServerCertSelectionDelegate is null &&
 293                             _sslAuthenticationOptions.CertSelectionDelegate is { } certSelectionDelegate)
 294                    {
 295                        // Match legacy SecureTransport PAL: when CertSelectionDelegate is
 296                        // configured and returns null, surface NotSupportedException
 297                        // instead of timing out the handshake.
 298                        var tempCollection = new X509CertificateCollection();
 299                        if (_sslAuthenticationOptions.CertificateContext?.TargetCertificate is X509Certificate2 ctx)
 300                        {
 301                            tempCollection.Add(ctx);
 302                        }
 303                        X509Certificate? selected = certSelectionDelegate(this, string.Empty, tempCollection, null, Arra
 304                        if (selected is null)
 305                        {
 306                            throw new NotSupportedException(SR.net_ssl_io_no_server_cert);
 307                        }
 308                    }
 309
 310                    Task<Exception?> handshakeTask = SslStreamPal.AsyncHandshakeAsync(ref _securityContext, this, cancel
 311                    await TIOAdapter.WaitAsync(handshakeTask).ConfigureAwait(false);
 312                    if (await handshakeTask.ConfigureAwait(false) is Exception ex)
 313                    {
 314                        if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(this, ex, "Async handshake failed");
 315                        if (ex is ArgumentException or IOException)
 316                        {
 317                            throw ex;
 318                        }
 319                        throw new AuthenticationException(SR.net_auth_SSPI, ex);
 320                    }
 321
 322                    CompleteHandshake(_sslAuthenticationOptions);
 323                    return;
 324                }
 325                else
 326                {
 327                    _sslAuthenticationOptions.ForceSyncPal = true;
 328                }
 329#endif // TARGET_APPLE
 330
 31331                if (!receiveFirst)
 0332                {
 0333                    token = NextMessage(reAuthenticationData, out int consumed);
 0334                    Debug.Assert(consumed == (reAuthenticationData?.Length ?? 0));
 335
 0336                    if (token.Size > 0)
 0337                    {
 0338                        Debug.Assert(token.Payload != null);
 0339                        await TIOAdapter.WriteAsync(InnerStream, new ReadOnlyMemory<byte>(token.Payload!, 0, token.Size)
 0340                        await TIOAdapter.FlushAsync(InnerStream, cancellationToken).ConfigureAwait(false);
 0341                        if (NetEventSource.Log.IsEnabled())
 0342                            NetEventSource.Log.SentFrame(this, token.Payload);
 0343                    }
 344
 0345                    token.ReleasePayload();
 346
 0347                    if (token.Failed)
 0348                    {
 349                        // tracing done in NextMessage()
 0350                        throw new AuthenticationException(SR.net_auth_SSPI, token.GetException());
 351                    }
 0352                    else if (token.Status.ErrorCode == SecurityStatusPalErrorCode.OK)
 0353                    {
 354                        // We can finish renegotiation without doing any read.
 0355                        handshakeCompleted = true;
 0356                    }
 0357                }
 358
 31359                if (!handshakeCompleted)
 31360                {
 31361                    _buffer.EnsureAvailableSpace(InitialHandshakeBufferSize);
 31362                }
 363
 31364                while (!handshakeCompleted)
 31365                {
 31366                    int frameSize = await ReceiveHandshakeFrameAsync<TIOAdapter>(cancellationToken).ConfigureAwait(false
 0367                    token = ProcessTlsFrame(frameSize);
 368
 0369                    ReadOnlyMemory<byte> payload = default;
 0370                    if (token.Size > 0)
 0371                    {
 0372                        payload = token.AsMemory();
 0373                    }
 0374                    else if (token.Failed && (_lastFrame.Header.Type == TlsContentType.Handshake || _lastFrame.Header.Ty
 0375                    {
 376                        // If we failed without OS sending out alert, inject one here to be consistent across platforms.
 0377                        payload = TlsFrameHelper.CreateAlertFrame(_lastFrame.Header.Version, TlsAlertDescription.Protoco
 0378                    }
 379
 0380                    if (!payload.IsEmpty)
 0381                    {
 382                        // If there is message send it out even if call failed. It may contain TLS Alert.
 0383                        await TIOAdapter.WriteAsync(InnerStream, payload, cancellationToken).ConfigureAwait(false);
 0384                        await TIOAdapter.FlushAsync(InnerStream, cancellationToken).ConfigureAwait(false);
 385
 0386                        if (NetEventSource.Log.IsEnabled())
 0387                            NetEventSource.Log.SentFrame(this, payload.Span);
 0388                    }
 389
 0390                    token.ReleasePayload();
 391
 0392                    if (token.Failed)
 0393                    {
 0394                        if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(this, token.Status);
 395
 0396                        if (_lastFrame.Header.Type == TlsContentType.Alert && _lastFrame.AlertDescription != TlsAlertDes
 0397                                 token.Status.ErrorCode == SecurityStatusPalErrorCode.IllegalMessage)
 0398                        {
 399                            // Improve generic message and show details if we failed because of TLS Alert.
 0400                            throw new AuthenticationException(SR.Format(SR.net_auth_tls_alert, _lastFrame.AlertDescripti
 401                        }
 402
 0403                        if (token.Status.ErrorCode == SecurityStatusPalErrorCode.CertValidationFailed && token.GetExcept
 0404                        {
 405                            // The cert-validation path carries the user-visible exception directly;
 406                            // throw it without wrapping to preserve parity with the SendAuthResetSignal
 407                            // path used after handshake completion on all platforms.
 0408                            ExceptionDispatchInfo.Throw(certException);
 409                        }
 410
 0411                        throw new AuthenticationException(SR.net_auth_SSPI, token.GetException());
 412                    }
 0413                    else if (token.Status.ErrorCode == SecurityStatusPalErrorCode.OK)
 0414                    {
 415                        // We can finish renegotiation without doing any read.
 0416                        handshakeCompleted = true;
 0417                    }
 0418                }
 419
 0420                CompleteHandshake(_sslAuthenticationOptions);
 0421            }
 422            finally
 31423            {
 31424                if (reAuthenticationData == null)
 31425                {
 31426                    _nestedAuth = NestedState.StreamNotInUse;
 31427                    _isRenego = false;
 31428                }
 429
 31430                token.ReleasePayload();
 431
 432                // reset the cached flag which has potentially outdated value.
 31433                _localClientCertificateUsed = -1;
 31434            }
 435
 436#pragma warning disable SYSLIB0058 // Use NegotiatedCipherSuite.
 0437            if (NetEventSource.Log.IsEnabled())
 0438                NetEventSource.Log.SspiSelectedCipherSuite(nameof(ForceAuthenticationAsync),
 0439                                                                    SslProtocol,
 0440                                                                    CipherAlgorithm,
 0441                                                                    CipherStrength,
 0442                                                                    HashAlgorithm,
 0443                                                                    HashStrength,
 0444                                                                    KeyExchangeAlgorithm,
 0445                                                                    KeyExchangeStrength);
 446#pragma warning restore SYSLIB0058 // Use NegotiatedCipherSuite.
 0447        }
 448
 449        // This method will make sure we have at least one full TLS frame buffered.
 450        private async ValueTask<int> ReceiveHandshakeFrameAsync<TIOAdapter>(CancellationToken cancellationToken)
 451            where TIOAdapter : IReadWriteAdapter
 31452        {
 31453            int frameSize = await EnsureFullTlsFrameAsync<TIOAdapter>(cancellationToken, InitialHandshakeBufferSize).Con
 454
 9455            if (frameSize == 0)
 0456            {
 457                // We expect to receive at least one frame
 0458                throw new IOException(SR.net_io_eof);
 459            }
 460
 461#pragma warning disable CS0618
 9462            int handshakeTypeOffset = _lastFrame.Header.Version == SslProtocols.Ssl2 ? HandshakeTypeOffsetSsl2 : Handsha
 463#pragma warning restore CS0618
 464
 465            // At this point, we have at least one TLS frame.
 9466            switch (_lastFrame.Header.Type)
 467            {
 468                case TlsContentType.Alert:
 6469                    if (TlsFrameHelper.TryGetFrameInfo(_buffer.EncryptedReadOnlySpan, ref _lastFrame))
 2470                    {
 2471                        if (NetEventSource.Log.IsEnabled() && _lastFrame.AlertDescription != TlsAlertDescription.CloseNo
 2472                    }
 6473                    break;
 474                case TlsContentType.Handshake:
 0475                    if (frameSize <= handshakeTypeOffset)
 0476                    {
 0477                        throw new IOException(SR.net_ssl_io_frame);
 478                    }
 479
 0480                    if (!_isRenego && _buffer.EncryptedReadOnlySpan[handshakeTypeOffset] == (byte)TlsHandshakeType.Clien
 0481                        _sslAuthenticationOptions!.IsServer) // guard against malicious endpoints. We should not see Cli
 0482                    {
 0483                        TlsFrameHelper.ProcessingOptions options = TlsFrameHelper.ProcessingOptions.ServerName;
 484
 0485                        if (OperatingSystem.IsMacOS() && _sslAuthenticationOptions.IsServer)
 0486                        {
 487                            // macOS cannot process ALPN on server at the moment.
 488                            // We fallback to our own process similar to SNI bellow.
 489                            // This will allocate.
 0490                            options |= TlsFrameHelper.ProcessingOptions.RawApplicationProtocol;
 0491                        }
 492
 0493                        if (NetEventSource.Log.IsEnabled())
 0494                        {
 0495                            options |= TlsFrameHelper.ProcessingOptions.ApplicationProtocol | TlsFrameHelper.ProcessingO
 0496                        }
 497
 0498                        if (_sslAuthenticationOptions.ServerOptionDelegate != null)
 0499                        {
 500                            // We need to process supported versions extension to pass it to user callback.
 0501                            options |= TlsFrameHelper.ProcessingOptions.Versions;
 0502                        }
 503
 504                        // Process SNI from Client Hello message
 0505                        if (!TlsFrameHelper.TryGetFrameInfo(_buffer.EncryptedReadOnlySpan, ref _lastFrame, options))
 0506                        {
 0507                            if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(this, $"Failed to parse TLS hello."
 0508                        }
 509
 0510                        if (_lastFrame.HandshakeType == TlsHandshakeType.ClientHello)
 0511                        {
 512                            // SNI if it exist. Even if we could not parse the hello, we can fall-back to default certif
 0513                            if (_lastFrame.TargetName != null)
 0514                            {
 0515                                _sslAuthenticationOptions.TargetHost = _lastFrame.TargetName;
 0516                            }
 517
 0518                            if (_sslAuthenticationOptions.ServerOptionDelegate != null)
 0519                            {
 0520                                SslServerAuthenticationOptions userOptions =
 0521                                    await _sslAuthenticationOptions.ServerOptionDelegate(this, new SslClientHelloInfo(_s
 0522                                        _sslAuthenticationOptions.UserState, cancellationToken).ConfigureAwait(false);
 0523                                _sslAuthenticationOptions.UpdateOptions(userOptions);
 0524                            }
 0525                        }
 526
 0527                        if (NetEventSource.Log.IsEnabled())
 0528                        {
 0529                            NetEventSource.Log.ReceivedFrame(this, _lastFrame);
 0530                        }
 0531                    }
 0532                    break;
 533                case TlsContentType.AppData:
 534                    // TLS1.3 it is not possible to distinguish between late Handshake and Application Data
 0535                    if (_isRenego && SslProtocol != SslProtocols.Tls13)
 0536                    {
 0537                        throw new InvalidOperationException(SR.net_ssl_renegotiate_data);
 538                    }
 0539                    break;
 540
 541            }
 542
 543            // Per TLS spec, the first message from the client must be ClientHello.
 544            // If we are the server and haven't started the security context yet
 545            // (_securityContext == null), reject any frame that is not a ClientHello.
 9546            if (_sslAuthenticationOptions!.IsServer && _securityContext == null)
 9547            {
 9548                bool isClientHello = _lastFrame.Header.Type == TlsContentType.Handshake &&
 9549                    _buffer.EncryptedReadOnlySpan[handshakeTypeOffset] == (byte)TlsHandshakeType.ClientHello;
 9550                if (!isClientHello)
 9551                {
 9552                    throw new AuthenticationException(SR.net_ssl_io_frame);
 553                }
 0554            }
 555
 0556            return frameSize;
 0557        }
 558
 559        // Calls crypto on received data. No IO inside.
 560        private ProtocolToken ProcessTlsFrame(int frameSize)
 0561        {
 0562            Debug.Assert(frameSize > 0);
 563
 0564            int chunkSize = frameSize;
 565
 0566            ReadOnlySpan<byte> availableData = _buffer.EncryptedReadOnlySpan;
 567
 568            // Often more TLS messages fit into same packet. Get as many complete frames as we can.
 0569            while (availableData.Length - chunkSize > TlsFrameHelper.HeaderSize)
 0570            {
 0571                TlsFrameHeader nextHeader = default;
 572
 573                // we should always have at least TlsFrameHelper.HeaderSize bytes left, so
 574                // if TryGetFrameHeader fails, it means the frame is malformed and we should not continue processing.
 0575                if (!TlsFrameHelper.TryGetFrameHeader(availableData.Slice(chunkSize), ref nextHeader))
 0576                {
 0577                    if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(this, "invalid TLS frame size");
 0578                    throw new AuthenticationException(SR.net_frame_read_size);
 579                }
 580
 0581                frameSize = nextHeader.Length;
 582
 583                // Can process more handshake frames in single step or during TLS1.3 post-handshake auth, but we should
 584                // avoid processing too much so as to preserve API boundary between handshake and I/O.
 0585                if ((nextHeader.Type != TlsContentType.Handshake && nextHeader.Type != TlsContentType.ChangeCipherSpec) 
 0586                {
 587                    // We don't have full frame left or we already have app data which needs to be processed by decrypt.
 0588                    break;
 589                }
 590
 0591                Debug.Assert(frameSize > 0);
 0592                chunkSize += frameSize;
 0593            }
 594
 0595            ProtocolToken token = NextMessage(availableData.Slice(0, chunkSize), out int consumed);
 0596            _buffer.DiscardEncrypted(consumed);
 0597            return token;
 0598        }
 599
 600        //
 601        //  This is to reset auth state on remote side.
 602        //  If this write succeeds we will allow auth retrying.
 603        //
 604        private void SendAuthResetSignal(ReadOnlySpan<byte> alert, ExceptionDispatchInfo exception)
 0605        {
 0606            SetException(exception.SourceException);
 607
 0608            if (alert.Length == 0)
 0609            {
 610                //
 611                // We don't have an alert to send so cannot retry and fail prematurely.
 612                //
 0613                exception.Throw();
 614            }
 615
 0616            InnerStream.Write(alert);
 617
 0618            exception.Throw();
 619        }
 620
 621        // - Loads the channel parameters
 622        // - Optionally verifies the Remote Certificate
 623        // - Sets HandshakeCompleted flag
 624        // - Sets the guarding event if other thread is waiting for
 625        //   handshake completion
 626        //
 627        // - Returns false if failed to verify the Remote Cert
 628        //
 629        private bool CompleteHandshake(ref ProtocolToken alertToken, out SslPolicyErrors sslPolicyErrors, out X509ChainS
 0630        {
 0631            ProcessHandshakeSuccess();
 632
 0633            if (_nestedAuth != NestedState.StreamInUse)
 0634            {
 0635                if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(this, $"Ignoring unsolicited renegotiated certi
 636                // ignore certificates received outside of handshake or requested renegotiation.
 0637                sslPolicyErrors = SslPolicyErrors.None;
 0638                chainStatus = X509ChainStatusFlags.NoError;
 0639                return true;
 640            }
 641
 642#if TARGET_ANDROID
 643            // On Android, the remote certificate verification can be invoked from Java TrustManager's callback
 644            // during the handshake process. If that has occurred, we shouldn't run the validation again and
 645            // return the existing validation result.
 646            //
 647            // The Java TrustManager callback is called only when the peer has a certificate. It's possible that
 648            // the peer didn't provide any certificate (for example when the peer is the client) and the validation
 649            // result hasn't been set. In that case we still need to run the verification at this point.
 650            if (TryGetRemoteCertificateValidationResult(out sslPolicyErrors, out chainStatus, ref alertToken, out bool i
 651            {
 652                _handshakeCompleted = isValid;
 653                return isValid;
 654            }
 655#endif
 656
 0657            sslPolicyErrors = SslPolicyErrors.None;
 658#pragma warning disable CS0162 // unreachable code on some platforms
 0659            if (!SslStreamPal.CertValidationInCallback)
 0660            {
 0661                if (!VerifyRemoteCertificate(_sslAuthenticationOptions.CertificateContext?.Trust, ref alertToken, ref ss
 0662                {
 0663                    _handshakeCompleted = false;
 0664                    return false;
 665                }
 0666            }
 667            else if (_remoteCertificate is null)
 668            {
 669                // CertVerifyCallback was not called during the handshake. This happens when:
 670                // 1. The session was resumed â€” the cert is available from the SSL handle
 671                //    but OpenSSL skips the verify callback.
 672                // 2. The peer didn't provide a certificate at all.
 673                // In both cases, run VerifyRemoteCertificate to invoke the user's callback
 674                // and perform full validation.
 675                if (!VerifyRemoteCertificate(_sslAuthenticationOptions.CertificateContext?.Trust, ref alertToken, ref ss
 676                {
 677                    _handshakeCompleted = false;
 678                    return false;
 679                }
 680            }
 681            else
 682            {
 683                chainStatus = X509ChainStatusFlags.NoError;
 684            }
 685#pragma warning restore CS0162 // unreachable code on some platforms
 686
 0687            _handshakeCompleted = true;
 0688            return true;
 0689        }
 690
 691        private void CompleteHandshake(SslAuthenticationOptions sslAuthenticationOptions)
 0692        {
 0693            ProtocolToken alertToken = default;
 0694            if (!CompleteHandshake(ref alertToken, out SslPolicyErrors sslPolicyErrors, out X509ChainStatusFlags chainSt
 0695            {
 0696                SendAuthResetSignal(new ReadOnlySpan<byte>(alertToken.Payload), ExceptionDispatchInfo.Capture(CreateCert
 0697            }
 0698        }
 699
 700        internal static Exception CreateCertificateValidationException(SslAuthenticationOptions options, SslPolicyErrors
 0701        {
 0702            if (options.CertValidationDelegate != null)
 0703            {
 704                // there may be some chain errors but the decision was made by custom callback. Details should be tracin
 0705                return ExceptionDispatchInfo.SetCurrentStackTrace(new AuthenticationException(SR.net_ssl_io_cert_custom_
 706            }
 0707            else if (sslPolicyErrors == SslPolicyErrors.RemoteCertificateChainErrors && chainStatus != X509ChainStatusFl
 0708            {
 709                // We failed only because of chain and we have some insight.
 0710                return ExceptionDispatchInfo.SetCurrentStackTrace(new AuthenticationException(SR.Format(SR.net_ssl_io_ce
 711            }
 712            else
 0713            {
 714                // Simple add sslPolicyErrors as crude info.
 0715                return ExceptionDispatchInfo.SetCurrentStackTrace(new AuthenticationException(SR.Format(SR.net_ssl_io_ce
 716            }
 0717        }
 718
 719        private async ValueTask WriteAsyncChunked<TIOAdapter>(ReadOnlyMemory<byte> buffer, CancellationToken cancellatio
 720            where TIOAdapter : IReadWriteAdapter
 0721        {
 722            do
 0723            {
 0724                int chunkBytes = Math.Min(buffer.Length, MaxDataSize);
 0725                await WriteSingleChunk<TIOAdapter>(buffer.Slice(0, chunkBytes), cancellationToken).ConfigureAwait(false)
 0726                buffer = buffer.Slice(chunkBytes);
 0727            } while (buffer.Length != 0);
 0728        }
 729
 730        private ValueTask WriteSingleChunk<TIOAdapter>(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken)
 731            where TIOAdapter : IReadWriteAdapter
 0732        {
 733            ProtocolToken token;
 0734            while (true)
 0735            {
 0736                token = EncryptData(buffer);
 737                // TryAgain should be rare, when renegotiation happens exactly when we want to write.
 0738                if (token.Status.ErrorCode != SecurityStatusPalErrorCode.TryAgain)
 0739                {
 0740                    break;
 741                }
 742
 743                // We failed to encrypt because renegotiation is pending.
 0744                TaskCompletionSource<bool>? waiter = _handshakeWaiter;
 0745                if (waiter != null)
 0746                {
 0747                    Task waiterTask = TIOAdapter.WaitAsync(waiter);
 748                    // We finished synchronously waiting for renegotiation. We can try again immediately.
 0749                    if (waiterTask.IsCompletedSuccessfully)
 0750                    {
 0751                        continue;
 752                    }
 753
 754                    // We need to wait asynchronously as well as for the write when EncryptData is finished.
 0755                    return WaitAndWriteAsync(buffer, waiterTask, cancellationToken);
 756                }
 0757            }
 758
 0759            if (token.Status.ErrorCode != SecurityStatusPalErrorCode.OK)
 0760            {
 0761                token.ReleasePayload();
 0762                return ValueTask.FromException(ExceptionDispatchInfo.SetCurrentStackTrace(new IOException(SR.net_io_encr
 763            }
 764
 0765            ValueTask t = TIOAdapter.WriteAsync(InnerStream, token.AsMemory(), cancellationToken);
 0766            if (t.IsCompletedSuccessfully)
 0767            {
 0768                token.ReleasePayload();
 0769                return t;
 770            }
 771            else
 0772            {
 0773                return CompleteWriteAsync(t, token);
 774            }
 775
 776            async ValueTask WaitAndWriteAsync(ReadOnlyMemory<byte> buffer, Task waitTask, CancellationToken cancellation
 0777            {
 0778                ProtocolToken token = default;
 779                try
 0780                {
 781                    // Wait for renegotiation to finish.
 0782                    await waitTask.ConfigureAwait(false);
 783
 0784                    token = EncryptData(buffer);
 0785                    if (token.Status.ErrorCode == SecurityStatusPalErrorCode.TryAgain)
 0786                    {
 787                        // Call WriteSingleChunk() recursively to avoid code duplication.
 788                        // This should be extremely rare in cases when second renegotiation happens concurrently with Wr
 0789                        await WriteSingleChunk<TIOAdapter>(buffer, cancellationToken).ConfigureAwait(false);
 0790                    }
 0791                    else if (token.Status.ErrorCode == SecurityStatusPalErrorCode.OK)
 0792                    {
 0793                        await TIOAdapter.WriteAsync(InnerStream, token.AsMemory(), cancellationToken).ConfigureAwait(fal
 0794                    }
 795                    else
 0796                    {
 0797                        throw new IOException(SR.net_io_encrypt, SslStreamPal.GetException(token.Status));
 798                    }
 0799                }
 800                finally
 0801                {
 0802                    token.ReleasePayload();
 0803                }
 0804            }
 805
 806            static async ValueTask CompleteWriteAsync(ValueTask writeTask, ProtocolToken token)
 0807            {
 808                try
 0809                {
 0810                    await writeTask.ConfigureAwait(false);
 0811                }
 812                finally
 0813                {
 0814                    token.ReleasePayload();
 0815                }
 0816            }
 0817        }
 818
 819        ~SslStream()
 0820        {
 0821            Dispose(disposing: false);
 0822        }
 823
 824        private void ReturnReadBufferIfEmpty()
 0825        {
 0826            if (_buffer.ActiveLength == 0)
 0827            {
 0828                _buffer.ReturnBuffer();
 0829            }
 0830        }
 831
 832        private bool HaveFullTlsFrame(out int frameSize)
 31833        {
 31834            frameSize = GetFrameSize(_buffer.EncryptedReadOnlySpan);
 31835            return _buffer.EncryptedLength >= frameSize;
 31836        }
 837
 838        [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder<>))]
 839        [RuntimeAsyncMethodGeneration(false)]
 840        private async ValueTask<int> EnsureFullTlsFrameAsync<TIOAdapter>(CancellationToken cancellationToken, int estima
 841            where TIOAdapter : IReadWriteAdapter
 31842        {
 31843            if (HaveFullTlsFrame(out int frameSize))
 0844            {
 0845                return frameSize;
 846            }
 847
 31848            await TIOAdapter.ReadAsync(InnerStream, Memory<byte>.Empty, cancellationToken).ConfigureAwait(false);
 849
 850            // If we don't have enough data to determine the frame size, use the provided estimate
 851            // (e.g. a full TLS frame for reads, and a somewhat shorter frame for handshake / renegotiation).
 852            // If we do know the frame size, ensure we have space for the whole frame.
 31853            _buffer.EnsureAvailableSpace(frameSize == UnknownTlsFrameLength ?
 31854                estimatedSize :
 31855                frameSize - _buffer.EncryptedLength);
 856
 54857            while (_buffer.EncryptedLength < frameSize)
 45858            {
 859                // there should be space left to read into
 45860                Debug.Assert(_buffer.AvailableLength > 0, "_buffer.AvailableBytes > 0");
 861
 862                // We either don't have full frame or we don't have enough data to even determine the size.
 45863                int bytesRead = await TIOAdapter.ReadAsync(InnerStream, _buffer.AvailableMemory, cancellationToken).Conf
 45864                if (bytesRead == 0)
 14865                {
 14866                    if (_buffer.EncryptedLength != 0)
 14867                    {
 868                        // we got EOF in middle of TLS frame. Treat that as error.
 14869                        throw new IOException(SR.net_io_eof);
 870                    }
 871
 0872                    return 0;
 873                }
 874
 31875                _buffer.Commit(bytesRead);
 31876                if (frameSize == UnknownTlsFrameLength && _buffer.EncryptedLength >= TlsFrameHelper.HeaderSize)
 31877                {
 878                    // recalculate frame size if needed e.g. we could not get it before.
 31879                    frameSize = GetFrameSize(_buffer.EncryptedReadOnlySpan);
 23880                    _buffer.EnsureAvailableSpace(frameSize - _buffer.EncryptedLength);
 23881                }
 23882            }
 883
 9884            return frameSize;
 9885        }
 886
 887        [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder<>))]
 888        [RuntimeAsyncMethodGeneration(false)]
 889        private async ValueTask<int> ReadAsyncInternal<TIOAdapter>(Memory<byte> buffer, CancellationToken cancellationTo
 890            where TIOAdapter : IReadWriteAdapter
 0891        {
 892            // Throw first if we already have exception.
 893            // Check for disposal is not atomic so we will check again below.
 0894            ThrowIfExceptionalOrNotAuthenticated();
 895
 0896            if (Interlocked.CompareExchange(ref _nestedRead, NestedState.StreamInUse, NestedState.StreamNotInUse) != Nes
 0897            {
 0898                ObjectDisposedException.ThrowIf(_nestedRead == NestedState.StreamDisposed, this);
 0899                throw new NotSupportedException(SR.Format(SR.net_io_invalidnestedcall, "read"));
 900            }
 901
 902            try
 0903            {
 904
 905#if TARGET_APPLE
 906                if (SslStreamPal.IsAsyncSecurityContext(_securityContext!))
 907                {
 908                    ValueTask<int> task = SslStreamPal.AsyncReadAsync(_securityContext!, buffer, cancellationToken);
 909                    return await TIOAdapter.WaitAsync(task).ConfigureAwait(false);
 910                }
 911#endif // TARGET_APPLE
 912
 0913                int processedLength = 0;
 0914                int nextTlsFrameLength = UnknownTlsFrameLength;
 915
 0916                if (_buffer.DecryptedLength != 0)
 0917                {
 0918                    processedLength = CopyDecryptedData(buffer);
 0919                    if (processedLength == buffer.Length || !HaveFullTlsFrame(out nextTlsFrameLength))
 0920                    {
 921                        // We either filled whole buffer or used all buffered frames.
 0922                        return processedLength;
 923                    }
 924
 0925                    buffer = buffer.Slice(processedLength);
 0926                }
 927
 0928                if (_receivedEOF && nextTlsFrameLength == UnknownTlsFrameLength)
 0929                {
 930                    // there should be no frames waiting for processing
 0931                    Debug.Assert(_buffer.EncryptedLength == 0);
 932                    // We received EOF during previous read but had buffered data to return.
 0933                    return 0;
 934                }
 935
 0936                Debug.Assert(_buffer.DecryptedLength == 0);
 937
 0938                while (true)
 0939                {
 0940                    int payloadBytes = await EnsureFullTlsFrameAsync<TIOAdapter>(cancellationToken, ReadBufferSize).Conf
 0941                    if (payloadBytes == 0)
 0942                    {
 0943                        _receivedEOF = true;
 0944                        break;
 945                    }
 946
 947                    // Pass the user buffer to DecryptData so PALs that support it can decrypt directly
 948                    // into the destination, avoiding a CopyDecryptedData memcpy. When the renegotiate
 949                    // path is in flight we suppress the direct path by passing an empty span - the PAL
 950                    // will fall back to in-place decrypt and the existing CopyDecryptedData path runs.
 0951                    Span<byte> destination = _handshakeWaiter == null ? buffer.Span : default;
 0952                    SecurityStatusPal status = DecryptData(payloadBytes, destination, out int directWritten);
 953
 0954                    if (status.ErrorCode != SecurityStatusPalErrorCode.OK)
 0955                    {
 0956                        byte[]? extraBuffer = null;
 0957                        if (_buffer.DecryptedLength != 0)
 0958                        {
 0959                            extraBuffer = new byte[_buffer.DecryptedLength];
 0960                            _buffer.DecryptedSpan.CopyTo(extraBuffer);
 961
 0962                            _buffer.Discard(_buffer.DecryptedLength);
 0963                        }
 964
 0965                        if (NetEventSource.Log.IsEnabled())
 0966                            NetEventSource.Info(null, $"***Processing an error Status = {status}");
 967
 0968                        if (status.ErrorCode == SecurityStatusPalErrorCode.Renegotiate)
 0969                        {
 970                            // We determined above that we will not process it.
 0971                            if (_handshakeWaiter == null)
 0972                            {
 0973                                throw new IOException(SR.net_ssl_io_renego);
 974                            }
 0975                            await ReplyOnReAuthenticationAsync<TIOAdapter>(extraBuffer, cancellationToken).ConfigureAwai
 0976                        }
 0977                        else if (status.ErrorCode == SecurityStatusPalErrorCode.ContextExpired)
 0978                        {
 0979                            _receivedEOF = true;
 0980                            break;
 981                        }
 982                        else
 0983                        {
 0984                            throw new IOException(SR.net_io_decrypt, SslStreamPal.GetException(status));
 985                        }
 0986                    }
 987
 0988                    if (directWritten > 0)
 0989                    {
 0990                        processedLength += directWritten;
 0991                        buffer = buffer.Slice(directWritten);
 0992                        if (buffer.IsEmpty)
 0993                        {
 0994                            break;
 995                        }
 0996                    }
 0997                    else if (_buffer.DecryptedLength > 0)
 0998                    {
 999                        // This will either copy data from rented buffer or adjust final buffer as needed.
 1000                        // In both cases _decryptedBytesOffset and _decryptedBytesCount will be updated as needed.
 01001                        int copyLength = CopyDecryptedData(buffer);
 01002                        processedLength += copyLength;
 01003                        if (copyLength == buffer.Length)
 01004                        {
 1005                            // We have more decrypted data after we filled provided buffer.
 01006                            break;
 1007                        }
 1008
 01009                        buffer = buffer.Slice(copyLength);
 01010                    }
 1011
 01012                    if (processedLength == 0)
 01013                    {
 1014                        // We did not get any real data so far.
 01015                        continue;
 1016                    }
 1017
 01018                    if (!HaveFullTlsFrame(out payloadBytes))
 01019                    {
 1020                        // We don't have another frame to process but we have some data to return to caller.
 01021                        break;
 1022                    }
 1023
 01024                    TlsFrameHelper.TryGetFrameHeader(_buffer.EncryptedReadOnlySpan, ref _lastFrame.Header);
 01025                    if (_lastFrame.Header.Type != TlsContentType.AppData)
 01026                    {
 1027                        // Alerts, handshake and anything else will be processed separately.
 1028                        // This may not be necessary but it improves compatibility with older versions.
 01029                        break;
 1030                    }
 01031                }
 1032
 01033                return processedLength;
 1034            }
 01035            catch (Exception e)
 01036            {
 01037                if (e is IOException || (e is OperationCanceledException && cancellationToken.IsCancellationRequested))
 01038                {
 01039                    throw;
 1040                }
 1041
 01042                throw new IOException(SR.net_io_read, e);
 1043            }
 1044            finally
 01045            {
 01046                ReturnReadBufferIfEmpty();
 01047                _nestedRead = NestedState.StreamNotInUse;
 01048            }
 01049        }
 1050
 1051        private async ValueTask WriteAsyncInternal<TIOAdapter>(ReadOnlyMemory<byte> buffer, CancellationToken cancellati
 1052            where TIOAdapter : IReadWriteAdapter
 01053        {
 01054            ThrowIfExceptionalOrNotAuthenticatedOrShutdown();
 1055
 01056            if (buffer.Length == 0 && !SslStreamPal.CanEncryptEmptyMessage)
 01057            {
 1058                // If it's an empty message and the PAL doesn't support that, we're done.
 01059                return;
 1060            }
 1061
 01062            if (Interlocked.Exchange(ref _nestedWrite, NestedState.StreamInUse) == NestedState.StreamInUse)
 01063            {
 01064                throw new NotSupportedException(SR.Format(SR.net_io_invalidnestedcall, "write"));
 1065            }
 1066
 1067            try
 01068            {
 1069#if TARGET_APPLE
 1070                if (SslStreamPal.IsAsyncSecurityContext(_securityContext!))
 1071                {
 1072                    Task task = SslStreamPal.AsyncWriteAsync(_securityContext!, buffer, cancellationToken);
 1073                    await TIOAdapter.WaitAsync(task).ConfigureAwait(false);
 1074                    return;
 1075                }
 1076#endif // TARGET_APPLE
 1077
 01078                ValueTask t = buffer.Length < MaxDataSize ?
 01079                    WriteSingleChunk<TIOAdapter>(buffer, cancellationToken) :
 01080                    WriteAsyncChunked<TIOAdapter>(buffer, cancellationToken);
 01081                await t.ConfigureAwait(false);
 01082            }
 01083            catch (Exception e)
 01084            {
 01085                if (e is IOException || (e is OperationCanceledException && cancellationToken.IsCancellationRequested))
 01086                {
 01087                    throw;
 1088                }
 1089
 01090                throw new IOException(SR.net_io_write, e);
 1091            }
 1092            finally
 01093            {
 01094                _nestedWrite = NestedState.StreamNotInUse;
 01095            }
 01096        }
 1097
 1098        private int CopyDecryptedData(Memory<byte> buffer)
 01099        {
 01100            Debug.Assert(_buffer.DecryptedLength > 0);
 1101
 01102            int copyBytes = Math.Min(_buffer.DecryptedLength, buffer.Length);
 01103            if (copyBytes != 0)
 01104            {
 01105                _buffer.DecryptedReadOnlySpanSliced(copyBytes).CopyTo(buffer.Span);
 01106                _buffer.Discard(copyBytes);
 01107            }
 1108
 01109            return copyBytes;
 01110        }
 1111
 1112        // Returns TLS Frame size including header size.
 1113        private int GetFrameSize(ReadOnlySpan<byte> buffer)
 621114        {
 621115            if (buffer.Length < TlsFrameHelper.HeaderSize)
 311116            {
 311117                return UnknownTlsFrameLength;
 1118            }
 1119
 311120            if (!TlsFrameHelper.TryGetFrameHeader(buffer, ref _lastFrame.Header))
 81121            {
 81122                if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(this, "invalid TLS frame size");
 81123                throw new AuthenticationException(SR.net_frame_read_size);
 1124            }
 1125
 231126            return _lastFrame.Header.Length;
 541127        }
 1128    }
 1129}
 1130

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Buffers;
 5using System.Collections.Generic;
 6using System.Diagnostics;
 7using System.Runtime.CompilerServices;
 8using System.Runtime.ExceptionServices;
 9using System.Security;
 10using System.Security.Authentication;
 11using System.Security.Authentication.ExtendedProtection;
 12using System.Security.Cryptography;
 13using System.Security.Cryptography.X509Certificates;
 14using System.Threading;
 15using System.Threading.Tasks;
 16
 17namespace System.Net.Security
 18{
 19    public partial class SslStream
 20    {
 21
 22
 23        private SafeFreeCredentials? _credentialsHandle;
 24        // Keeps a cache hit alive until SSPI has retained its own credential reference.
 25        private SafeFreeCredentials? _cachedCredentialsHandle;
 26
 27#if TARGET_APPLE
 28        // on OSX, we have two implementations of SafeDeleteContext, so store a reference to the base class
 29        private SafeDeleteContext? _securityContext;
 30#else
 31        internal SafeDeleteSslContext? _securityContext;
 32#endif
 33
 34        private SslConnectionInfo _connectionInfo;
 35        private X509Certificate? _selectedClientCertificate;
 36        private X509Certificate2? _remoteCertificate;
 37        private bool _remoteCertificateExposed;
 38
 39        // -1 for uninitialized, 0 for false, 1 for true, should be accessed via IsLocalClientCertificateUsed property
 3140        private int _localClientCertificateUsed = -1;
 41
 42        // These are the MAX encrypt buffer output sizes, not the actual sizes.
 3143        private int _headerSize = 5; //ATTN must be set to at least 5 by default
 3144        private int _trailerSize = 16;
 3145        private int _maxDataSize = 16354;
 46
 147        private static readonly Oid s_serverAuthOid = new Oid("1.3.6.1.5.5.7.3.1", "1.3.6.1.5.5.7.3.1");
 148        private static readonly Oid s_clientAuthOid = new Oid("1.3.6.1.5.5.7.3.2", "1.3.6.1.5.5.7.3.2");
 49
 50        //
 51        // Protocol properties
 52        //
 53        //   LocalServerCertificate - local certificate for server mode channel
 54        //   LocalClientCertificate - selected certificated used in the client channel mode otherwise null
 55        //   IsRemoteCertificateAvailable - true if the remote side has provided a certificate
 56        //   HeaderSize             - Header & trailer sizes used in the TLS stream
 57        //   TrailerSize -
 58        //
 59        internal X509Certificate? LocalServerCertificate
 60        {
 61            get
 062            {
 063                return _sslAuthenticationOptions.CertificateContext?.TargetCertificate;
 064            }
 65        }
 66
 67        // IsLocalCertificateUsed is expensive, but it does not change during the lifetime of the SslStream except for r
 68        // can cache the value.
 69        private bool IsLocalClientCertificateUsed
 70        {
 71            get
 072            {
 073                if (_localClientCertificateUsed == -1)
 074                {
 075                    _localClientCertificateUsed = CertificateValidationPal.IsLocalCertificateUsed(_credentialsHandle, _s
 076                        ? 1
 077                        : 0;
 078                }
 79
 080                return _localClientCertificateUsed == 1;
 081            }
 82        }
 83
 84        internal X509Certificate? LocalClientCertificate
 85        {
 86            get
 087            {
 088                if (_selectedClientCertificate != null && IsLocalClientCertificateUsed)
 089                {
 090                    return _selectedClientCertificate;
 91                }
 92
 093                return null;
 094            }
 95        }
 96
 97        internal bool IsRemoteCertificateAvailable
 98        {
 99            get
 0100            {
 0101                return _remoteCertificate != null;
 0102            }
 103        }
 104
 105        internal ChannelBinding? GetChannelBinding(ChannelBindingKind kind)
 0106        {
 0107            ChannelBinding? result = null;
 0108            if (_securityContext != null)
 0109            {
 0110                result = SslStreamPal.QueryContextChannelBinding(_securityContext, kind);
 0111            }
 112
 0113            return result;
 0114        }
 115
 116        internal int MaxDataSize
 117        {
 118            get
 0119            {
 0120                return _maxDataSize;
 0121            }
 122        }
 123
 124        internal bool IsValidContext
 125        {
 126            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 127            get
 0128            {
 0129                return !(_securityContext == null || _securityContext.IsInvalid);
 0130            }
 131        }
 132
 133        internal bool RemoteCertRequired
 134        {
 135            get
 136            {
 137                return _sslAuthenticationOptions.RemoteCertRequired;
 138            }
 139        }
 140
 141        internal void CloseContext()
 31142        {
 31143            if (!_remoteCertificateExposed)
 31144            {
 31145                _remoteCertificate?.Dispose();
 31146                _remoteCertificate = null;
 31147            }
 148
 31149            _securityContext?.Dispose();
 31150            _credentialsHandle?.Dispose();
 31151            ReleaseCachedCredentials();
 152
 31153            _sslAuthenticationOptions.Dispose();
 31154        }
 155
 156        private void ReleaseCachedCredentials() =>
 31157            Interlocked.Exchange(ref _cachedCredentialsHandle, null)?.DangerousRelease();
 158
 159        //
 160        // SECURITY: we open a private key container on behalf of the caller
 161        // and we require the caller to have permission associated with that operation.
 162        //
 163        internal static unsafe X509Certificate2? FindCertificateWithPrivateKey(object instance, bool isServer, X509Certi
 0164        {
 0165            if (certificate == null)
 0166            {
 0167                return null;
 168            }
 169
 0170            if (NetEventSource.Log.IsEnabled())
 0171                NetEventSource.Log.LocatingPrivateKey(certificate, instance);
 172
 173            try
 0174            {
 175                // Protecting from X509Certificate2 derived classes.
 0176                X509Certificate2? certEx = MakeEx(certificate);
 177
 0178                if (certEx is null)
 0179                {
 0180                    return null;
 181                }
 182
 0183                if (certEx.HasPrivateKey)
 0184                {
 0185                    if (NetEventSource.Log.IsEnabled())
 0186                        NetEventSource.Log.CertIsType2(instance);
 187
 0188                    return certEx;
 189                }
 190
 0191                Span<byte> certHash = stackalloc byte[SHA512.HashSizeInBytes];
 0192                bool ret = certEx.TryGetCertHash(HashAlgorithmName.SHA512, certHash, out int written);
 0193                Debug.Assert(ret && written == certHash.Length);
 194
 0195                if (!object.ReferenceEquals(certificate, certEx))
 0196                {
 0197                    certEx.Dispose();
 0198                }
 199
 200                // ELSE Try the MY user and machine stores for private key check.
 201                // For server side mode MY machine store takes priority.
 0202                X509Certificate2? found =
 0203                    FindCertWithPrivateKey(isServer, certHash) ??
 0204                    FindCertWithPrivateKey(!isServer, certHash);
 0205                if (found is not null)
 0206                {
 0207                    return found;
 208                }
 209
 210                X509Certificate2? FindCertWithPrivateKey(bool isServer, ReadOnlySpan<byte> certHash)
 0211                {
 0212                    if (CertificateValidationPal.EnsureStoreOpened(isServer) is X509Store store)
 0213                    {
 0214                        X509Certificate2Collection certs = store.Certificates;
 0215                        X509Certificate2Collection found = certs.FindByThumbprint(HashAlgorithmName.SHA512, certHash);
 0216                        X509Certificate2? cert = null;
 217                        try
 0218                        {
 0219                            if (found.Count > 0)
 0220                            {
 0221                                cert = found[0];
 0222                                if (cert.HasPrivateKey)
 0223                                {
 0224                                    if (NetEventSource.Log.IsEnabled())
 0225                                    {
 0226                                        NetEventSource.Log.FoundCertInStore(isServer, instance);
 0227                                    }
 228
 0229                                    return cert;
 230                                }
 0231                            }
 0232                        }
 233                        finally
 0234                        {
 0235                            for (int i = 0; i < certs.Count; i++)
 0236                            {
 0237                                X509Certificate2 toDispose = certs[i];
 0238                                if (!ReferenceEquals(toDispose, cert))
 0239                                {
 0240                                    toDispose.Dispose();
 0241                                }
 0242                            }
 0243                        }
 0244                    }
 245
 0246                    return null;
 0247                }
 0248            }
 0249            catch (CryptographicException)
 0250            {
 0251            }
 252
 0253            if (NetEventSource.Log.IsEnabled())
 0254                NetEventSource.Log.NotFoundCertInStore(instance);
 0255            return null;
 0256        }
 257
 258        private static X509Certificate2? MakeEx(X509Certificate certificate)
 0259        {
 0260            Debug.Assert(certificate != null);
 261
 0262            if (certificate.GetType() == typeof(X509Certificate2))
 0263            {
 0264                return (X509Certificate2)certificate;
 265            }
 266
 0267            X509Certificate2? certificateEx = null;
 268            try
 0269            {
 0270                if (certificate.Handle != IntPtr.Zero)
 0271                {
 0272                    certificateEx = new X509Certificate2(certificate);
 0273                }
 0274            }
 0275            catch (SecurityException) { }
 0276            catch (CryptographicException) { }
 277
 0278            return certificateEx;
 0279        }
 280
 281        //
 282        // Get certificate_authorities list, according to RFC 5246, Section 7.4.4.
 283        // Used only by client SSL code, never returns null.
 284        //
 285        private string[] GetRequestCertificateAuthorities()
 0286        {
 0287            string[] issuers = Array.Empty<string>();
 288
 0289            if (IsValidContext)
 0290            {
 0291                issuers = CertificateValidationPal.GetRequestCertificateAuthorities(_securityContext!);
 0292            }
 0293            return issuers;
 0294        }
 295
 296        internal X509Certificate2? SelectClientCertificate()
 0297        {
 0298            X509Certificate? clientCertificate = null;        // candidate certificate that can come from the user callb
 0299            X509Certificate2? selectedCert = null;            // final selected cert (ensured that it does have private 
 0300            List<X509Certificate>? filteredCerts = null;      // This is an intermediate client certs collection that tr
 301            string[] issuers;                                 // This is a list of issuers sent by the server, only vali
 302
 0303            if (_sslAuthenticationOptions.CertificateContext != null)
 0304            {
 0305                if (NetEventSource.Log.IsEnabled())
 0306                    NetEventSource.Log.CertificateFromCertContext(this);
 307
 308                //
 309                // SslStreamCertificateContext can only be constructed with a cert with a
 310                // private key, so we don't have to do any further processing.
 311                //
 312
 0313                _selectedClientCertificate = _sslAuthenticationOptions.CertificateContext.TargetCertificate;
 0314                if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"Selected cert = {_selectedClientCertific
 0315                return _sslAuthenticationOptions.CertificateContext.TargetCertificate;
 316            }
 0317            else if (_sslAuthenticationOptions.CertSelectionDelegate != null)
 0318            {
 0319                if (NetEventSource.Log.IsEnabled())
 0320                    NetEventSource.Info(this, "Calling CertificateSelectionCallback");
 321
 0322                X509Certificate2? remoteCert = null;
 323                try
 0324                {
 0325                    issuers = GetRequestCertificateAuthorities();
 0326                    remoteCert = CertificateValidationPal.GetRemoteCertificate(_securityContext);
 0327                    _sslAuthenticationOptions.ClientCertificates ??= new X509CertificateCollection();
 0328                    clientCertificate = _sslAuthenticationOptions.CertSelectionDelegate(this, _sslAuthenticationOptions.
 0329                }
 330                finally
 0331                {
 0332                    remoteCert?.Dispose();
 0333                }
 334
 0335                if (clientCertificate != null)
 0336                {
 0337                    EnsureInitialized(ref filteredCerts).Add(clientCertificate);
 0338                    if (NetEventSource.Log.IsEnabled())
 0339                        NetEventSource.Log.CertificateFromDelegate(this);
 0340                }
 341                else
 0342                {
 0343                    if (_sslAuthenticationOptions.ClientCertificates == null || _sslAuthenticationOptions.ClientCertific
 0344                    {
 0345                        if (NetEventSource.Log.IsEnabled())
 0346                            NetEventSource.Log.NoDelegateNoClientCert(this);
 0347                    }
 348                    else
 0349                    {
 0350                        if (NetEventSource.Log.IsEnabled())
 0351                            NetEventSource.Log.NoDelegateButClientCert(this);
 0352                    }
 0353                }
 0354            }
 0355            else if (_credentialsHandle == null && _sslAuthenticationOptions.ClientCertificates != null && _sslAuthentic
 0356            {
 357                // This is where we attempt to restart a session by picking the FIRST cert from the collection.
 358                // Otherwise it is either server sending a client cert request or the session is renegotiated.
 0359                clientCertificate = _sslAuthenticationOptions.ClientCertificates[0];
 0360                if (clientCertificate != null)
 0361                {
 0362                    EnsureInitialized(ref filteredCerts).Add(clientCertificate);
 0363                }
 364
 0365                if (NetEventSource.Log.IsEnabled())
 0366                    NetEventSource.Log.AttemptingRestartUsingCert(clientCertificate, this);
 0367            }
 0368            else if (_sslAuthenticationOptions.ClientCertificates != null && _sslAuthenticationOptions.ClientCertificate
 0369            {
 370                //
 371                // This should be a server request for the client cert sent over currently anonymous sessions.
 372                //
 0373                issuers = GetRequestCertificateAuthorities();
 374
 0375                if (NetEventSource.Log.IsEnabled())
 0376                {
 0377                    if (issuers == null || issuers.Length == 0)
 0378                    {
 0379                        NetEventSource.Log.NoIssuersTryAllCerts(this);
 0380                    }
 381                    else
 0382                    {
 0383                        NetEventSource.Log.LookForMatchingCerts(issuers.Length, this);
 0384                    }
 0385                }
 386
 0387                for (int i = 0; i < _sslAuthenticationOptions.ClientCertificates.Count; ++i)
 0388                {
 389                    //
 390                    // Make sure we add only if the cert matches one of the issuers.
 391                    // If no issuers were sent and then try all client certs starting with the first one.
 392                    //
 0393                    if (issuers != null && issuers.Length != 0)
 0394                    {
 0395                        X509Certificate2? certificateEx = null;
 0396                        X509Chain? chain = null;
 397                        try
 0398                        {
 0399                            certificateEx = MakeEx(_sslAuthenticationOptions.ClientCertificates[i]);
 0400                            if (certificateEx == null)
 0401                            {
 0402                                continue;
 403                            }
 404
 0405                            if (NetEventSource.Log.IsEnabled())
 0406                                NetEventSource.Info(this, $"Root cert: {certificateEx}");
 407
 0408                            chain = new X509Chain();
 409
 0410                            chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
 0411                            chain.ChainPolicy.VerificationFlags = X509VerificationFlags.IgnoreInvalidName;
 0412                            chain.Build(certificateEx);
 0413                            bool found = false;
 414
 415                            //
 416                            // We ignore any errors happened with chain.
 417                            //
 0418                            if (chain.ChainElements.Count > 0)
 0419                            {
 0420                                int elementsCount = chain.ChainElements.Count;
 0421                                for (int ii = 0; ii < elementsCount; ++ii)
 0422                                {
 0423                                    string issuer = chain.ChainElements[ii].Certificate!.Issuer;
 0424                                    found = Array.IndexOf(issuers, issuer) >= 0;
 0425                                    if (found)
 0426                                    {
 0427                                        if (NetEventSource.Log.IsEnabled())
 0428                                            NetEventSource.Info(this, $"Matched {issuer}");
 0429                                        break;
 430                                    }
 0431                                    if (NetEventSource.Log.IsEnabled())
 0432                                        NetEventSource.Info(this, $"No match: {issuer}");
 0433                                }
 0434                            }
 435
 0436                            if (!found)
 0437                            {
 0438                                continue;
 439                            }
 0440                        }
 441                        finally
 0442                        {
 0443                            if (chain != null)
 0444                            {
 0445                                int elementsCount = chain.ChainElements.Count;
 0446                                for (int element = 0; element < elementsCount; element++)
 0447                                {
 0448                                    chain.ChainElements[element].Certificate.Dispose();
 0449                                }
 450
 0451                                chain.Dispose();
 0452                            }
 453
 0454                            if (certificateEx != null && (object)certificateEx != (object)_sslAuthenticationOptions.Clie
 0455                            {
 0456                                certificateEx.Dispose();
 0457                            }
 0458                        }
 0459                    }
 460
 0461                    if (NetEventSource.Log.IsEnabled())
 0462                        NetEventSource.Log.SelectedCert(_sslAuthenticationOptions.ClientCertificates[i], this);
 463
 0464                    EnsureInitialized(ref filteredCerts).Add(_sslAuthenticationOptions.ClientCertificates[i]);
 0465                }
 0466            }
 467
 0468            clientCertificate = null;
 469
 0470            if (NetEventSource.Log.IsEnabled())
 0471            {
 0472                if (filteredCerts != null && filteredCerts.Count != 0)
 0473                {
 0474                    NetEventSource.Log.CertsAfterFiltering(filteredCerts.Count, this);
 0475                    NetEventSource.Log.FindingMatchingCerts(this);
 0476                }
 477                else
 0478                {
 0479                    NetEventSource.Log.CertsAfterFiltering(0, this);
 0480                    NetEventSource.Info(this, "No client certificate to choose from");
 0481                }
 0482            }
 483
 484            //
 485            // ATTN: When the client cert was returned by the user callback OR it was guessed AND it has no private key,
 486            //       THEN anonymous (no client cert) credential will be used.
 487            //
 488            // SECURITY: Accessing X509 cert Credential is disabled for semitrust.
 489            // We no longer need to demand for unmanaged code permissions.
 490            // FindCertificateWithPrivateKey should do the right demand for us.
 0491            if (filteredCerts != null)
 0492            {
 0493                for (int i = 0; i < filteredCerts.Count; ++i)
 0494                {
 0495                    clientCertificate = filteredCerts[i];
 0496                    if ((selectedCert = FindCertificateWithPrivateKey(this, _sslAuthenticationOptions.IsServer, clientCe
 0497                    {
 0498                        break;
 499                    }
 500
 0501                    clientCertificate = null;
 0502                    selectedCert = null;
 0503                }
 0504            }
 505
 0506            Debug.Assert((object?)clientCertificate == (object?)selectedCert || clientCertificate!.Equals(selectedCert),
 507
 0508            if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"Selected cert = {selectedCert}");
 509
 0510            _selectedClientCertificate = clientCertificate;
 511
 0512            return selectedCert;
 0513        }
 514
 515        /*++
 516            AcquireCredentials - Attempts to find Client Credential
 517            Information, that can be sent to the server.  In our case,
 518            this is only Client Certificates, that we have Credential Info.
 519
 520            How it works:
 521                case 0: Cert Selection delegate is present
 522                        Always use its result as the client cert answer.
 523                        Try to use cached credential handle whenever feasible.
 524                        Do not use cached anonymous creds if the delegate has returned null
 525                        and the collection is not empty (allow responding with the cert later).
 526
 527                case 1: Certs collection is empty
 528                        Always use the same statically acquired anonymous SSL Credential
 529
 530                case 2: Before our Connection with the Server
 531                        If we have a cached credential handle keyed by first X509Certificate
 532                        **content** in the passed collection, then we use that cached
 533                        credential and hoping to restart a session.
 534
 535                        Otherwise create a new anonymous (allow responding with the cert later).
 536
 537                case 3: After our Connection with the Server (i.e. during handshake or re-handshake)
 538                        The server has requested that we send it a Certificate then
 539                        we Enumerate a list of server sent Issuers trying to match against
 540                        our list of Certificates, the first match is sent to the server.
 541
 542                        Once we got a cert we again try to match cached credential handle if possible.
 543                        This will not restart a session but helps minimizing the number of handles we create.
 544
 545                In the case of an error getting a Certificate or checking its private Key we fall back
 546                to the behavior of having no certs, case 1.
 547
 548            Returns: True if cached creds were used, false otherwise.
 549
 550        --*/
 551
 552        internal bool AcquireClientCredentials(ref byte[]? thumbPrint, bool newCredentialsRequested = false)
 553        {
 0554            ReleaseCachedCredentials();
 555
 556            // Acquire possible Client Certificate information and set it on the handle.
 0557            bool cachedCred = false;                   // this is a return result from this method.
 558
 0559            X509Certificate2? selectedCert = SelectClientCertificate();
 560
 0561            if (newCredentialsRequested)
 0562            {
 0563                UpdateCertificateContext(selectedCert);
 564
 0565                if (SslStreamPal.TryUpdateClintCertificate(_credentialsHandle, _securityContext, _sslAuthenticationOptio
 566                {
 567                    // If the certificate was updated we do not need to deal with the credential handle.
 568                    return false;
 569                }
 0570            }
 571
 0572            SslStreamCertificateContext? certificateContextToRestore = null;
 573            try
 0574            {
 575                // Try to locate cached creds first.
 576                //
 577                // SECURITY: selectedCert ref if not null is a safe object that does not depend on possible **user** inh
 578                //
 0579                byte[]? guessedThumbPrint = selectedCert?.GetCertHash(HashAlgorithmName.SHA512);
 0580                SafeFreeCredentials? cachedCredentialHandle = SslSessionsCache.TryCachedCredential(
 0581                    guessedThumbPrint,
 0582                    _sslAuthenticationOptions.EnabledSslProtocols,
 0583                    _sslAuthenticationOptions.IsServer,
 0584                    _sslAuthenticationOptions.EncryptionPolicy,
 0585                    _sslAuthenticationOptions.CertificateRevocationCheckMode != X509RevocationMode.NoCheck,
 0586                    _sslAuthenticationOptions.AllowTlsResume,
 0587                    sendTrustList: false,
 0588                    _sslAuthenticationOptions.AllowRsaPssPadding,
 0589                    _sslAuthenticationOptions.AllowRsaPkcs1Padding);
 0590                Volatile.Write(ref _cachedCredentialsHandle, cachedCredentialHandle);
 591
 592                // We can probably do some optimization here. If the selectedCert is returned by the delegate
 593                // we can always go ahead and use the certificate to create our credential
 594                // (instead of going anonymous as we do here).
 0595                if (!newCredentialsRequested &&
 0596                    cachedCredentialHandle == null &&
 0597                    selectedCert != null &&
 0598                    SslStreamPal.StartMutualAuthAsAnonymous)
 0599                {
 0600                    if (NetEventSource.Log.IsEnabled())
 0601                        NetEventSource.Info(this, "Reset to anonymous session.");
 602
 603                    // IIS does not renegotiate a restarted session if client cert is needed.
 604                    // So we don't want to reuse **anonymous** cached credential for a new SSL connection if the client 
 605                    // The following block happens if client did specify a certificate but no cached creds were found in
 606                    // Since we don't restart a session the server side can still challenge for a client cert.
 0607                    if ((object?)_selectedClientCertificate != (object?)selectedCert)
 0608                    {
 0609                        selectedCert.Dispose();
 0610                    }
 611
 0612                    guessedThumbPrint = null;
 0613                    selectedCert = null;
 0614                    _selectedClientCertificate = null;
 0615                    certificateContextToRestore = _sslAuthenticationOptions.CertificateContext;
 0616                    _sslAuthenticationOptions.CertificateContext = null;
 0617                }
 618
 0619                if (cachedCredentialHandle != null)
 0620                {
 0621                    if (NetEventSource.Log.IsEnabled())
 0622                        NetEventSource.Log.UsingCachedCredential(this);
 0623                    _credentialsHandle = cachedCredentialHandle;
 0624                    cachedCred = true;
 0625                    UpdateCertificateContext(selectedCert);
 0626                }
 627                else
 0628                {
 0629                    UpdateCertificateContext(selectedCert);
 630
 0631                    _credentialsHandle = AcquireCredentialsHandle(_sslAuthenticationOptions, newCredentialsRequested);
 0632                    thumbPrint = guessedThumbPrint; // Delay until here in case something above threw.
 0633                }
 0634            }
 635            finally
 0636            {
 0637                UpdateCertificateContext(selectedCert);
 0638                if (certificateContextToRestore is not null)
 0639                {
 0640                    Debug.Assert(_sslAuthenticationOptions.CertificateContext is null);
 0641                    _sslAuthenticationOptions.CertificateContext = certificateContextToRestore;
 0642                }
 0643            }
 644
 0645            return cachedCred;
 646
 647            void UpdateCertificateContext(X509Certificate2? cert)
 0648            {
 0649                if (cert != null && _sslAuthenticationOptions.CertificateContext == null)
 0650                {
 0651                    _sslAuthenticationOptions.SetCertificateContextFromCert(cert);
 0652                }
 0653            }
 0654        }
 655
 0656        private static List<T> EnsureInitialized<T>(ref List<T>? list) => list ??= new List<T>();
 657
 658        //
 659        // Acquire Server Side Certificate information and set it on the class.
 660        //
 661        private bool AcquireServerCredentials(ref byte[]? thumbPrint)
 0662        {
 0663            ReleaseCachedCredentials();
 664
 0665            X509Certificate? localCertificate = null;
 0666            X509Certificate2? selectedCert = null;
 0667            bool cachedCred = false;
 668
 669            // There are three options for selecting the server certificate. When
 670            // selecting which to use, we prioritize the new ServerCertSelectionDelegate
 671            // API. If the new API isn't used we call LocalCertSelectionCallback (for compat
 672            // with .NET Framework), and if neither is set we fall back to using CertificateContext.
 0673            if (_sslAuthenticationOptions.ServerCertSelectionDelegate != null)
 0674            {
 0675                localCertificate = _sslAuthenticationOptions.ServerCertSelectionDelegate(this, _sslAuthenticationOptions
 0676                if (localCertificate == null)
 0677                {
 0678                    if (NetEventSource.Log.IsEnabled())
 0679                        NetEventSource.Error(this, $"ServerCertSelectionDelegate returned no certificate for '{_sslAuthe
 0680                    throw new AuthenticationException(SR.net_ssl_io_no_server_cert);
 681                }
 682
 0683                if (NetEventSource.Log.IsEnabled())
 0684                    NetEventSource.Info(this, "ServerCertSelectionDelegate selected Cert");
 0685            }
 0686            else if (_sslAuthenticationOptions.CertSelectionDelegate != null)
 0687            {
 0688                X509CertificateCollection tempCollection = new X509CertificateCollection();
 0689                tempCollection.Add(_sslAuthenticationOptions.CertificateContext!.TargetCertificate!);
 690                // We pass string.Empty here to maintain strict compatibility with .NET Framework.
 0691                localCertificate = _sslAuthenticationOptions.CertSelectionDelegate(this, string.Empty, tempCollection, n
 0692                if (localCertificate == null)
 0693                {
 0694                    if (NetEventSource.Log.IsEnabled())
 0695                        NetEventSource.Error(this, $"CertSelectionDelegate returned no certificaete for '{_sslAuthentica
 0696                    throw new NotSupportedException(SR.net_ssl_io_no_server_cert);
 697                }
 698
 0699                if (NetEventSource.Log.IsEnabled())
 0700                    NetEventSource.Info(this, "CertSelectionDelegate selected Cert");
 0701            }
 0702            else if (_sslAuthenticationOptions.CertificateContext != null)
 0703            {
 0704                selectedCert = _sslAuthenticationOptions.CertificateContext.TargetCertificate;
 0705            }
 706
 0707            if (selectedCert == null)
 0708            {
 709                // We will get here if certificate was selected via legacy callback using X509Certificate
 710                // Fail immediately if no certificate was given.
 0711                if (localCertificate == null)
 0712                {
 0713                    if (NetEventSource.Log.IsEnabled())
 0714                        NetEventSource.Error(this, "Certiticate callback returned no certificaete.");
 0715                    throw new NotSupportedException(SR.net_ssl_io_no_server_cert);
 716                }
 717
 718                // SECURITY: Accessing X509 cert Credential is disabled for semitrust.
 719                // We no longer need to demand for unmanaged code permissions.
 720                // EnsurePrivateKey should do the right demand for us.
 0721                selectedCert = FindCertificateWithPrivateKey(this, _sslAuthenticationOptions.IsServer, localCertificate)
 722
 0723                if (selectedCert == null)
 0724                {
 0725                    throw new NotSupportedException(SR.net_ssl_io_no_server_cert);
 726                }
 727
 0728                Debug.Assert(localCertificate.Equals(selectedCert), "'selectedCert' does not match 'localCertificate'.")
 0729                _sslAuthenticationOptions.SetCertificateContextFromCert(selectedCert);
 0730            }
 731
 0732            Debug.Assert(_sslAuthenticationOptions.CertificateContext != null);
 733            //
 734            // Note selectedCert is a safe ref possibly cloned from the user passed Cert object
 735            //
 0736            byte[] guessedThumbPrint = selectedCert.GetCertHash(HashAlgorithmName.SHA512); bool sendTrustedList = _sslAu
 0737            SafeFreeCredentials? cachedCredentialHandle = SslSessionsCache.TryCachedCredential(guessedThumbPrint,
 0738                                                                _sslAuthenticationOptions.EnabledSslProtocols,
 0739                                                                _sslAuthenticationOptions.IsServer,
 0740                                                                _sslAuthenticationOptions.EncryptionPolicy,
 0741                                                                _sslAuthenticationOptions.CertificateRevocationCheckMode
 0742                                                                _sslAuthenticationOptions.AllowTlsResume,
 0743                                                                sendTrustedList,
 0744                                                                _sslAuthenticationOptions.AllowRsaPssPadding,
 0745                                                                _sslAuthenticationOptions.AllowRsaPkcs1Padding);
 0746            Volatile.Write(ref _cachedCredentialsHandle, cachedCredentialHandle);
 0747            if (cachedCredentialHandle != null)
 0748            {
 0749                _credentialsHandle = cachedCredentialHandle;
 0750                cachedCred = true;
 0751            }
 752            else
 0753            {
 0754                _credentialsHandle = AcquireCredentialsHandle(_sslAuthenticationOptions);
 0755                thumbPrint = guessedThumbPrint;
 0756            }
 757
 0758            return cachedCred;
 0759        }
 760
 761        private static SafeFreeCredentials? AcquireCredentialsHandle(SslAuthenticationOptions sslAuthenticationOptions, 
 0762        {
 0763            SafeFreeCredentials? cred = SslStreamPal.AcquireCredentialsHandle(sslAuthenticationOptions, newCredentialsRe
 764
 0765            if (sslAuthenticationOptions.CertificateContext != null && cred != null)
 0766            {
 767                //
 768                // Since the SafeFreeCredentials can be cached and reused, it may happen on long running processes that 
 769                // the chain expires and all subsequent connections would send expired intermediate certificates. Find t
 770                // NotAfter timestamp on the chain and use it as expiration timestamp for the credentials.
 771                // This provides an opportunity to recreate the credentials with an alternative (and still valid)
 772                // certificate chain.
 773                //
 0774                SslStreamCertificateContext certificateContext = sslAuthenticationOptions.CertificateContext;
 0775                cred._expiry = GetExpiryTimestamp(certificateContext);
 776
 0777                if (cred._expiry < DateTime.UtcNow)
 0778                {
 779                    //
 780                    // The CertificateContext from auth options is recreated just before creating the SafeFreeCredential
 781                    // it was provided by the user code, it may still contain the (now expired) certificate chain. Such 
 782                    // effectively disable caching as it would lead to creating new credentials for each connection. We 
 783                    // a temporary certificate context (which builds a new chain with hopefully more recent chain).
 784                    //
 0785                    certificateContext = certificateContext.Duplicate();
 0786                    cred._expiry = GetExpiryTimestamp(certificateContext);
 0787                }
 788
 789                static DateTime GetExpiryTimestamp(SslStreamCertificateContext certificateContext)
 0790                {
 0791                    DateTime expiry = certificateContext.TargetCertificate.NotAfter;
 792
 0793                    foreach (X509Certificate2 cert in certificateContext.IntermediateCertificates)
 0794                    {
 0795                        if (cert.NotAfter < expiry)
 0796                        {
 0797                            expiry = cert.NotAfter;
 0798                        }
 0799                    }
 800
 0801                    return expiry.ToUniversalTime();
 0802                }
 0803            }
 804
 0805            return cred;
 0806        }
 807
 808        //
 809        internal ProtocolToken NextMessage(ReadOnlySpan<byte> incomingBuffer, out int consumed)
 0810        {
 0811            if (!LocalAppContextSwitches.UseLegacySslStreamHandshake &&
 0812                TryNextMessageViaTlsSession(incomingBuffer, out ProtocolToken wedged, out consumed))
 0813            {
 0814                if (NetEventSource.Log.IsEnabled() && wedged.Failed)
 0815                {
 0816                    NetEventSource.Error(this, $"Authentication failed. Status: {wedged.Status}, Exception message: {wed
 0817                }
 0818                return wedged;
 819            }
 820
 0821            ProtocolToken token = GenerateToken(incomingBuffer, out consumed);
 0822            if (NetEventSource.Log.IsEnabled())
 0823            {
 0824                if (token.Failed)
 0825                {
 0826                    NetEventSource.Error(this, $"Authentication failed. Status: {token.Status}, Exception message: {toke
 0827                }
 0828            }
 829
 0830            return token;
 0831        }
 832
 833        private partial bool TryNextMessageViaTlsSession(ReadOnlySpan<byte> incomingBuffer, out ProtocolToken token, out
 834
 835        /*++
 836            GenerateToken - Called after each successive state
 837            in the Client - Server handshake.  This function
 838            generates a set of bytes that will be sent next to
 839            the server.  The server responds, each response,
 840            is pass then into this function, again, and the cycle
 841            repeats until successful connection, or failure.
 842
 843            Input:
 844                input  - bytes from the wire
 845            Return:
 846                token - ProtocolToken with status and optionally buffer.
 847        --*/
 848        private ProtocolToken GenerateToken(ReadOnlySpan<byte> inputBuffer, out int consumed)
 0849        {
 0850            bool cachedCreds = false;
 0851            bool sendTrustList = false;
 0852            byte[]? thumbPrint = null;
 853
 0854            ProtocolToken token = default;
 0855            token.RentBuffer = true;
 856
 857            // We need to try get credentials at the beginning.
 858            // _credentialsHandle may be always null on some platforms but
 859            // _securityContext will be allocated on first call.
 0860            bool refreshCredentialNeeded = _securityContext == null;
 861            try
 0862            {
 863                do
 0864                {
 0865                    thumbPrint = null;
 0866                    if (refreshCredentialNeeded)
 0867                    {
 0868                        cachedCreds = _sslAuthenticationOptions.IsServer
 0869                                        ? AcquireServerCredentials(ref thumbPrint)
 0870                                        : AcquireClientCredentials(ref thumbPrint);
 0871                    }
 872
 0873                    if (_sslAuthenticationOptions.IsServer)
 0874                    {
 0875                        sendTrustList = _sslAuthenticationOptions.CertificateContext?.Trust?._sendTrustInHandshake ?? fa
 876
 0877                        token = SslStreamPal.AcceptSecurityContext(
 0878                                      ref _credentialsHandle!,
 0879                                      ref _securityContext,
 0880                                      inputBuffer,
 0881                                      out consumed,
 0882                                      _sslAuthenticationOptions);
 0883                        if (token.Status.ErrorCode == SecurityStatusPalErrorCode.HandshakeStarted)
 0884                        {
 0885                            token.Status = SslStreamPal.SelectApplicationProtocol(
 0886                                        _credentialsHandle!,
 0887                                        _securityContext!,
 0888                                        _sslAuthenticationOptions,
 0889                                        _lastFrame.RawApplicationProtocols);
 890
 0891                            if (token.Status.ErrorCode == SecurityStatusPalErrorCode.OK)
 0892                            {
 0893                                token = SslStreamPal.AcceptSecurityContext(
 0894                                        ref _credentialsHandle!,
 0895                                        ref _securityContext,
 0896                                        ReadOnlySpan<byte>.Empty,
 0897                                        out _,
 0898                                        _sslAuthenticationOptions);
 0899                            }
 0900                        }
 0901                    }
 902                    else
 0903                    {
 0904                        string hostName = TargetHostNameHelper.NormalizeHostName(_sslAuthenticationOptions.TargetHost);
 0905                        token = SslStreamPal.InitializeSecurityContext(
 0906                                       ref _credentialsHandle!,
 0907                                       ref _securityContext,
 0908                                       hostName,
 0909                                       inputBuffer,
 0910                                       out consumed,
 0911                                       _sslAuthenticationOptions);
 912
 0913                        if (token.Status.ErrorCode == SecurityStatusPalErrorCode.CredentialsNeeded)
 0914                        {
 0915                            if (NetEventSource.Log.IsEnabled())
 0916                                NetEventSource.Info(this, "InitializeSecurityContext() returned 'CredentialsNeeded'.");
 917
 0918                            refreshCredentialNeeded = true;
 0919                            cachedCreds = AcquireClientCredentials(ref thumbPrint, newCredentialsRequested: true);
 920
 0921                            token = SslStreamPal.InitializeSecurityContext(
 0922                                       ref _credentialsHandle!,
 0923                                       ref _securityContext,
 0924                                       hostName,
 0925                                       ReadOnlySpan<byte>.Empty,
 0926                                       out _,
 0927                                       _sslAuthenticationOptions);
 0928                        }
 0929                    }
 930
 931#if TARGET_APPLE
 932                    if (token.Status.ErrorCode == SecurityStatusPalErrorCode.CertValidationNeeded)
 933                    {
 934                        token = VerifyRemoteCertificateAndGenerateNextToken(token);
 935                    }
 936#endif
 0937                } while (cachedCreds && _credentialsHandle == null);
 0938            }
 939            finally
 0940            {
 0941                ReleaseCachedCredentials();
 0942                if (refreshCredentialNeeded)
 0943                {
 944                    //
 945                    // Assuming the ISC or ASC has referenced the credential,
 946                    // we want to call dispose so to decrement the effective ref count.
 947                    //
 0948                    _credentialsHandle?.Dispose();
 949
 950                    //
 951                    // This call may bump up the credential reference count further.
 952                    // Note that thumbPrint is retrieved from a safe cert object that was possible cloned from the user 
 953                    //
 0954                    if (!cachedCreds && _securityContext != null && !_securityContext.IsInvalid && _credentialsHandle !=
 0955                    {
 0956                        SslSessionsCache.CacheCredential(
 0957                            _credentialsHandle,
 0958                            thumbPrint,
 0959                            _sslAuthenticationOptions.EnabledSslProtocols,
 0960                            _sslAuthenticationOptions.IsServer,
 0961                            _sslAuthenticationOptions.EncryptionPolicy,
 0962                            _sslAuthenticationOptions.CertificateRevocationCheckMode != X509RevocationMode.NoCheck,
 0963                            _sslAuthenticationOptions.AllowTlsResume,
 0964                            sendTrustList,
 0965                            _sslAuthenticationOptions.AllowRsaPssPadding,
 0966                            _sslAuthenticationOptions.AllowRsaPkcs1Padding);
 0967                    }
 0968                }
 0969            }
 970
 0971            return token;
 0972        }
 973
 974#if TARGET_APPLE
 975        private ProtocolToken VerifyRemoteCertificateAndGenerateNextToken(ProtocolToken token)
 976        {
 977            // SecureTransport pauses the handshake (errSSL{Server,Client}AuthCompleted) before
 978            // any bytes are produced for the next handshake flight, so the pending-writes buffer
 979            // drained into token should be empty here. Assert to catch any future regression
 980            // that would silently drop handshake bytes.
 981            Debug.Assert(token.Size == 0, "Expected empty payload at CertValidationNeeded pause; dropping non-empty payl
 982            token.ReleasePayload();
 983
 984            ProtocolToken alertToken = default;
 985            SslPolicyErrors sslPolicyErrors = SslPolicyErrors.None;
 986
 987            if (!VerifyRemoteCertificate(_sslAuthenticationOptions.CertificateContext?.Trust, ref alertToken, ref sslPol
 988            {
 989                alertToken.Status = new SecurityStatusPal(SecurityStatusPalErrorCode.CertValidationFailed, CreateCertifi
 990                return alertToken;
 991            }
 992
 993            return GenerateToken(ReadOnlySpan<byte>.Empty, out _);
 994        }
 995#endif
 996
 997        internal ProtocolToken Renegotiate()
 0998        {
 0999            Debug.Assert(_securityContext != null);
 1000
 01001            return SslStreamPal.Renegotiate(
 01002                                      ref _credentialsHandle!,
 01003                                      ref _securityContext,
 01004                                      _sslAuthenticationOptions);
 01005        }
 1006
 1007        /*++
 1008            ProcessHandshakeSuccess -
 1009               Called on successful completion of Handshake -
 1010               used to set header/trailer sizes for encryption use
 1011
 1012            Fills in the information about established protocol
 1013        --*/
 1014        internal void ProcessHandshakeSuccess()
 01015        {
 01016            SslStreamPal.QueryContextStreamSizes(_securityContext!, out StreamSizes streamSizes);
 1017
 01018            _headerSize = streamSizes.Header;
 01019            _trailerSize = streamSizes.Trailer;
 01020            _maxDataSize = streamSizes.MaximumMessage;
 01021            Debug.Assert(_maxDataSize > 0);
 1022
 01023            SslStreamPal.QueryContextConnectionInfo(_securityContext!, ref _connectionInfo);
 1024#if DEBUG
 01025            if (NetEventSource.Log.IsEnabled())
 01026            {
 1027                // This keeps the property alive only for tests via reflection
 1028                // Otherwise it could be optimized out as it is not used by production code.
 01029                NetEventSource.Info(this, $"TLS resumed {_connectionInfo.TlsResumed}");
 01030            }
 1031#endif
 01032        }
 1033
 1034        private ProtocolToken EncryptData(ReadOnlyMemory<byte> buffer)
 01035        {
 01036            ThrowIfExceptionalOrNotAuthenticated();
 1037
 01038            lock (_handshakeLock)
 01039            {
 01040                if (_handshakeWaiter != null)
 01041                {
 01042                    ProtocolToken waitToken = default;
 1043                    // avoid waiting under lock.
 01044                    waitToken.Status = new SecurityStatusPal(SecurityStatusPalErrorCode.TryAgain);
 01045                    return waitToken;
 1046                }
 1047
 01048                if (NetEventSource.Log.IsEnabled()) NetEventSource.DumpBuffer(this, buffer.Span);
 1049
 01050                ProtocolToken token = SslStreamPal.EncryptMessage(
 01051                    _securityContext!,
 01052                    buffer,
 01053                    _headerSize,
 01054                    _trailerSize);
 1055
 01056                if (token.Status.ErrorCode != SecurityStatusPalErrorCode.OK)
 01057                {
 01058                    if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(this, $"ERROR {token.Status}");
 01059                }
 1060
 01061                return token;
 1062            }
 01063        }
 1064
 1065        // On some platforms, the platform APIs decrypt in-place via single
 1066        // call (Schannel), while others have separate write-ciphertext +
 1067        // read-plaintext primitives. To allow the most efficient thing (copying
 1068        // plaintext straight to the `destination` buffer provided by the
 1069        // SslStream caller) on platforms that support it, the contract of this
 1070        // method is as follows:
 1071        //  - After the call, first `bytesWritten` bytes of `destination` contain decrypted plaintext
 1072        //  - Rest of the decrypted plaintext, if any, is stored in `_buffer.DecryptedSpan`.
 1073        private SecurityStatusPal DecryptData(int frameSize, Span<byte> destination, out int bytesWritten)
 01074        {
 1075            SecurityStatusPal status;
 1076
 01077            lock (_handshakeLock)
 01078            {
 01079                ThrowIfExceptionalOrNotAuthenticated();
 1080
 01081                status = SslStreamPal.DecryptMessage(
 01082                    _securityContext!,
 01083                    _buffer.EncryptedSpanSliced(frameSize),
 01084                    destination,
 01085                    out bytesWritten,
 01086                    out int leftoverOffset,
 01087                    out int leftoverLength);
 1088
 01089                _buffer.OnDecrypted(leftoverOffset, leftoverLength, frameSize);
 1090
 01091                if (NetEventSource.Log.IsEnabled() && status.ErrorCode == SecurityStatusPalErrorCode.OK)
 01092                {
 01093                    if (bytesWritten > 0)
 01094                    {
 01095                        NetEventSource.DumpBuffer(this, destination.Slice(0, bytesWritten));
 01096                    }
 1097
 01098                    if (_buffer.DecryptedSpan.Length > 0)
 01099                    {
 01100                        NetEventSource.DumpBuffer(this, _buffer.DecryptedSpan);
 01101                    }
 01102                }
 1103
 01104                if (status.ErrorCode == SecurityStatusPalErrorCode.Renegotiate)
 01105                {
 1106                    // The status indicates that the peer or TLS implementation requires additional
 1107                    // handshake/session processing. In practice, there can be other reasons too,
 1108                    // like TLS1.3 session creation or alert handling. We need to pass the data to
 1109                    // the underlying security provider and it is not safe to do parallel write any
 1110                    // more as that can change TLS state and the EncryptData() can fail in strange ways.
 1111
 1112                    // To handle this we call DecryptData() under lock and we create TCS waiter.
 1113                    // EncryptData() checks that under same lock and if it exist it will not call low-level crypto.
 1114                    // Instead it will wait synchronously or asynchronously and it will try again after the wait.
 1115                    // The result will be set when ReplyOnReAuthenticationAsync() is finished e.g. lsass business is ove
 1116                    // If that happen before EncryptData() runs, _handshakeWaiter will be set to null
 1117                    // and EncryptData() will work normally e.g. no waiting, just exclusion with DecryptData()
 1118
 01119                    if (_sslAuthenticationOptions.AllowRenegotiation || SslProtocol == SslProtocols.Tls13 || _nestedAuth
 01120                    {
 1121                        // create TCS only if we plan to proceed. If not, we will throw later outside of the lock.
 1122                        // Tls1.3 does not have renegotiation. However on Windows this error code is used
 1123                        // for session management e.g. anything lsass needs to see.
 1124                        // We also allow it when explicitly requested using RenegotiateAsync().
 01125                        _handshakeWaiter = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchrono
 01126                    }
 01127                }
 01128            }
 1129
 01130            return status;
 01131        }
 1132
 1133        /*++
 1134            VerifyRemoteCertificate - Validates the content of a Remote Certificate
 1135
 1136            checkCRL if true, checks the certificate revocation list for validity.
 1137            checkCertName, if true checks the CN field of the certificate
 1138        --*/
 1139
 1140        //This method validates a remote certificate.
 1141        internal bool VerifyRemoteCertificate(SslCertificateTrust? trust, ref ProtocolToken alertToken, ref SslPolicyErr
 01142        {
 1143            // We need to note the number of certs in ExtraStore that were
 1144            // provided (by the user), we will add more from the received peer
 1145            // chain and we want to dispose only these after we perform the
 1146            // validation.
 1147            // TODO: this forces allocation of X509Certificate2Collection
 01148            int preexistingExtraCertsCount = _sslAuthenticationOptions.CertificateChainPolicy?.ExtraStore?.Count ?? 0;
 1149
 01150            X509Chain? chain = null;
 01151            bool certificateValidationSkippedOnResume = false;
 1152
 1153            try
 01154            {
 01155                X509Certificate2? certificate = CertificateValidationPal.GetRemoteCertificate(_securityContext, ref chai
 1156
 01157                return VerifyRemoteCertificateCore(
 01158                    this,
 01159                    !_isRenego && !_isReAuthentication,
 01160                    _sslAuthenticationOptions,
 01161                    _securityContext,
 01162                    ref _remoteCertificate,
 01163                    ref _connectionInfo,
 01164                    certificate,
 01165                    chain,
 01166                    trust,
 01167                    ref alertToken,
 01168                    ref sslPolicyErrors,
 01169                    out chainStatus,
 01170                    out certificateValidationSkippedOnResume,
 01171                    peerCertificateChain: null,
 01172                    cloneCertificateChainPolicy: false);
 1173            }
 1174            finally
 01175            {
 1176                // At least on Win2k server the chain is found to have dependencies on the original cert context.
 1177                // So it should be closed first.
 1178
 01179                if (chain != null)
 01180                {
 1181                    // Only cleanup certificates if no user callback was provided.
 1182                    // When a callback is provided, users might add their own certificates to ExtraStore
 1183                    // or keep references to certificates from ChainElements.
 1184                    // On a resumed handshake we skip the callback entirely (see the resumption shortcut
 1185                    // in VerifyRemoteCertificateCore), so nothing else adopts the peer-sent intermediates
 1186                    // GetRemoteCertificate appended; dispose them here even when a callback is configured
 1187                    // to avoid leaking X509Certificate2 handles across repeated resumptions.
 01188                    if (_sslAuthenticationOptions.CertValidationDelegate == null || certificateValidationSkippedOnResume
 01189                    {
 1190                        // Dispose only the certificates that were added by GetRemoteCertificate
 01191                        for (int i = preexistingExtraCertsCount; i < chain.ChainPolicy.ExtraStore.Count; i++)
 01192                        {
 01193                            chain.ChainPolicy.ExtraStore[i].Dispose();
 01194                        }
 1195
 01196                        int elementsCount = chain.ChainElements.Count;
 01197                        for (int i = 0; i < elementsCount; i++)
 01198                        {
 01199                            chain.ChainElements[i].Certificate.Dispose();
 01200                        }
 01201                    }
 1202
 01203                    chain.Dispose();
 01204                }
 01205            }
 01206        }
 1207
 1208        internal bool VerifyRemoteCertificate(
 1209            X509Certificate2? certificate,
 1210            X509Chain? chain,
 1211            SslCertificateTrust? trust,
 1212            ref ProtocolToken alertToken,
 1213            ref SslPolicyErrors sslPolicyErrors,
 1214            out X509ChainStatusFlags chainStatus)
 1215        {
 1216            return VerifyRemoteCertificateCore(
 1217                this,
 1218                !_isRenego && !_isReAuthentication,
 1219                _sslAuthenticationOptions,
 1220                _securityContext,
 1221                ref _remoteCertificate,
 1222                ref _connectionInfo,
 1223                certificate,
 1224                chain,
 1225                trust,
 1226                ref alertToken,
 1227                ref sslPolicyErrors,
 1228                out chainStatus,
 1229                out _,
 1230                peerCertificateChain: null,
 1231                cloneCertificateChainPolicy: false);
 1232        }
 1233
 1234        internal static bool VerifyRemoteCertificateCore(
 1235            object sender,
 1236            bool isInitialHandshake,
 1237            SslAuthenticationOptions sslAuthenticationOptions,
 1238#if TARGET_APPLE
 1239            SafeDeleteContext? securityContext,
 1240#else
 1241            SafeDeleteSslContext? securityContext,
 1242#endif
 1243            ref X509Certificate2? remoteCertificateSlot,
 1244            ref SslConnectionInfo connectionInfo,
 1245            X509Certificate2? certificate,
 1246            X509Chain? chain,
 1247            SslCertificateTrust? trust,
 1248            ref ProtocolToken alertToken,
 1249            ref SslPolicyErrors sslPolicyErrors,
 1250            out X509ChainStatusFlags chainStatus,
 1251            out bool certificateValidationSkippedOnResume,
 1252            X509Certificate2Collection? peerCertificateChain,
 1253            bool cloneCertificateChainPolicy)
 1254        {
 01255            chainStatus = X509ChainStatusFlags.NoError;
 01256            certificateValidationSkippedOnResume = false;
 1257
 01258            bool success = false;
 1259
 01260            RemoteCertificateValidationCallback? remoteCertValidationCallback = sslAuthenticationOptions.CertValidationD
 1261
 01262            if (remoteCertificateSlot != null &&
 01263                certificate != null &&
 01264                certificate.RawDataMemory.Span.SequenceEqual(remoteCertificateSlot.RawDataMemory.Span))
 01265            {
 1266                // This is renegotiation or TLS 1.3 post-handshake auth and the (remote) certificate did not change.
 1267                // Revalidating the same certificate MAY fail for a couple of reasons (expiration, revocation,
 1268                // change in system trust, ...), but we have already established trust on this particular
 1269                // connection to even get this far.
 01270                certificate.Dispose();
 01271                return true;
 1272            }
 1273
 01274            if (certificate != null &&
 01275                isInitialHandshake &&
 01276                connectionInfo.TlsResumed &&
 01277                !LocalAppContextSwitches.RevalidateCertificateOnTlsResume)
 01278            {
 1279                // The initial TLS handshake was a resumption via an abbreviated handshake. The
 1280                // peer did not send its certificate again; its identity was established and
 1281                // validated during the original full handshake that produced the session ticket
 1282                // / session id. Common TLS stacks (e.g. OpenSSL, SChannel) do not re-run
 1283                // certificate verification on resumption, so by default neither do we: adopt the
 1284                // cached peer certificate for the RemoteCertificate property but skip rebuilding
 1285                // the chain and invoking the user validation callback. Set the
 1286                // System.Net.Security.RevalidateCertificateOnTlsResume switch to opt back into
 1287                // re-validating the peer certificate on every resumption.
 1288                //
 1289                // This shortcut is gated on the initial handshake: during renegotiation or
 1290                // TLS 1.3 post-handshake authentication the peer can present a new certificate,
 1291                // which must always be validated (the identical-certificate case above is handled
 1292                // separately).
 01293                remoteCertificateSlot = certificate;
 01294                certificateValidationSkippedOnResume = true;
 01295                if (NetEventSource.Log.IsEnabled())
 01296                {
 01297                    NetEventSource.Info(sender, "Skipping remote certificate validation on resumed TLS session.");
 01298                }
 01299                return true;
 1300            }
 1301
 1302            // don't assign to remoteCertificateSlot yet, this prevents weird exceptions if SslStream is disposed in par
 1303
 01304            if (certificate == null)
 01305            {
 01306                if (NetEventSource.Log.IsEnabled() && sslAuthenticationOptions.RemoteCertRequired)
 01307                {
 01308                    NetEventSource.Error(sender, $"Remote certificate required, but no remote certificate received");
 01309                }
 01310                sslPolicyErrors |= SslPolicyErrors.RemoteCertificateNotAvailable;
 01311            }
 1312            else
 01313            {
 01314                chain ??= new X509Chain();
 1315
 01316                if (sslAuthenticationOptions.CertificateChainPolicy != null)
 01317                {
 01318                    chain.ChainPolicy = cloneCertificateChainPolicy
 01319                        ? sslAuthenticationOptions.CertificateChainPolicy.Clone()
 01320                        : sslAuthenticationOptions.CertificateChainPolicy;
 01321                }
 1322                else
 01323                {
 01324                    chain.ChainPolicy.RevocationMode = sslAuthenticationOptions.CertificateRevocationCheckMode;
 01325                    chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot;
 1326
 01327                    if (sslAuthenticationOptions.IsServer && !LocalAppContextSwitches.EnableServerAiaDownloads)
 01328                    {
 01329                        chain.ChainPolicy.DisableCertificateDownloads = true;
 01330                    }
 1331
 01332                    if (trust != null)
 01333                    {
 01334                        chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
 01335                        if (trust._store != null)
 01336                        {
 01337                            chain.ChainPolicy.CustomTrustStore.AddRange(trust._store.Certificates);
 01338                        }
 01339                        if (trust._trustList != null)
 01340                        {
 01341                            chain.ChainPolicy.CustomTrustStore.AddRange(trust._trustList);
 01342                        }
 01343                    }
 01344                }
 1345
 01346                if (peerCertificateChain is { Count: > 0 })
 01347                {
 01348                    chain.ChainPolicy.ExtraStore.AddRange(peerCertificateChain);
 01349                }
 1350
 1351                // set ApplicationPolicy unless already provided.
 01352                if (chain.ChainPolicy.ApplicationPolicy.Count == 0)
 01353                {
 1354                    // Authenticate the remote party: (e.g. when operating in server mode, authenticate the client).
 01355                    chain.ChainPolicy.ApplicationPolicy.Add(sslAuthenticationOptions.IsServer ? s_clientAuthOid : s_serv
 01356                }
 1357
 01358                sslPolicyErrors |= CertificateValidationPal.VerifyCertificateProperties(
 01359                    securityContext!,
 01360                    chain,
 01361                    certificate,
 01362                    sslAuthenticationOptions.CheckCertName,
 01363                    sslAuthenticationOptions.IsServer,
 01364                    TargetHostNameHelper.NormalizeHostName(sslAuthenticationOptions.TargetHost));
 01365            }
 1366
 01367            remoteCertificateSlot = certificate;
 1368
 01369            if (remoteCertValidationCallback != null)
 01370            {
 1371                // Ensure connection info is populated before calling the user callback,
 1372                // which may access properties like SslProtocol or CipherAlgorithm.
 1373                // During inline cert validation the handshake hasn't completed yet, so
 1374                // connectionInfo may not have been set by ProcessHandshakeSuccess.
 01375                if (connectionInfo.Protocol == 0 && securityContext is not null)
 01376                {
 01377                    SslStreamPal.QueryContextConnectionInfo(securityContext, ref connectionInfo);
 01378                }
 1379
 01380                success = remoteCertValidationCallback(sender, certificate, chain, sslPolicyErrors);
 01381            }
 1382            else
 01383            {
 01384                if (!sslAuthenticationOptions.RemoteCertRequired)
 01385                {
 01386                    sslPolicyErrors &= ~SslPolicyErrors.RemoteCertificateNotAvailable;
 01387                }
 1388
 01389                success = sslPolicyErrors == SslPolicyErrors.None;
 01390            }
 1391
 01392            if (NetEventSource.Log.IsEnabled())
 01393            {
 01394                LogCertificateValidation(sender, remoteCertValidationCallback, sslPolicyErrors, success, chain);
 01395                NetEventSource.Info(sender, $"Cert validation, remote cert = {remoteCertificateSlot}");
 01396            }
 1397
 01398            if (!success)
 01399            {
 1400#pragma warning disable CS0162 // unreachable code detected (compile time const)
 01401                if (SslStreamPal.CanGenerateCustomAlertsForContext(securityContext) && !SslStreamPal.CertValidationInCal
 01402                {
 01403                    sslStream.CreateFatalHandshakeAlertToken(sslPolicyErrors, chain!, ref alertToken);
 01404                }
 1405#pragma warning restore CS0162 // unreachable code detected (compile time const)
 1406
 01407                if (chain != null)
 01408                {
 01409                    foreach (X509ChainStatus status in chain.ChainStatus)
 01410                    {
 01411                        chainStatus |= status.Status;
 01412                    }
 01413                }
 01414            }
 1415
 01416            return success;
 01417        }
 1418
 1419        private void CreateFatalHandshakeAlertToken(SslPolicyErrors sslPolicyErrors, X509Chain? chain, ref ProtocolToken
 01420        {
 1421            TlsAlertMessage alertMessage;
 1422
 01423            switch (sslPolicyErrors)
 1424            {
 1425                case SslPolicyErrors.RemoteCertificateChainErrors:
 01426                    Debug.Assert(chain != null);
 01427                    alertMessage = GetAlertMessageFromChain(chain!);
 01428                    break;
 1429                case SslPolicyErrors.RemoteCertificateNameMismatch:
 01430                    alertMessage = TlsAlertMessage.BadCertificate;
 01431                    break;
 1432                case SslPolicyErrors.RemoteCertificateNotAvailable:
 1433                default:
 01434                    alertMessage = TlsAlertMessage.CertificateUnknown;
 01435                    break;
 1436            }
 1437
 01438            if (NetEventSource.Log.IsEnabled())
 01439                NetEventSource.Info(this, $"alertMessage:{alertMessage}");
 1440
 1441            SecurityStatusPal status;
 01442            status = SslStreamPal.ApplyAlertToken(_securityContext, TlsAlertType.Fatal, alertMessage);
 1443
 01444            if (status.ErrorCode != SecurityStatusPalErrorCode.OK)
 01445            {
 01446                if (NetEventSource.Log.IsEnabled())
 01447                    NetEventSource.Info(this, $"ApplyAlertToken() returned {status.ErrorCode}");
 1448
 01449                if (status.Exception != null)
 01450                {
 01451                    ExceptionDispatchInfo.Throw(status.Exception);
 1452                }
 01453            }
 1454
 1455#if TARGET_APPLE
 1456            if (_securityContext is not null && !SslStreamPal.IsAsyncSecurityContext(_securityContext))
 1457            {
 1458                byte[] alertFrame = TlsFrameHelper.CreateAlertFrame(_lastFrame.Header.Version, (TlsAlertDescription)aler
 1459                if (alertFrame.Length != 0)
 1460                {
 1461                    alertToken.SetPayload(alertFrame);
 1462                    return;
 1463                }
 1464            }
 1465#endif
 01466            alertToken = GenerateAlertToken();
 01467        }
 1468
 1469        private ProtocolToken CreateShutdownToken()
 01470        {
 1471            SecurityStatusPal status;
 01472            status = SslStreamPal.ApplyShutdownToken(_securityContext!);
 1473
 01474            if (status.ErrorCode != SecurityStatusPalErrorCode.OK)
 01475            {
 01476                if (NetEventSource.Log.IsEnabled())
 01477                    NetEventSource.Info(this, $"ApplyAlertToken() returned {status.ErrorCode}");
 1478
 01479                if (status.Exception != null)
 01480                {
 01481                    ExceptionDispatchInfo.Throw(status.Exception);
 1482                }
 1483
 01484                return default;
 1485            }
 1486
 01487            return GenerateToken(default, out _);
 01488        }
 1489
 1490        private ProtocolToken GenerateAlertToken()
 01491        {
 01492            return GenerateToken(default, out _);
 01493        }
 1494
 1495        internal static TlsAlertMessage GetAlertMessageFromChain(X509Chain chain)
 01496        {
 01497            foreach (X509ChainStatus chainStatus in chain.ChainStatus)
 01498            {
 01499                if (chainStatus.Status == X509ChainStatusFlags.NoError)
 01500                {
 01501                    continue;
 1502                }
 1503
 01504                if ((chainStatus.Status &
 01505                    (X509ChainStatusFlags.UntrustedRoot | X509ChainStatusFlags.PartialChain |
 01506                     X509ChainStatusFlags.Cyclic)) != 0)
 01507                {
 01508                    return TlsAlertMessage.UnknownCA;
 1509                }
 1510
 01511                if ((chainStatus.Status &
 01512                    (X509ChainStatusFlags.Revoked | X509ChainStatusFlags.OfflineRevocation)) != 0)
 01513                {
 01514                    return TlsAlertMessage.CertificateRevoked;
 1515                }
 1516
 01517                if ((chainStatus.Status &
 01518                    (X509ChainStatusFlags.CtlNotTimeValid | X509ChainStatusFlags.NotTimeNested |
 01519                     X509ChainStatusFlags.NotTimeValid)) != 0)
 01520                {
 01521                    return TlsAlertMessage.CertificateExpired;
 1522                }
 1523
 01524                if ((chainStatus.Status & X509ChainStatusFlags.CtlNotValidForUsage) != 0)
 01525                {
 01526                    return TlsAlertMessage.UnsupportedCert;
 1527                }
 1528
 01529                if ((chainStatus.Status &
 01530                    (X509ChainStatusFlags.CtlNotSignatureValid | X509ChainStatusFlags.InvalidExtension |
 01531                     X509ChainStatusFlags.NotSignatureValid | X509ChainStatusFlags.InvalidPolicyConstraints |
 01532                     X509ChainStatusFlags.NoIssuanceChainPolicy | X509ChainStatusFlags.NotValidForUsage)) != 0)
 01533                {
 01534                    return TlsAlertMessage.BadCertificate;
 1535                }
 1536
 1537                // All other errors:
 01538                return TlsAlertMessage.CertificateUnknown;
 1539            }
 1540
 01541            return TlsAlertMessage.BadCertificate;
 01542        }
 1543
 1544        private static void LogCertificateValidation(object sender, RemoteCertificateValidationCallback? remoteCertValid
 01545        {
 01546            if (!NetEventSource.Log.IsEnabled())
 01547                return;
 1548
 01549            if (sslPolicyErrors != SslPolicyErrors.None)
 01550            {
 01551                NetEventSource.Log.RemoteCertificateError(sender, SR.net_log_remote_cert_has_errors);
 01552                if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateNotAvailable) != 0)
 01553                {
 01554                    NetEventSource.Log.RemoteCertificateError(sender, SR.net_log_remote_cert_not_available);
 01555                }
 1556
 01557                if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateNameMismatch) != 0)
 01558                {
 01559                    NetEventSource.Log.RemoteCertificateError(sender, SR.net_log_remote_cert_name_mismatch);
 01560                }
 1561
 01562                if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateChainErrors) != 0)
 01563                {
 01564                    Debug.Assert(chain != null);
 01565                    string chainStatusString = "ChainStatus: ";
 01566                    foreach (X509ChainStatus chainStatus in chain!.ChainStatus)
 01567                    {
 01568                        chainStatusString += "\t" + chainStatus.StatusInformation;
 01569                    }
 01570                    NetEventSource.Log.RemoteCertificateError(sender, chainStatusString);
 01571                }
 01572            }
 1573
 01574            if (success)
 01575            {
 01576                if (remoteCertValidationCallback != null)
 01577                {
 01578                    NetEventSource.Log.RemoteCertDeclaredValid(sender);
 01579                }
 1580                else
 01581                {
 01582                    NetEventSource.Log.RemoteCertHasNoErrors(sender);
 01583                }
 01584            }
 1585            else
 01586            {
 01587                if (remoteCertValidationCallback != null)
 01588                {
 01589                    NetEventSource.Log.RemoteCertUserDeclaredInvalid(sender);
 01590                }
 01591            }
 01592        }
 1593    }
 1594
 1595    // ProtocolToken - used to process and handle the return codes from the SSPI wrapper
 1596    internal struct ProtocolToken
 1597    {
 1598        internal SecurityStatusPal Status;
 1599        internal byte[]? Payload;
 1600        internal int Size;
 1601        internal bool RentBuffer;
 1602
 1603        internal bool Failed
 1604        {
 1605            get
 1606            {
 1607                return ((Status.ErrorCode != SecurityStatusPalErrorCode.OK) && (Status.ErrorCode != SecurityStatusPalErr
 1608            }
 1609        }
 1610
 1611        internal bool Done
 1612        {
 1613            get
 1614            {
 1615                return (Status.ErrorCode == SecurityStatusPalErrorCode.OK);
 1616            }
 1617        }
 1618
 1619        internal bool Renegotiate
 1620        {
 1621            get
 1622            {
 1623                return (Status.ErrorCode == SecurityStatusPalErrorCode.Renegotiate);
 1624            }
 1625        }
 1626
 1627        internal bool CloseConnection
 1628        {
 1629            get
 1630            {
 1631                return (Status.ErrorCode == SecurityStatusPalErrorCode.ContextExpired);
 1632            }
 1633        }
 1634        internal void SetPayload(ReadOnlySpan<byte> payload)
 1635        {
 1636            Debug.Assert(Payload == null);
 1637            Size = payload.Length;
 1638
 1639            if (Size > 0)
 1640            {
 1641                Payload = RentBuffer ? ArrayPool<byte>.Shared.Rent(Size) : new byte[Size];
 1642                payload.CopyTo(new Span<byte>(Payload, 0, Size));
 1643            }
 1644        }
 1645
 1646        internal void EnsureAvailableSpace(int size)
 1647        {
 1648            if (Available >= size)
 1649            {
 1650                return;
 1651            }
 1652
 1653            var oldPayload = Payload;
 1654
 1655            Payload = RentBuffer ? ArrayPool<byte>.Shared.Rent(Size + size) : new byte[Size + size];
 1656            if (oldPayload != null)
 1657            {
 1658                oldPayload.AsSpan<byte>().CopyTo(Payload);
 1659                if (RentBuffer)
 1660                {
 1661                    ArrayPool<byte>.Shared.Return(oldPayload);
 1662                }
 1663            }
 1664        }
 1665
 1666        internal int Available => Payload == null ? 0 : Payload.Length - Size;
 1667        internal Span<byte> AvailableSpan => Payload == null ? Span<byte>.Empty : new Span<byte>(Payload, Size, Availabl
 1668
 1669        internal ReadOnlyMemory<byte> AsMemory() => new ReadOnlyMemory<byte>(Payload, 0, Size);
 1670
 1671        internal void ReleasePayload()
 1672        {
 1673            Debug.Assert(Payload != null || Size == 0);
 1674
 1675            byte[]? toReturn = Payload;
 1676            Payload = null;
 1677            Size = 0;
 1678            if (RentBuffer && toReturn != null)
 1679            {
 1680                ArrayPool<byte>.Shared.Return(toReturn);
 1681            }
 1682        }
 1683
 1684        internal Exception? GetException()
 1685        {
 1686            // If it's not done, then there's got to be an error, even if it's
 1687            // a Handshake message up, and we only have a Warning message.
 1688            return Done ? null : SslStreamPal.GetException(Status);
 1689        }
 1690    }
 1691}
 1692

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.TlsSessionWedge.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Diagnostics;
 5using System.Security.Cryptography.X509Certificates;
 6
 7namespace System.Net.Security
 8{
 9    // Routes the SslStream handshake hot-path through TlsSession. The PAL calls
 10    // underneath are unchanged; this is a wedge that proves TlsSession is
 11    // expressive enough to host SslStream's TLS engine. Compiled on Linux,
 12    // FreeBSD, and Windows.
 13    //
 14    // SslStream's _securityContext / _credentialsHandle fields are mirrored from
 15    // the TlsSession after each step so that the rest of SslStream (cert
 16    // validation, channel binding, ProcessHandshakeSuccess, renegotiation,
 17    // dispose) continues to work against the same SafeHandles.
 18    public partial class SslStream
 19    {
 20        private TlsBufferSession? _tlsSession;
 21
 22        private void EnsureTlsSession()
 023        {
 024            if (_tlsSession is null)
 025            {
 026                Debug.Assert(_sslAuthenticationOptions != null);
 027                TlsContext ctx = TlsContext.WrapShared(_sslAuthenticationOptions);
 028                _tlsSession = new TlsBufferSession();
 029                _tlsSession.SetContext(ctx);
 30
 31                // SslStream owns post-handshake certificate validation (see
 32                // SslStream.IO.cs ProcessHandshakeSuccess). Tell TlsSession not to run
 33                // its own callback so the user delegate sees the SslStream as sender
 34                // and isn't invoked twice.
 035                _tlsSession.SuppressInternalCertificateValidation = true;
 036            }
 037        }
 38
 39        private partial bool TryNextMessageViaTlsSession(ReadOnlySpan<byte> incomingBuffer, out ProtocolToken token, out
 040        {
 041            EnsureTlsSession();
 42
 43            // The legacy GenerateToken acquires credentials before the first PAL call.
 44            // On Unix AcquireCredentialsHandle is a no-op (returns null), but
 45            // AcquireServerCredentials has a side effect we must preserve: it resolves
 46            // the cert via ServerCertSelectionDelegate / CertSelectionDelegate /
 47            // CertificateContext and assigns _sslAuthenticationOptions.CertificateContext,
 48            // which the OpenSSL handshake asserts on. AcquireClientCredentials similarly
 49            // bootstraps the client cert context. Run them once per handshake before the
 50            // first PAL call.
 051            bool refreshCredentialNeeded = _securityContext is null;
 052            bool cachedCreds = false;
 053            bool sendTrustList = false;
 054            byte[]? thumbPrint = null;
 55            try
 056            {
 057                if (refreshCredentialNeeded)
 058                {
 059                    if (_sslAuthenticationOptions!.IsServer)
 060                    {
 061                        sendTrustList = _sslAuthenticationOptions.CertificateContext?.Trust?._sendTrustInHandshake ?? fa
 062                        cachedCreds = AcquireServerCredentials(ref thumbPrint);
 063                    }
 64                    else
 065                    {
 066                        cachedCreds = AcquireClientCredentials(ref thumbPrint);
 067                    }
 68
 69                    // SChannel-style PALs populate SslStream._credentialsHandle from
 70                    // SslSessionsCache before the first ASC/ISC. Seed TlsSession with it
 71                    // so its ref parameter starts from the cached handle rather than null.
 072                    _tlsSession!.CredentialsHandle = _credentialsHandle;
 073                }
 74
 075                token = _tlsSession!.HandshakeStepForSslStream(incomingBuffer, out consumed);
 76
 77                // SChannel server-side ALPN: when the first ASC call returns
 78                // HandshakeStarted, the wire bytes were consumed but ASC stopped so we
 79                // can run SelectApplicationProtocol with the parsed ClientHello before
 80                // generating the ServerHello. Re-enter with no new input afterwards.
 081                if (token.Status.ErrorCode == SecurityStatusPalErrorCode.HandshakeStarted)
 082                {
 083                    token.Status = SslStreamPal.SelectApplicationProtocol(
 084                        _tlsSession.CredentialsHandle!,
 085                        _tlsSession.SecurityContext!,
 086                        _sslAuthenticationOptions!,
 087                        _lastFrame.RawApplicationProtocols);
 88
 089                    if (token.Status.ErrorCode == SecurityStatusPalErrorCode.OK)
 090                    {
 091                        token = _tlsSession.HandshakeStepForSslStream(ReadOnlySpan<byte>.Empty, out _);
 092                    }
 093                }
 94
 95                // OpenSSL surfaces CredentialsNeeded when the local cert callback returned
 96                // null on the first call. SChannel surfaces it on a later ISC step after
 97                // the server's CertificateRequest is parsed. Re-run client cert selection
 98                // with newCredentialsRequested=true (mirrors legacy GenerateToken), then
 99                // drive the handshake again with no new input. Set refreshCredentialNeeded
 100                // so the finally-block caches the new cert-bound credential.
 0101                if (token.Status.ErrorCode == SecurityStatusPalErrorCode.CredentialsNeeded)
 0102                {
 0103                    if (NetEventSource.Log.IsEnabled())
 0104                    {
 0105                        NetEventSource.Info(this, "TlsSession reported 'CredentialsNeeded'; reselecting client credentia
 0106                    }
 107
 0108                    refreshCredentialNeeded = true;
 0109                    cachedCreds = AcquireClientCredentials(ref thumbPrint, newCredentialsRequested: true);
 0110                    _tlsSession.CredentialsHandle = _credentialsHandle;
 111
 0112                    token = _tlsSession.HandshakeStepForSslStream(ReadOnlySpan<byte>.Empty, out _);
 0113                }
 114
 115                // Mirror handles so legacy SslStream paths (cert validation, channel binding,
 116                // ProcessHandshakeSuccess, renegotiation, Dispose) keep working unchanged.
 0117                _securityContext = _tlsSession.SecurityContext;
 0118                _credentialsHandle = _tlsSession.CredentialsHandle;
 0119            }
 120            finally
 0121            {
 0122                ReleaseCachedCredentials();
 0123                if (refreshCredentialNeeded)
 0124                {
 125                    // Mirror legacy GenerateToken bookkeeping: the PAL has bumped the cred
 126                    // refcount, so drop our reference. Then publish a fresh entry to
 127                    // SslSessionsCache so subsequent connections to the same host can
 128                    // resume the TLS session (Windows SChannel session ticket lives on
 129                    // the cred handle).
 0130                    _credentialsHandle?.Dispose();
 131
 0132                    bool wouldCache = !cachedCreds && _securityContext is not null && !_securityContext.IsInvalid &&
 0133                        _credentialsHandle is not null && !_credentialsHandle.IsInvalid;
 134
 0135                    if (wouldCache)
 0136                    {
 0137                        SslSessionsCache.CacheCredential(
 0138                            _credentialsHandle!,
 0139                            thumbPrint,
 0140                            _sslAuthenticationOptions!.EnabledSslProtocols,
 0141                            _sslAuthenticationOptions.IsServer,
 0142                            _sslAuthenticationOptions.EncryptionPolicy,
 0143                            _sslAuthenticationOptions.CertificateRevocationCheckMode != X509RevocationMode.NoCheck,
 0144                            _sslAuthenticationOptions.AllowTlsResume,
 0145                            sendTrustList,
 0146                            _sslAuthenticationOptions.AllowRsaPssPadding,
 0147                            _sslAuthenticationOptions.AllowRsaPkcs1Padding);
 0148                    }
 0149                }
 0150            }
 151
 0152            return true;
 0153        }
 154    }
 155}
 156

Methods/Properties

.cctor()
.ctor(System.IO.Stream,System.Boolean,System.Net.Security.RemoteCertificateValidationCallback,System.Net.Security.LocalCertificateSelectionCallback,System.Net.Security.EncryptionPolicy)
.ctor()
IsValid()
DecryptedSpan()
DecryptedReadOnlySpanSliced(System.Int32)
DecryptedLength()
ActiveLength()
EncryptedSpanSliced(System.Int32)
EncryptedReadOnlySpan()
EncryptedLength()
AvailableMemory()
AvailableLength()
Commit(System.Int32)
EnsureAvailableSpace(System.Int32)
Discard(System.Int32)
DiscardEncrypted(System.Int32)
OnDecrypted(System.Int32,System.Int32,System.Int32)
ReturnBuffer()
.ctor(System.IO.Stream)
.ctor(System.IO.Stream,System.Boolean)
.ctor(System.IO.Stream,System.Boolean,System.Net.Security.RemoteCertificateValidationCallback)
.ctor(System.IO.Stream,System.Boolean,System.Net.Security.RemoteCertificateValidationCallback,System.Net.Security.LocalCertificateSelectionCallback)
BeginAuthenticateAsClient(System.String,System.AsyncCallback,System.Object)
BeginAuthenticateAsClient(System.String,System.Security.Cryptography.X509Certificates.X509CertificateCollection,System.Boolean,System.AsyncCallback,System.Object)
BeginAuthenticateAsClient(System.String,System.Security.Cryptography.X509Certificates.X509CertificateCollection,System.Security.Authentication.SslProtocols,System.Boolean,System.AsyncCallback,System.Object)
BeginAuthenticateAsClient(System.Net.Security.SslClientAuthenticationOptions,System.Threading.CancellationToken,System.AsyncCallback,System.Object)
EndAuthenticateAsClient(System.IAsyncResult)
BeginAuthenticateAsServer(System.Security.Cryptography.X509Certificates.X509Certificate,System.AsyncCallback,System.Object)
BeginAuthenticateAsServer(System.Security.Cryptography.X509Certificates.X509Certificate,System.Boolean,System.Boolean,System.AsyncCallback,System.Object)
BeginAuthenticateAsServer(System.Security.Cryptography.X509Certificates.X509Certificate,System.Boolean,System.Security.Authentication.SslProtocols,System.Boolean,System.AsyncCallback,System.Object)
BeginAuthenticateAsServer(System.Net.Security.SslServerAuthenticationOptions,System.Threading.CancellationToken,System.AsyncCallback,System.Object)
EndAuthenticateAsServer(System.IAsyncResult)
TransportContext()
AuthenticateAsClient(System.String)
AuthenticateAsClient(System.String,System.Security.Cryptography.X509Certificates.X509CertificateCollection,System.Boolean)
AuthenticateAsClient(System.String,System.Security.Cryptography.X509Certificates.X509CertificateCollection,System.Security.Authentication.SslProtocols,System.Boolean)
AuthenticateAsClient(System.Net.Security.SslClientAuthenticationOptions)
AuthenticateAsServer(System.Security.Cryptography.X509Certificates.X509Certificate)
AuthenticateAsServer(System.Security.Cryptography.X509Certificates.X509Certificate,System.Boolean,System.Boolean)
AuthenticateAsServer(System.Security.Cryptography.X509Certificates.X509Certificate,System.Boolean,System.Security.Authentication.SslProtocols,System.Boolean)
AuthenticateAsServer(System.Net.Security.SslServerAuthenticationOptions)
AuthenticateAsClientAsync(System.String)
AuthenticateAsClientAsync(System.String,System.Security.Cryptography.X509Certificates.X509CertificateCollection,System.Boolean)
AuthenticateAsClientAsync(System.String,System.Security.Cryptography.X509Certificates.X509CertificateCollection,System.Security.Authentication.SslProtocols,System.Boolean)
AuthenticateAsClientAsync(System.Net.Security.SslClientAuthenticationOptions,System.Threading.CancellationToken)
AuthenticateAsServerAsync(System.Security.Cryptography.X509Certificates.X509Certificate)
AuthenticateAsServerAsync(System.Security.Cryptography.X509Certificates.X509Certificate,System.Boolean,System.Boolean)
AuthenticateAsServerAsync(System.Security.Cryptography.X509Certificates.X509Certificate,System.Boolean,System.Security.Authentication.SslProtocols,System.Boolean)
AuthenticateAsServerAsync(System.Net.Security.SslServerAuthenticationOptions,System.Threading.CancellationToken)
AuthenticateAsServerAsync(System.Net.Security.ServerOptionsSelectionCallback,System.Object,System.Threading.CancellationToken)
ShutdownAsync()
IsAuthenticated()
IsMutuallyAuthenticated()
IsEncrypted()
IsSigned()
IsServer()
SslProtocol()
GetSslProtocolInternal()
CheckCertRevocationStatus()
LocalCertificate()
RemoteCertificate()
NegotiatedApplicationProtocol()
NegotiatedCipherSuite()
CipherAlgorithm()
CipherStrength()
HashAlgorithm()
HashStrength()
KeyExchangeAlgorithm()
KeyExchangeStrength()
TargetHostName()
CanSeek()
CanRead()
CanTimeout()
CanWrite()
ReadTimeout()
ReadTimeout(System.Int32)
WriteTimeout()
WriteTimeout(System.Int32)
Length()
Position()
Position(System.Int64)
SetLength(System.Int64)
Seek(System.Int64,System.IO.SeekOrigin)
Flush()
FlushAsync(System.Threading.CancellationToken)
NegotiateClientCertificateAsync(System.Threading.CancellationToken)
Dispose(System.Boolean)
DisposeAsync()
RentPointerMemoryManager(System.Net.Security.SslStream/PoolingPointerMemoryManager&,System.Byte*,System.Int32)
ReturnPointerMemoryManager(System.Net.Security.SslStream/PoolingPointerMemoryManager&,System.Net.Security.SslStream/PoolingPointerMemoryManager)
ReadByte()
Read(System.Span`1<System.Byte>)
Read(System.Byte[],System.Int32,System.Int32)
WriteByte(System.Byte)
Write(System.ReadOnlySpan`1<System.Byte>)
Write(System.Byte[])
Write(System.Byte[],System.Int32,System.Int32)
BeginRead(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object)
EndRead(System.IAsyncResult)
BeginWrite(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object)
EndWrite(System.IAsyncResult)
WriteAsync(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken)
WriteAsync(System.ReadOnlyMemory`1<System.Byte>,System.Threading.CancellationToken)
ReadAsync(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken)
ReadAsync(System.Memory`1<System.Byte>,System.Threading.CancellationToken)
ThrowIfExceptional()
ThrowExceptional(System.Runtime.ExceptionServices.ExceptionDispatchInfo)
ThrowIfExceptionalOrNotAuthenticated()
ThrowIfExceptionalOrNotHandshake()
ThrowIfExceptionalOrNotAuthenticatedOrShutdown()
ThrowAlreadyShutdown()
ThrowNotAuthenticated()
Dispose(System.Boolean)
Reset(System.Byte*,System.Int32)
GetSpan()
Pin(System.Int32)
Unpin()
.ctor(System.IO.Stream,System.Boolean,System.Net.Security.RemoteCertificateValidationCallback,System.Net.Security.LocalCertificateSelectionCallback,System.Net.Security.EncryptionPolicy)
InnerStream()
_handshakeLock()
SetException(System.Exception)
CloseInternal()
ProcessAuthenticationAsync(System.Boolean,System.Threading.CancellationToken)
ProcessAuthenticationWithTelemetryAsync(System.Boolean,System.Threading.CancellationToken)
ReplyOnReAuthenticationAsync(System.Byte[],System.Threading.CancellationToken)
RenegotiateAsync(System.Threading.CancellationToken)
ForceAuthenticationAsync(System.Boolean,System.Byte[],System.Threading.CancellationToken)
ReceiveHandshakeFrameAsync(System.Threading.CancellationToken)
ProcessTlsFrame(System.Int32)
SendAuthResetSignal(System.ReadOnlySpan`1<System.Byte>,System.Runtime.ExceptionServices.ExceptionDispatchInfo)
CompleteHandshake(System.Net.Security.ProtocolToken&,System.Net.Security.SslPolicyErrors&,System.Security.Cryptography.X509Certificates.X509ChainStatusFlags&)
CompleteHandshake(System.Net.Security.SslAuthenticationOptions)
CreateCertificateValidationException(System.Net.Security.SslAuthenticationOptions,System.Net.Security.SslPolicyErrors,System.Security.Cryptography.X509Certificates.X509ChainStatusFlags)
WriteAsyncChunked(System.ReadOnlyMemory`1<System.Byte>,System.Threading.CancellationToken)
WriteSingleChunk(System.ReadOnlyMemory`1<System.Byte>,System.Threading.CancellationToken)
WaitAndWriteAsync(System.ReadOnlyMemory`1<System.Byte>,System.Threading.Tasks.Task,System.Threading.CancellationToken)
CompleteWriteAsync(System.Threading.Tasks.ValueTask,System.Net.Security.ProtocolToken)
Finalize()
ReturnReadBufferIfEmpty()
HaveFullTlsFrame(System.Int32&)
EnsureFullTlsFrameAsync()
ReadAsyncInternal()
WriteAsyncInternal(System.ReadOnlyMemory`1<System.Byte>,System.Threading.CancellationToken)
CopyDecryptedData(System.Memory`1<System.Byte>)
GetFrameSize(System.ReadOnlySpan`1<System.Byte>)
.ctor(System.IO.Stream,System.Boolean,System.Net.Security.RemoteCertificateValidationCallback,System.Net.Security.LocalCertificateSelectionCallback,System.Net.Security.EncryptionPolicy)
.cctor()
LocalServerCertificate()
IsLocalClientCertificateUsed()
LocalClientCertificate()
IsRemoteCertificateAvailable()
GetChannelBinding(System.Security.Authentication.ExtendedProtection.ChannelBindingKind)
MaxDataSize()
IsValidContext()
CloseContext()
ReleaseCachedCredentials()
FindCertificateWithPrivateKey(System.Object,System.Boolean,System.Security.Cryptography.X509Certificates.X509Certificate)
MakeEx(System.Security.Cryptography.X509Certificates.X509Certificate)
GetRequestCertificateAuthorities()
SelectClientCertificate()
AcquireClientCredentials(System.Byte[]&,System.Boolean)
UpdateCertificateContext(System.Security.Cryptography.X509Certificates.X509Certificate2)
EnsureInitialized(System.Collections.Generic.List`1<T>&)
AcquireServerCredentials(System.Byte[]&)
AcquireCredentialsHandle(System.Net.Security.SslAuthenticationOptions,System.Boolean)
GetExpiryTimestamp(System.Net.Security.SslStreamCertificateContext)
NextMessage(System.ReadOnlySpan`1<System.Byte>,System.Int32&)
GenerateToken(System.ReadOnlySpan`1<System.Byte>,System.Int32&)
Renegotiate()
ProcessHandshakeSuccess()
EncryptData(System.ReadOnlyMemory`1<System.Byte>)
DecryptData(System.Int32,System.Span`1<System.Byte>,System.Int32&)
VerifyRemoteCertificate(System.Net.Security.SslCertificateTrust,System.Net.Security.ProtocolToken&,System.Net.Security.SslPolicyErrors&,System.Security.Cryptography.X509Certificates.X509ChainStatusFlags&)
VerifyRemoteCertificateCore(System.Object,System.Boolean,System.Net.Security.SslAuthenticationOptions,System.Net.Security.SafeDeleteSslContext,System.Security.Cryptography.X509Certificates.X509Certificate2&,System.Net.Security.SslConnectionInfo&,System.Security.Cryptography.X509Certificates.X509Certificate2,System.Security.Cryptography.X509Certificates.X509Chain,System.Net.Security.SslCertificateTrust,System.Net.Security.ProtocolToken&,System.Net.Security.SslPolicyErrors&,System.Security.Cryptography.X509Certificates.X509ChainStatusFlags&,System.Boolean&,System.Security.Cryptography.X509Certificates.X509Certificate2Collection,System.Boolean)
CreateFatalHandshakeAlertToken(System.Net.Security.SslPolicyErrors,System.Security.Cryptography.X509Certificates.X509Chain,System.Net.Security.ProtocolToken&)
CreateShutdownToken()
GenerateAlertToken()
GetAlertMessageFromChain(System.Security.Cryptography.X509Certificates.X509Chain)
LogCertificateValidation(System.Object,System.Net.Security.RemoteCertificateValidationCallback,System.Net.Security.SslPolicyErrors,System.Boolean,System.Security.Cryptography.X509Certificates.X509Chain)
EnsureTlsSession()
TryNextMessageViaTlsSession(System.ReadOnlySpan`1<System.Byte>,System.Net.Security.ProtocolToken&,System.Int32&)