From fdbfbacbd73cc7d69cb17884c8e6d1f550ee4447 Mon Sep 17 00:00:00 2001 From: wfurt Date: Sun, 5 Mar 2023 10:35:03 -0800 Subject: [PATCH 1/4] Add option to decrypt Quic traffic in debug builds --- .../src/System.Net.Quic.csproj | 1 + .../Net/Quic/Internal/MsQuicTlsSecret.cs | 94 +++++++++++++++++++ .../src/System/Net/Quic/QuicConnection.cs | 34 ++++++- 3 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicTlsSecret.cs diff --git a/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj b/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj index b7b6b7e35dbf66..9cc7fbc98f131d 100644 --- a/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj +++ b/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj @@ -31,6 +31,7 @@ + diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicTlsSecret.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicTlsSecret.cs new file mode 100644 index 00000000000000..c6ecba002c8465 --- /dev/null +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicTlsSecret.cs @@ -0,0 +1,94 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +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 static readonly int ClientRandomOffset = (int)Marshal.OffsetOf(typeof(QUIC_TLS_SECRETS), "ClientRandom"); + private static readonly int ClientEarlyTrafficSecretOffset = (int)Marshal.OffsetOf(typeof(QUIC_TLS_SECRETS), "ClientEarlyTrafficSecret"); + private static readonly int ClientHandshakeTrafficSecretOffset = (int)Marshal.OffsetOf(typeof(QUIC_TLS_SECRETS), "ClientHandshakeTrafficSecret"); + private static readonly int ServerHandshakeTrafficSecretOffset = (int)Marshal.OffsetOf(typeof(QUIC_TLS_SECRETS), "ServerHandshakeTrafficSecret"); + private static readonly int ClientTrafficSecretOffset = (int)Marshal.OffsetOf(typeof(QUIC_TLS_SECRETS), "ClientTrafficSecret0"); + private static readonly int ServerTrafficSecretOffset = (int)Marshal.OffsetOf(typeof(QUIC_TLS_SECRETS), "ServerTrafficSecret0"); + + public QUIC_TLS_SECRETS tlsSecret; + private GCHandle gcHandle; + + public unsafe MsQuicTlsSecret(MsQuicContextSafeHandle handle) + { + tlsSecret = default; + gcHandle = GCHandle.Alloc(this, GCHandleType.Pinned); + + fixed (void* ptr = &tlsSecret) + { + // Best effort. Ignores failures. + MsQuicApi.Api.SetParam(handle, QUIC_PARAM_CONN_TLS_SECRETS, (uint)sizeof(QUIC_TLS_SECRETS), ptr); + } + } + + public void WriteSecret(string fileName) + { + using (FileStream stream = File.Open(fileName, FileMode.Append, FileAccess.Write)) + { + WriteSecret(stream); + } + } + + public unsafe void WriteSecret(FileStream stream) + { + lock (stream) + { + fixed (void* tls = &tlsSecret) + { + string clientRandom = string.Empty; + ReadOnlySpan secrets = new ReadOnlySpan(tls, sizeof(QUIC_TLS_SECRETS)); + + if (tlsSecret.IsSet.ClientRandom != 0) + { + clientRandom = HexConverter.ToString(secrets.Slice(ClientRandomOffset, 32)); + } + + if (tlsSecret.IsSet.ClientHandshakeTrafficSecret != 0) + { + stream.Write(Encoding.ASCII.GetBytes($"CLIENT_HANDSHAKE_TRAFFIC_SECRET {clientRandom} {HexConverter.ToString(secrets.Slice(ClientHandshakeTrafficSecretOffset, tlsSecret.SecretLength))}\n")); + } + + if (tlsSecret.IsSet.ServerHandshakeTrafficSecret != 0) + { + stream.Write(Encoding.ASCII.GetBytes($"SERVER_HANDSHAKE_TRAFFIC_SECRET {clientRandom} {HexConverter.ToString(secrets.Slice(ServerHandshakeTrafficSecretOffset, tlsSecret.SecretLength))}\n")); + } + + if (tlsSecret.IsSet.ClientTrafficSecret0 != 0) + { + stream.Write(Encoding.ASCII.GetBytes($"CLIENT_TRAFFIC_SECRET_0 {clientRandom} {HexConverter.ToString(secrets.Slice(ClientTrafficSecretOffset, tlsSecret.SecretLength))}\n")); + } + + if (tlsSecret.IsSet.ServerTrafficSecret0 != 0) + { + stream.Write(Encoding.ASCII.GetBytes($"SERVER_TRAFFIC_SECRET_0 {clientRandom} {HexConverter.ToString(secrets.Slice(ServerTrafficSecretOffset, tlsSecret.SecretLength))}\n")); + } + + if (tlsSecret.IsSet.ClientEarlyTrafficSecret != 0) + { + stream.Write(Encoding.ASCII.GetBytes($"CLIENT_EARLY_TRAFFIC_SECRET {clientRandom} {HexConverter.ToString(secrets.Slice(ClientEarlyTrafficSecretOffset, tlsSecret.SecretLength))}\n")); + } + } + } + } + + public void Dispose() + { + if (gcHandle.IsAllocated) + { + gcHandle.Free(); + } + } +} diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs index d46ce9ecff409c..fad974e30643c2 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs @@ -37,6 +37,16 @@ namespace System.Net.Quic; /// public sealed partial class QuicConnection : IAsyncDisposable { +#if DEBUG + /// + /// Name of file where secrets should be stored. + /// + private static readonly string? KeyLogFile = Environment.GetEnvironmentVariable("SSLKEYLOGFILE"); + + /// The actual secret structure wrapper passed to MsQuic + private MsQuicTlsSecret? _tlsSecret; +#endif + /// /// Returns true if QUIC is supported on the current machine and can be used; otherwise, false. /// @@ -139,7 +149,6 @@ public static async ValueTask ConnectAsync(QuicClientConnectionO /// Set when CONNECTED is received. /// private SslApplicationProtocol _negotiatedApplicationProtocol; - /// /// The remote endpoint used for this connection. /// @@ -186,6 +195,13 @@ private unsafe QuicConnection() &handle), "ConnectionOpen failed"); _handle = new MsQuicContextSafeHandle(handle, context, SafeHandleType.Connection); + +#if DEBUG + if (KeyLogFile != null) + { + _tlsSecret = new MsQuicTlsSecret(_handle); + } +#endif } catch { @@ -219,6 +235,12 @@ internal unsafe QuicConnection(QUIC_HANDLE* handle, QUIC_NEW_CONNECTION_INFO* in _remoteEndPoint = info->RemoteAddress->ToIPEndPoint(); _localEndPoint = info->LocalAddress->ToIPEndPoint(); +#if DEBUG + if (KeyLogFile != null) + { + _tlsSecret = new MsQuicTlsSecret(_handle); + } +#endif } private async ValueTask FinishConnectAsync(QuicClientConnectionOptions options, CancellationToken cancellationToken = default) @@ -455,6 +477,13 @@ private unsafe int HandleEventConnected(ref CONNECTED_DATA data) NetEventSource.Info(this, $"{this} Received event CONNECTED {LocalEndPoint} -> {RemoteEndPoint}"); } +#if DEBUG + if (KeyLogFile != null && _tlsSecret != null) + { + _tlsSecret.WriteSecret(KeyLogFile); + _tlsSecret.Dispose(); + } +#endif _connectedTcs.TrySetResult(); return QUIC_STATUS_SUCCESS; } @@ -619,6 +648,9 @@ public async ValueTask DisposeAsync() return; } +#if DEBUG + _tlsSecret?.Dispose(); +#endif // Check if the connection has been shut down and if not, shut it down silently. if (_shutdownTcs.TryInitialize(out ValueTask valueTask, this)) { From 098ac11efbb66fe69b20b8b0520e03f4f443746a Mon Sep 17 00:00:00 2001 From: wfurt Date: Tue, 7 Mar 2023 22:23:05 -0800 Subject: [PATCH 2/4] feedback from review --- .../Net/Quic/Internal/MsQuicTlsSecret.cs | 123 +++++++++++------- .../src/System/Net/Quic/QuicConnection.cs | 28 ++-- 2 files changed, 84 insertions(+), 67 deletions(-) diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicTlsSecret.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicTlsSecret.cs index c6ecba002c8465..adbf2b1b708a86 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicTlsSecret.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicTlsSecret.cs @@ -1,10 +1,12 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#if DEBUG using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; using System.Text; +using System.Threading; using Microsoft.Quic; using static Microsoft.Quic.MsQuic; @@ -12,25 +14,46 @@ namespace System.Net.Quic; internal sealed class MsQuicTlsSecret : IDisposable { - private static readonly int ClientRandomOffset = (int)Marshal.OffsetOf(typeof(QUIC_TLS_SECRETS), "ClientRandom"); - private static readonly int ClientEarlyTrafficSecretOffset = (int)Marshal.OffsetOf(typeof(QUIC_TLS_SECRETS), "ClientEarlyTrafficSecret"); - private static readonly int ClientHandshakeTrafficSecretOffset = (int)Marshal.OffsetOf(typeof(QUIC_TLS_SECRETS), "ClientHandshakeTrafficSecret"); - private static readonly int ServerHandshakeTrafficSecretOffset = (int)Marshal.OffsetOf(typeof(QUIC_TLS_SECRETS), "ServerHandshakeTrafficSecret"); - private static readonly int ClientTrafficSecretOffset = (int)Marshal.OffsetOf(typeof(QUIC_TLS_SECRETS), "ClientTrafficSecret0"); - private static readonly int ServerTrafficSecretOffset = (int)Marshal.OffsetOf(typeof(QUIC_TLS_SECRETS), "ServerTrafficSecret0"); + private static readonly string? s_keyLogFile = Environment.GetEnvironmentVariable("SSLKEYLOGFILE"); - public QUIC_TLS_SECRETS tlsSecret; - private GCHandle gcHandle; + private static readonly int s_clientRandomOffset = (int)Marshal.OffsetOf(typeof(QUIC_TLS_SECRETS), "ClientRandom"); + private static readonly int s_clientEarlyTrafficSecretOffset = (int)Marshal.OffsetOf(typeof(QUIC_TLS_SECRETS), "ClientEarlyTrafficSecret"); + private static readonly int s_clientHandshakeTrafficSecretOffset = (int)Marshal.OffsetOf(typeof(QUIC_TLS_SECRETS), "ClientHandshakeTrafficSecret"); + private static readonly int s_serverHandshakeTrafficSecretOffset = (int)Marshal.OffsetOf(typeof(QUIC_TLS_SECRETS), "ServerHandshakeTrafficSecret"); + private static readonly int s_clientTrafficSecretOffset = (int)Marshal.OffsetOf(typeof(QUIC_TLS_SECRETS), "ClientTrafficSecret0"); + private static readonly int s_serverTrafficSecretOffset = (int)Marshal.OffsetOf(typeof(QUIC_TLS_SECRETS), "ServerTrafficSecret0"); - public unsafe MsQuicTlsSecret(MsQuicContextSafeHandle handle) + private IntPtr tlsSecretsPtr = IntPtr.Zero; + + public static MsQuicTlsSecret? Create(MsQuicContextSafeHandle handle) { - tlsSecret = default; - gcHandle = GCHandle.Alloc(this, GCHandleType.Pinned); + MsQuicTlsSecret? secret = null; + if (s_keyLogFile != null) + { + try { + secret = new MsQuicTlsSecret(handle); + } + catch { }; + } - fixed (void* ptr = &tlsSecret) + return secret; + } + + private unsafe MsQuicTlsSecret(MsQuicContextSafeHandle handle) + { + void * ptr = NativeMemory.Alloc((nuint)sizeof(QUIC_TLS_SECRETS)); + if (ptr != null) { - // Best effort. Ignores failures. MsQuicApi.Api.SetParam(handle, QUIC_PARAM_CONN_TLS_SECRETS, (uint)sizeof(QUIC_TLS_SECRETS), ptr); + tlsSecretsPtr = (IntPtr)ptr; + } + } + + public void WriteSecret() + { + if (s_keyLogFile != null) + { + WriteSecret(s_keyLogFile); } } @@ -46,49 +69,51 @@ public unsafe void WriteSecret(FileStream stream) { lock (stream) { - fixed (void* tls = &tlsSecret) + string clientRandom = string.Empty; + ReadOnlySpan buffer = new ReadOnlySpan((void*)tlsSecretsPtr, sizeof(QUIC_TLS_SECRETS)); + ReadOnlySpan secrets = MemoryMarshal.Cast(buffer); + + if (secrets[0].IsSet.ClientRandom != 0) + { + clientRandom = HexConverter.ToString(buffer.Slice(s_clientRandomOffset, 32)); + } + + if (secrets[0].IsSet.ClientHandshakeTrafficSecret != 0) + { + stream.Write(Encoding.ASCII.GetBytes($"CLIENT_HANDSHAKE_TRAFFIC_SECRET {clientRandom} {HexConverter.ToString(buffer.Slice(s_clientHandshakeTrafficSecretOffset, secrets[0].SecretLength))}\n")); + } + + if (secrets[0].IsSet.ServerHandshakeTrafficSecret != 0) + { + stream.Write(Encoding.ASCII.GetBytes($"SERVER_HANDSHAKE_TRAFFIC_SECRET {clientRandom} {HexConverter.ToString(buffer.Slice(s_serverHandshakeTrafficSecretOffset, secrets[0].SecretLength))}\n")); + } + + if (secrets[0].IsSet.ClientTrafficSecret0 != 0) + { + stream.Write(Encoding.ASCII.GetBytes($"CLIENT_TRAFFIC_SECRET_0 {clientRandom} {HexConverter.ToString(buffer.Slice(s_clientTrafficSecretOffset, secrets[0].SecretLength))}\n")); + } + + if (secrets[0].IsSet.ServerTrafficSecret0 != 0) + { + stream.Write(Encoding.ASCII.GetBytes($"SERVER_TRAFFIC_SECRET_0 {clientRandom} {HexConverter.ToString(buffer.Slice(s_serverTrafficSecretOffset, secrets[0].SecretLength))}\n")); + } + + if (secrets[0].IsSet.ClientEarlyTrafficSecret != 0) { - string clientRandom = string.Empty; - ReadOnlySpan secrets = new ReadOnlySpan(tls, sizeof(QUIC_TLS_SECRETS)); - - if (tlsSecret.IsSet.ClientRandom != 0) - { - clientRandom = HexConverter.ToString(secrets.Slice(ClientRandomOffset, 32)); - } - - if (tlsSecret.IsSet.ClientHandshakeTrafficSecret != 0) - { - stream.Write(Encoding.ASCII.GetBytes($"CLIENT_HANDSHAKE_TRAFFIC_SECRET {clientRandom} {HexConverter.ToString(secrets.Slice(ClientHandshakeTrafficSecretOffset, tlsSecret.SecretLength))}\n")); - } - - if (tlsSecret.IsSet.ServerHandshakeTrafficSecret != 0) - { - stream.Write(Encoding.ASCII.GetBytes($"SERVER_HANDSHAKE_TRAFFIC_SECRET {clientRandom} {HexConverter.ToString(secrets.Slice(ServerHandshakeTrafficSecretOffset, tlsSecret.SecretLength))}\n")); - } - - if (tlsSecret.IsSet.ClientTrafficSecret0 != 0) - { - stream.Write(Encoding.ASCII.GetBytes($"CLIENT_TRAFFIC_SECRET_0 {clientRandom} {HexConverter.ToString(secrets.Slice(ClientTrafficSecretOffset, tlsSecret.SecretLength))}\n")); - } - - if (tlsSecret.IsSet.ServerTrafficSecret0 != 0) - { - stream.Write(Encoding.ASCII.GetBytes($"SERVER_TRAFFIC_SECRET_0 {clientRandom} {HexConverter.ToString(secrets.Slice(ServerTrafficSecretOffset, tlsSecret.SecretLength))}\n")); - } - - if (tlsSecret.IsSet.ClientEarlyTrafficSecret != 0) - { - stream.Write(Encoding.ASCII.GetBytes($"CLIENT_EARLY_TRAFFIC_SECRET {clientRandom} {HexConverter.ToString(secrets.Slice(ClientEarlyTrafficSecretOffset, tlsSecret.SecretLength))}\n")); - } + stream.Write(Encoding.ASCII.GetBytes($"CLIENT_EARLY_TRAFFIC_SECRET {clientRandom} {HexConverter.ToString(buffer.Slice(s_clientEarlyTrafficSecretOffset, secrets[0].SecretLength))}\n")); } } } - public void Dispose() + public unsafe void Dispose() { - if (gcHandle.IsAllocated) + IntPtr ptr = Interlocked.Exchange(ref tlsSecretsPtr, IntPtr.Zero); + if (ptr != IntPtr.Zero) { - gcHandle.Free(); + Span secret = new Span((void*)ptr, sizeof(QUIC_TLS_SECRETS)); + secret.Clear(); + NativeMemory.Free((void*)ptr); } } } +#endif diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs index 8d697ae9d12adf..433bc3e79aec2c 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs @@ -39,11 +39,8 @@ public sealed partial class QuicConnection : IAsyncDisposable { #if DEBUG /// - /// Name of file where secrets should be stored. - /// - private static readonly string? KeyLogFile = Environment.GetEnvironmentVariable("SSLKEYLOGFILE"); - - /// The actual secret structure wrapper passed to MsQuic + /// The actual secret structure wrapper passed to MsQuic. + /// private MsQuicTlsSecret? _tlsSecret; #endif @@ -195,19 +192,16 @@ private unsafe QuicConnection() &handle), "ConnectionOpen failed"); _handle = new MsQuicContextSafeHandle(handle, context, SafeHandleType.Connection); - -#if DEBUG - if (KeyLogFile != null) - { - _tlsSecret = new MsQuicTlsSecret(_handle); - } -#endif } catch { context.Free(); throw; } + +#if DEBUG + _tlsSecret = MsQuicTlsSecret.Create(_handle); +#endif } /// @@ -236,10 +230,7 @@ internal unsafe QuicConnection(QUIC_HANDLE* handle, QUIC_NEW_CONNECTION_INFO* in _remoteEndPoint = info->RemoteAddress->ToIPEndPoint(); _localEndPoint = info->LocalAddress->ToIPEndPoint(); #if DEBUG - if (KeyLogFile != null) - { - _tlsSecret = new MsQuicTlsSecret(_handle); - } + _tlsSecret = MsQuicTlsSecret.Create(_handle); #endif } @@ -478,10 +469,11 @@ private unsafe int HandleEventConnected(ref CONNECTED_DATA data) } #if DEBUG - if (KeyLogFile != null && _tlsSecret != null) + if (_tlsSecret != null) { - _tlsSecret.WriteSecret(KeyLogFile); + _tlsSecret.WriteSecret(); _tlsSecret.Dispose(); + _tlsSecret = null; } #endif _connectedTcs.TrySetResult(); From da206a68e4811893ceffa6904c612b37a00e71e6 Mon Sep 17 00:00:00 2001 From: wfurt Date: Mon, 15 May 2023 09:41:08 -0700 Subject: [PATCH 3/4] fixes & feedback --- .../Net/Quic/Internal/MsQuicSafeHandle.cs | 26 +++- .../Net/Quic/Internal/MsQuicTlsSecret.cs | 134 ++++++++---------- 2 files changed, 85 insertions(+), 75 deletions(-) diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicSafeHandle.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicSafeHandle.cs index 3adc922745423d..8e87654f2e321c 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicSafeHandle.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicSafeHandle.cs @@ -92,6 +92,23 @@ internal sealed class MsQuicContextSafeHandle : MsQuicSafeHandle /// private readonly MsQuicSafeHandle? _parent; +#if DEBUG + /// + /// Native memory to hold TLS secrets. It needs to live same cycle as the underlying connection. + /// + private unsafe QUIC_TLS_SECRETS* _tlsSecrets; + + public unsafe QUIC_TLS_SECRETS* GetSecretsBuffer() + { + if (_tlsSecrets == null) + { + _tlsSecrets = (QUIC_TLS_SECRETS*)NativeMemory.Alloc((nuint)sizeof(QUIC_TLS_SECRETS)); + } + + return _tlsSecrets; + } +#endif + public unsafe MsQuicContextSafeHandle(QUIC_HANDLE* handle, GCHandle context, SafeHandleType safeHandleType, MsQuicSafeHandle? parent = null) : base(handle, safeHandleType) { @@ -108,7 +125,7 @@ public unsafe MsQuicContextSafeHandle(QUIC_HANDLE* handle, GCHandle context, Saf } } - protected override bool ReleaseHandle() + protected override unsafe bool ReleaseHandle() { base.ReleaseHandle(); if (_context.IsAllocated) @@ -123,6 +140,13 @@ protected override bool ReleaseHandle() NetEventSource.Info(this, $"{this} {_parent} ref count decremented"); } } +#if DEBUG + if (_tlsSecrets != null) + { + NativeMemory.Clear(_tlsSecrets, (nuint)sizeof(QUIC_TLS_SECRETS)); + NativeMemory.Free(_tlsSecrets); + } +#endif return true; } } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicTlsSecret.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicTlsSecret.cs index adbf2b1b708a86..f6534181b897f7 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicTlsSecret.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicTlsSecret.cs @@ -15,104 +15,90 @@ namespace System.Net.Quic; internal sealed class MsQuicTlsSecret : IDisposable { private static readonly string? s_keyLogFile = Environment.GetEnvironmentVariable("SSLKEYLOGFILE"); + private static readonly FileStream? s_fileStream = s_keyLogFile != null ? File.Open(s_keyLogFile, FileMode.Append, FileAccess.Write) : null; - private static readonly int s_clientRandomOffset = (int)Marshal.OffsetOf(typeof(QUIC_TLS_SECRETS), "ClientRandom"); - private static readonly int s_clientEarlyTrafficSecretOffset = (int)Marshal.OffsetOf(typeof(QUIC_TLS_SECRETS), "ClientEarlyTrafficSecret"); - private static readonly int s_clientHandshakeTrafficSecretOffset = (int)Marshal.OffsetOf(typeof(QUIC_TLS_SECRETS), "ClientHandshakeTrafficSecret"); - private static readonly int s_serverHandshakeTrafficSecretOffset = (int)Marshal.OffsetOf(typeof(QUIC_TLS_SECRETS), "ServerHandshakeTrafficSecret"); - private static readonly int s_clientTrafficSecretOffset = (int)Marshal.OffsetOf(typeof(QUIC_TLS_SECRETS), "ClientTrafficSecret0"); - private static readonly int s_serverTrafficSecretOffset = (int)Marshal.OffsetOf(typeof(QUIC_TLS_SECRETS), "ServerTrafficSecret0"); + private unsafe QUIC_TLS_SECRETS* _tlsSecrets; - private IntPtr tlsSecretsPtr = IntPtr.Zero; - - public static MsQuicTlsSecret? Create(MsQuicContextSafeHandle handle) + public static unsafe MsQuicTlsSecret? Create(MsQuicContextSafeHandle handle) { - MsQuicTlsSecret? secret = null; - if (s_keyLogFile != null) + if (s_fileStream != null) { try { - secret = new MsQuicTlsSecret(handle); + QUIC_TLS_SECRETS* ptr = handle.GetSecretsBuffer(); + if (ptr != null) + { + int status = MsQuicApi.Api.SetParam(handle, QUIC_PARAM_CONN_TLS_SECRETS, (uint)sizeof(QUIC_TLS_SECRETS), ptr ); + + if (status == QUIC_STATUS_SUCCESS) + { + return new MsQuicTlsSecret(ptr); + } + else + { + NetEventSource.Error(handle, "Failed to set native memory for TLS secret."); + } + } } catch { }; } - return secret; - } - - private unsafe MsQuicTlsSecret(MsQuicContextSafeHandle handle) - { - void * ptr = NativeMemory.Alloc((nuint)sizeof(QUIC_TLS_SECRETS)); - if (ptr != null) - { - MsQuicApi.Api.SetParam(handle, QUIC_PARAM_CONN_TLS_SECRETS, (uint)sizeof(QUIC_TLS_SECRETS), ptr); - tlsSecretsPtr = (IntPtr)ptr; - } + return null; } - public void WriteSecret() + private unsafe MsQuicTlsSecret(QUIC_TLS_SECRETS* memory) { - if (s_keyLogFile != null) - { - WriteSecret(s_keyLogFile); - } + _tlsSecrets = memory; } - public void WriteSecret(string fileName) + public void WriteSecret() => WriteSecret(s_fileStream); + public unsafe void WriteSecret(FileStream? stream) { - using (FileStream stream = File.Open(fileName, FileMode.Append, FileAccess.Write)) + if (stream != null && _tlsSecrets != null) { - WriteSecret(stream); - } - } - - public unsafe void WriteSecret(FileStream stream) - { - lock (stream) - { - string clientRandom = string.Empty; - ReadOnlySpan buffer = new ReadOnlySpan((void*)tlsSecretsPtr, sizeof(QUIC_TLS_SECRETS)); - ReadOnlySpan secrets = MemoryMarshal.Cast(buffer); - - if (secrets[0].IsSet.ClientRandom != 0) - { - clientRandom = HexConverter.ToString(buffer.Slice(s_clientRandomOffset, 32)); - } - - if (secrets[0].IsSet.ClientHandshakeTrafficSecret != 0) - { - stream.Write(Encoding.ASCII.GetBytes($"CLIENT_HANDSHAKE_TRAFFIC_SECRET {clientRandom} {HexConverter.ToString(buffer.Slice(s_clientHandshakeTrafficSecretOffset, secrets[0].SecretLength))}\n")); - } - - if (secrets[0].IsSet.ServerHandshakeTrafficSecret != 0) - { - stream.Write(Encoding.ASCII.GetBytes($"SERVER_HANDSHAKE_TRAFFIC_SECRET {clientRandom} {HexConverter.ToString(buffer.Slice(s_serverHandshakeTrafficSecretOffset, secrets[0].SecretLength))}\n")); - } - - if (secrets[0].IsSet.ClientTrafficSecret0 != 0) - { - stream.Write(Encoding.ASCII.GetBytes($"CLIENT_TRAFFIC_SECRET_0 {clientRandom} {HexConverter.ToString(buffer.Slice(s_clientTrafficSecretOffset, secrets[0].SecretLength))}\n")); - } - - if (secrets[0].IsSet.ServerTrafficSecret0 != 0) - { - stream.Write(Encoding.ASCII.GetBytes($"SERVER_TRAFFIC_SECRET_0 {clientRandom} {HexConverter.ToString(buffer.Slice(s_serverTrafficSecretOffset, secrets[0].SecretLength))}\n")); - } - - if (secrets[0].IsSet.ClientEarlyTrafficSecret != 0) + lock (stream) { - stream.Write(Encoding.ASCII.GetBytes($"CLIENT_EARLY_TRAFFIC_SECRET {clientRandom} {HexConverter.ToString(buffer.Slice(s_clientEarlyTrafficSecretOffset, secrets[0].SecretLength))}\n")); + string clientRandom = string.Empty; + + if (_tlsSecrets->IsSet.ClientRandom != 0) + { + clientRandom = HexConverter.ToString(new ReadOnlySpan(_tlsSecrets->ClientRandom, 32)); + } + + if (_tlsSecrets->IsSet.ClientHandshakeTrafficSecret != 0) + { + stream.Write(Encoding.ASCII.GetBytes($"CLIENT_HANDSHAKE_TRAFFIC_SECRET {clientRandom} {HexConverter.ToString(new ReadOnlySpan(_tlsSecrets->ClientHandshakeTrafficSecret, _tlsSecrets->SecretLength))}\n")); + } + + if (_tlsSecrets->IsSet.ServerHandshakeTrafficSecret != 0) + { + stream.Write(Encoding.ASCII.GetBytes($"SERVER_HANDSHAKE_TRAFFIC_SECRET {clientRandom} {HexConverter.ToString(new ReadOnlySpan(_tlsSecrets->ServerHandshakeTrafficSecret, _tlsSecrets->SecretLength))}\n")); + } + + if (_tlsSecrets->IsSet.ClientTrafficSecret0 != 0) + { + stream.Write(Encoding.ASCII.GetBytes($"CLIENT_TRAFFIC_SECRET_0 {clientRandom} {HexConverter.ToString(new ReadOnlySpan(_tlsSecrets->ClientTrafficSecret0, _tlsSecrets->SecretLength))}\n")); + } + + if (_tlsSecrets->IsSet.ServerTrafficSecret0 != 0) + { + stream.Write(Encoding.ASCII.GetBytes($"SERVER_TRAFFIC_SECRET_0 {clientRandom} {HexConverter.ToString(new ReadOnlySpan(_tlsSecrets->ServerTrafficSecret0, _tlsSecrets->SecretLength))}\n")); + } + + if (_tlsSecrets->IsSet.ClientEarlyTrafficSecret != 0) + { + stream.Write(Encoding.ASCII.GetBytes($"CLIENT_EARLY_TRAFFIC_SECRET {clientRandom} {HexConverter.ToString(new ReadOnlySpan(_tlsSecrets->ClientEarlyTrafficSecret, _tlsSecrets->SecretLength))}\n")); + } + + stream.Flush(); } } } public unsafe void Dispose() { - IntPtr ptr = Interlocked.Exchange(ref tlsSecretsPtr, IntPtr.Zero); - if (ptr != IntPtr.Zero) + if (_tlsSecrets != null) { - Span secret = new Span((void*)ptr, sizeof(QUIC_TLS_SECRETS)); - secret.Clear(); - NativeMemory.Free((void*)ptr); + NativeMemory.Clear(_tlsSecrets, (nuint)sizeof(QUIC_TLS_SECRETS)); } } } From 2cfe28f0c44c381db0a7c03a3fa417db26cfc916 Mon Sep 17 00:00:00 2001 From: wfurt Date: Mon, 5 Jun 2023 03:30:57 -0700 Subject: [PATCH 4/4] feedback from review --- .../src/System/Net/Quic/Internal/MsQuicTlsSecret.cs | 9 ++++++--- .../src/System/Net/Quic/QuicConnection.cs | 8 -------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicTlsSecret.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicTlsSecret.cs index f724752878b68b..3a19656f63acb7 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicTlsSecret.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicTlsSecret.cs @@ -28,15 +28,18 @@ internal sealed class MsQuicTlsSecret : IDisposable QUIC_TLS_SECRETS* ptr = handle.GetSecretsBuffer(); if (ptr != null) { - int status = MsQuicApi.Api.SetParam(handle, QUIC_PARAM_CONN_TLS_SECRETS, (uint)sizeof(QUIC_TLS_SECRETS), ptr ); + int status = MsQuicApi.Api.SetParam(handle, QUIC_PARAM_CONN_TLS_SECRETS, (uint)sizeof(QUIC_TLS_SECRETS), ptr); - if (status == QUIC_STATUS_SUCCESS) + if (StatusSucceeded(status)) { return new MsQuicTlsSecret(ptr); } else { - NetEventSource.Error(handle, "Failed to set native memory for TLS secret."); + if (NetEventSource.Log.IsEnabled()) + { + NetEventSource.Error(handle, "Failed to set native memory for TLS secret."); + } } } } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs index e481fc157d98dc..afdc5b536f1aef 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs @@ -486,14 +486,6 @@ private unsafe int HandleEventConnected(ref CONNECTED_DATA data) NetEventSource.Info(this, $"{this} Received event CONNECTED {LocalEndPoint} -> {RemoteEndPoint}"); } -#if DEBUG - if (_tlsSecret != null) - { - _tlsSecret.WriteSecret(); - _tlsSecret.Dispose(); - _tlsSecret = null; - } -#endif _connectedTcs.TrySetResult(); return QUIC_STATUS_SUCCESS; }