Skip to content
Merged
Prev Previous commit
Next Next commit
Quick and dirty version to enable benchmarking
  • Loading branch information
rzikm committed Feb 26, 2024
commit aada566d76654985755bbd3fe73b424e1436cbb7
Original file line number Diff line number Diff line change
Expand Up @@ -375,4 +375,21 @@ public int StreamReceiveSetEnabled(MsQuicSafeHandle stream, byte enabled)
}
}
}

public int ConnectionCertificateValidationComplete(MsQuicSafeHandle connection, bool result, QUIC_TLS_ALERT_CODES alert)
{
bool success = false;
try
{
connection.DangerousAddRef(ref success);
return ApiTable->ConnectionCertificateValidationComplete(connection.QuicHandle, (byte)(result ? 1 : 0), alert);
}
finally
{
if (success)
{
connection.DangerousRelease();
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,65 +63,38 @@ public SslConnectionOptions(QuicConnection connection, bool isClient,
_certificateChainPolicy = certificateChainPolicy;
}

public unsafe int ValidateCertificate(QUIC_BUFFER* certificatePtr, QUIC_BUFFER* chainPtr, out X509Certificate2? certificate)
public unsafe int ValidateCertificate(X509Certificate2? certificate, X509Chain? chain)
{
SslPolicyErrors sslPolicyErrors = SslPolicyErrors.None;
IntPtr certificateBuffer = 0;
int certificateLength = 0;
bool wrapException = false;

X509Chain? chain = null;
X509Certificate2? result = null;
try
{
if (certificatePtr is not null)
{
chain = new X509Chain();
if (_certificateChainPolicy != null)
{
chain.ChainPolicy = _certificateChainPolicy;
}
else
{
chain.ChainPolicy.RevocationMode = _revocationMode;
chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot;

// TODO: configure chain.ChainPolicy.CustomTrustStore to mirror behavior of SslStream.VerifyRemoteCertificate (https://github.com/dotnet/runtime/issues/73053)
}
chain ??= new X509Chain();

// set ApplicationPolicy unless already provided.
if (chain.ChainPolicy.ApplicationPolicy.Count == 0)
{
// Authenticate the remote party: (e.g. when operating in server mode, authenticate the client).
chain.ChainPolicy.ApplicationPolicy.Add(_isClient ? s_serverAuthOid : s_clientAuthOid);
}
if (_certificateChainPolicy != null)
{
chain.ChainPolicy = _certificateChainPolicy;
}
else
{
chain.ChainPolicy.RevocationMode = _revocationMode;
chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot;

if (MsQuicApi.UsesSChannelBackend)
{
result = new X509Certificate2((IntPtr)certificatePtr);
}
else
{
if (certificatePtr->Length > 0)
{
certificateBuffer = (IntPtr)certificatePtr->Buffer;
certificateLength = (int)certificatePtr->Length;
result = new X509Certificate2(certificatePtr->Span);
}
// TODO: configure chain.ChainPolicy.CustomTrustStore to mirror behavior of SslStream.VerifyRemoteCertificate (https://github.com/dotnet/runtime/issues/73053)
}

if (chainPtr->Length > 0)
{
X509Certificate2Collection additionalCertificates = new X509Certificate2Collection();
additionalCertificates.Import(chainPtr->Span);
chain.ChainPolicy.ExtraStore.AddRange(additionalCertificates);
}
}
// set ApplicationPolicy unless already provided.
if (chain.ChainPolicy.ApplicationPolicy.Count == 0)
{
// Authenticate the remote party: (e.g. when operating in server mode, authenticate the client).
chain.ChainPolicy.ApplicationPolicy.Add(_isClient ? s_serverAuthOid : s_clientAuthOid);
}

if (result is not null)
if (certificate is not null)
{
bool checkCertName = !chain!.ChainPolicy!.VerificationFlags.HasFlag(X509VerificationFlags.IgnoreInvalidName);
sslPolicyErrors |= CertificateValidation.BuildChainAndVerifyProperties(chain!, result, checkCertName, !_isClient, TargetHostNameHelper.NormalizeHostName(_targetHost), certificateBuffer, certificateLength);
sslPolicyErrors |= CertificateValidation.BuildChainAndVerifyProperties(chain!, certificate, checkCertName, !_isClient, TargetHostNameHelper.NormalizeHostName(_targetHost), IntPtr.Zero, 0);
}
else if (_certificateRequired)
{
Expand All @@ -132,7 +105,7 @@ public unsafe int ValidateCertificate(QUIC_BUFFER* certificatePtr, QUIC_BUFFER*
if (_validationCallback is not null)
{
wrapException = true;
if (!_validationCallback(_connection, result, chain, sslPolicyErrors))
if (!_validationCallback(_connection, certificate, chain, sslPolicyErrors))
{
wrapException = false;
if (_isClient)
Expand All @@ -153,12 +126,11 @@ public unsafe int ValidateCertificate(QUIC_BUFFER* certificatePtr, QUIC_BUFFER*
status = QUIC_STATUS_HANDSHAKE_FAILURE;
}

certificate = result;
return status;
}
catch (Exception ex)
{
result?.Dispose();
certificate?.Dispose();
if (wrapException)
{
throw new QuicException(QuicError.CallbackError, null, SR.net_quic_callback_error, ex);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.Diagnostics.Metrics;
using System.Net.Security;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
Expand Down Expand Up @@ -571,15 +572,56 @@ private unsafe int HandleEventPeerStreamStarted(ref PEER_STREAM_STARTED_DATA dat
}
private unsafe int HandleEventPeerCertificateReceived(ref PEER_CERTIFICATE_RECEIVED_DATA data)
{
try
X509Certificate2? certificate = null;
X509Chain? chain = null;

QUIC_BUFFER* certificatePtr = (QUIC_BUFFER*)data.Certificate;
QUIC_BUFFER* chainPtr = (QUIC_BUFFER*)data.Chain;

if (certificatePtr is not null)
{
return _sslConnectionOptions.ValidateCertificate((QUIC_BUFFER*)data.Certificate, (QUIC_BUFFER*)data.Chain, out _remoteCertificate);
chain = new X509Chain();
if (MsQuicApi.UsesSChannelBackend)
{
certificate = new X509Certificate2((IntPtr)certificatePtr);
}
else
{
if (certificatePtr->Length > 0)
{
certificate = new X509Certificate2(certificatePtr->Span);
}

if (chainPtr->Length > 0)
{
X509Certificate2Collection additionalCertificates = new X509Certificate2Collection();
additionalCertificates.Import(chainPtr->Span);
chain.ChainPolicy.ExtraStore.AddRange(additionalCertificates);
}
}
}
catch (Exception ex)

_ = Task.Run(() =>
{
_connectedTcs.TrySetException(ex);
return QUIC_STATUS_HANDSHAKE_FAILURE;
}
int result;
try
{
result = _sslConnectionOptions.ValidateCertificate(certificate, chain);
_remoteCertificate = certificate;
}
catch (Exception ex)
{
_connectedTcs.TrySetException(ex);
result = QUIC_STATUS_HANDSHAKE_FAILURE;
}

bool success = result == QUIC_STATUS_SUCCESS;

// return result;
MsQuicApi.Api.ConnectionCertificateValidationComplete(_handle, success, success ? QUIC_TLS_ALERT_CODES.SUCCESS : QUIC_TLS_ALERT_CODES.BAD_CERTIFICATE);
});

return QUIC_STATUS_PENDING;
}

private unsafe int HandleConnectionEvent(ref QUIC_CONNECTION_EVENT connectionEvent)
Expand All @@ -596,6 +638,9 @@ private unsafe int HandleConnectionEvent(ref QUIC_CONNECTION_EVENT connectionEve
_ => QUIC_STATUS_SUCCESS,
};

private static readonly Meter s_meter = new Meter("System.Net.Quic.QuicConnection.Events");
private static readonly Histogram<double> s_eventProcessing = s_meter.CreateHistogram<double>("eventloop.process", "ms", "");

#pragma warning disable CS3016
[UnmanagedCallersOnly(CallConvs = new Type[] { typeof(CallConvCdecl) })]
#pragma warning restore CS3016
Expand All @@ -613,6 +658,7 @@ private static unsafe int NativeCallback(QUIC_HANDLE* connection, void* context,
return QUIC_STATUS_INVALID_STATE;
}

long timestamp = Stopwatch.GetTimestamp();
try
{
// Process the event.
Expand All @@ -630,6 +676,13 @@ private static unsafe int NativeCallback(QUIC_HANDLE* connection, void* context,
}
return QUIC_STATUS_INTERNAL_ERROR;
}
finally
{
var elapsed = Stopwatch.GetElapsedTime(timestamp);
TagList tags = default;
tags.Add("Type", connectionEvent->Type.ToString());
s_eventProcessing.Record(elapsed.TotalMilliseconds, tags);
}
}

/// <summary>
Expand Down