File: System\Net\Quic\Internal\MsQuicTlsSecret.cs
Web Access
Project: src\src\libraries\System.Net.Quic\src\System.Net.Quic.csproj (System.Net.Quic)
// 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;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Quic;
using static Microsoft.Quic.MsQuic;
 
namespace System.Net.Quic;
 
internal sealed class MsQuicTlsSecret : IDisposable
{
    private unsafe QUIC_TLS_SECRETS* _tlsSecrets;
 
    public static unsafe MsQuicTlsSecret? Create(MsQuicContextSafeHandle handle)
    {
        if (!SslKeyLogger.IsEnabled)
        {
            return null;
        }
 
        QUIC_TLS_SECRETS* tlsSecrets = null;
        try
        {
            tlsSecrets = (QUIC_TLS_SECRETS*)NativeMemory.AllocZeroed((nuint)sizeof(QUIC_TLS_SECRETS));
            MsQuicHelpers.SetMsQuicParameter(handle, QUIC_PARAM_CONN_TLS_SECRETS, (uint)sizeof(QUIC_TLS_SECRETS), (byte*)tlsSecrets);
            MsQuicTlsSecret instance = new MsQuicTlsSecret(tlsSecrets);
            handle.Disposable = instance;
            return instance;
        }
        catch (Exception ex)
        {
            if (NetEventSource.Log.IsEnabled())
            {
                NetEventSource.Error(handle, $"Failed to set native memory for TLS secret: {ex}");
            }
            if (tlsSecrets is not null)
            {
                NativeMemory.Free(tlsSecrets);
            }
            return null;
        }
    }
 
    private unsafe MsQuicTlsSecret(QUIC_TLS_SECRETS* tlsSecrets)
    {
        _tlsSecrets = tlsSecrets;
    }
 
    public unsafe void WriteSecret()
    {
        ReadOnlySpan<byte> clientRandom = _tlsSecrets->IsSet.ClientRandom != 0
            ? new ReadOnlySpan<byte>(_tlsSecrets->ClientRandom, 32)
            : ReadOnlySpan<byte>.Empty;
 
        Span<byte> clientHandshakeTrafficSecret = _tlsSecrets->IsSet.ClientHandshakeTrafficSecret != 0
            ? new Span<byte>(_tlsSecrets->ClientHandshakeTrafficSecret, _tlsSecrets->SecretLength)
            : Span<byte>.Empty;
 
        Span<byte> serverHandshakeTrafficSecret = _tlsSecrets->IsSet.ServerHandshakeTrafficSecret != 0
            ? new Span<byte>(_tlsSecrets->ServerHandshakeTrafficSecret, _tlsSecrets->SecretLength)
            : Span<byte>.Empty;
 
        Span<byte> clientTrafficSecret0 = _tlsSecrets->IsSet.ClientTrafficSecret0 != 0
            ? new Span<byte>(_tlsSecrets->ClientTrafficSecret0, _tlsSecrets->SecretLength)
            : Span<byte>.Empty;
 
        Span<byte> serverTrafficSecret0 = _tlsSecrets->IsSet.ServerTrafficSecret0 != 0
            ? new Span<byte>(_tlsSecrets->ServerTrafficSecret0, _tlsSecrets->SecretLength)
            : Span<byte>.Empty;
 
        Span<byte> clientEarlyTrafficSecret = _tlsSecrets->IsSet.ClientEarlyTrafficSecret != 0
            ? new Span<byte>(_tlsSecrets->ClientEarlyTrafficSecret, _tlsSecrets->SecretLength)
            : Span<byte>.Empty;
 
        SslKeyLogger.WriteSecrets(
            clientRandom,
            clientHandshakeTrafficSecret,
            serverHandshakeTrafficSecret,
            clientTrafficSecret0,
            serverTrafficSecret0,
            clientEarlyTrafficSecret);
 
        // clear secrets already logged, so they are not logged again on next call,
        // keep ClientRandom as it is used for all secrets (and is not a secret itself)
        if (!clientHandshakeTrafficSecret.IsEmpty)
        {
            clientHandshakeTrafficSecret.Clear();
            _tlsSecrets->IsSet.ClientHandshakeTrafficSecret = 0;
        }
 
        if (!serverHandshakeTrafficSecret.IsEmpty)
        {
            serverHandshakeTrafficSecret.Clear();
            _tlsSecrets->IsSet.ServerHandshakeTrafficSecret = 0;
        }
 
        if (!clientTrafficSecret0.IsEmpty)
        {
            clientTrafficSecret0.Clear();
            _tlsSecrets->IsSet.ClientTrafficSecret0 = 0;
        }
 
        if (!serverTrafficSecret0.IsEmpty)
        {
            serverTrafficSecret0.Clear();
            _tlsSecrets->IsSet.ServerTrafficSecret0 = 0;
        }
 
        if (!clientEarlyTrafficSecret.IsEmpty)
        {
            clientEarlyTrafficSecret.Clear();
            _tlsSecrets->IsSet.ClientEarlyTrafficSecret = 0;
        }
    }
 
    public unsafe void Dispose()
    {
        if (_tlsSecrets is null)
        {
            return;
        }
        lock (this)
        {
            if (_tlsSecrets is null)
            {
                return;
            }
 
            QUIC_TLS_SECRETS* tlsSecrets = _tlsSecrets;
            _tlsSecrets = null;
            NativeMemory.Free(_tlsSecrets);
        }
    }
}