File: System\Net\Security\TlsBufferSession.cs
Web Access
Project: src\runtime\src\libraries\System.Net.Security\src\System.Net.Security.csproj (System.Net.Security)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics.CodeAnalysis;
using System.Security.Authentication;

namespace System.Net.Security
{
    /// <summary>
    /// Non-blocking TLS state machine driven by caller-supplied byte spans.
    /// The session object performs no I/O; the caller is responsible for
    /// performing I/O, such as transmitting the output of <see cref="Write"/>.
    /// </summary>
    /// <remarks>
    /// A newly-constructed instance has no <see cref="TlsContext"/>. Call
    /// <see cref="TlsSession.SetContext"/> with a client or server context before
    /// invoking any operation. Server-side deferred flow (SNI-driven context
    /// selection) is supported by passing an empty
    /// <see cref="SslServerAuthenticationOptions"/> to <c>TlsContext.CreateServer</c>;
    /// the first <c>Handshake</c> call then suspends on
    /// <see cref="TlsOperationStatus.NeedsTlsContext"/> so the caller can supply the
    /// resolved per-tenant context via <see cref="TlsSession.SetContext"/>.
    /// </remarks>
    [Experimental(Experimentals.LowLevelTlsDiagId, UrlFormat = Experimentals.SharedUrlFormat)]
    public sealed class TlsBufferSession : TlsSession
    {
        /// <summary>Drives the TLS handshake forward using caller-supplied ciphertext.</summary>
        /// <param name="source">Bytes received from the peer.</param>
        /// <param name="destination">Buffer to receive any handshake bytes that must be sent to the peer.</param>
        /// <param name="bytesConsumed">The number of bytes read from <paramref name="source"/>.</param>
        /// <param name="bytesWritten">The number of bytes written to <paramref name="destination"/>.</param>
        /// <returns>The outcome of the operation.</returns>
        /// <exception cref="ObjectDisposedException">The session has been disposed.</exception>
        /// <exception cref="InvalidOperationException">A <see cref="TlsContext"/> has not been assigned via <see cref="TlsSession.SetContext"/>.</exception>
        /// <exception cref="AuthenticationException">The handshake failed.</exception>
        public TlsOperationStatus Handshake(ReadOnlySpan<byte> source, Span<byte> destination, out int bytesConsumed, out int bytesWritten)
            => HandshakeBufferedCore(source, destination, out bytesConsumed, out bytesWritten);

        /// <summary>Encrypts application-plaintext into ciphertext records.</summary>
        /// <param name="source">Plaintext bytes to encrypt.</param>
        /// <param name="destination">Buffer to receive the produced ciphertext records.</param>
        /// <param name="bytesConsumed">The number of plaintext bytes read from <paramref name="source"/>.</param>
        /// <param name="bytesWritten">The number of ciphertext bytes written to <paramref name="destination"/>.</param>
        /// <returns>The outcome of the operation.</returns>
        /// <exception cref="ObjectDisposedException">The session has been disposed.</exception>
        /// <exception cref="InvalidOperationException">The handshake has not yet completed.</exception>
        public TlsOperationStatus Write(ReadOnlySpan<byte> source, Span<byte> destination, out int bytesConsumed, out int bytesWritten)
            => WriteBufferedCore(source, destination, out bytesConsumed, out bytesWritten);

        /// <summary>Decrypts ciphertext records into application-plaintext.</summary>
        /// <param name="source">Ciphertext bytes received from the peer.</param>
        /// <param name="destination">Buffer to receive the decrypted plaintext.</param>
        /// <param name="bytesConsumed">The number of ciphertext bytes read from <paramref name="source"/>.</param>
        /// <param name="bytesWritten">The number of plaintext bytes written to <paramref name="destination"/>.</param>
        /// <returns>The outcome of the operation.</returns>
        /// <exception cref="ObjectDisposedException">The session has been disposed.</exception>
        /// <exception cref="InvalidOperationException">The handshake has not yet completed.</exception>
        public TlsOperationStatus Read(ReadOnlySpan<byte> source, Span<byte> destination, out int bytesConsumed, out int bytesWritten)
            => ReadBufferedCore(source, destination, out bytesConsumed, out bytesWritten);

        /// <summary>Initiates a TLS <c>close_notify</c> alert; writes the alert record into <paramref name="destination"/>.</summary>
        /// <param name="destination">Buffer to receive the <c>close_notify</c> alert record.</param>
        /// <param name="bytesWritten">The number of bytes written to <paramref name="destination"/>.</param>
        /// <returns>The outcome of the operation.</returns>
        /// <exception cref="ObjectDisposedException">The session has been disposed.</exception>
        public TlsOperationStatus Shutdown(Span<byte> destination, out int bytesWritten)
            => ShutdownBufferedCore(destination, out bytesWritten);

        /// <summary>Drains any staged pending output (handshake fragments, alerts, encrypted records) into <paramref name="destination"/>.</summary>
        /// <param name="destination">Buffer to receive the pending ciphertext.</param>
        /// <param name="bytesWritten">The number of bytes written to <paramref name="destination"/>.</param>
        /// <returns>The outcome of the operation.</returns>
        /// <exception cref="ObjectDisposedException">The session has been disposed.</exception>
        public TlsOperationStatus DrainPendingOutput(Span<byte> destination, out int bytesWritten)
            => DrainPendingOutputCore(destination, out bytesWritten);

        /// <summary>Server-side only. Sends a <c>CertificateRequest</c> to the peer as part of TLS 1.3 post-handshake authentication.</summary>
        /// <param name="destination">Buffer to receive the <c>CertificateRequest</c> record.</param>
        /// <param name="bytesWritten">The number of bytes written to <paramref name="destination"/>.</param>
        /// <returns>The outcome of the operation.</returns>
        /// <exception cref="ObjectDisposedException">The session has been disposed.</exception>
        /// <exception cref="InvalidOperationException">The session is client-side, the handshake has not yet completed, or the current session is not TLS 1.3.</exception>
        /// <exception cref="PlatformNotSupportedException">The current platform does not support post-handshake authentication.</exception>
        public TlsOperationStatus RequestClientCertificate(Span<byte> destination, out int bytesWritten)
            => RequestClientCertificateBufferedCore(destination, out bytesWritten);
    }
}