fix(historian-sidecar): cancel SocketException guard + version-reject log + TLS test (review)

This commit is contained in:
Joseph Doherty
2026-06-12 11:31:04 -04:00
parent fd4d05534e
commit 999e58c605
2 changed files with 35 additions and 1 deletions
@@ -97,7 +97,7 @@ public sealed class TcpRoundTripTests
userCertificateValidationCallback: (_, cert, _, _) =>
cert is not null &&
string.Equals(
new X509Certificate2(cert).Thumbprint,
cert.GetCertHashString(),
expectedThumbprint,
StringComparison.OrdinalIgnoreCase));
await ssl.AuthenticateAsClientAsync("otopcua-historian-sidecar-test", clientCertificates: null,
@@ -172,6 +172,38 @@ public sealed class TcpRoundTripTests
await serverTask;
}
/// <summary>
/// TLS: when the client pins a wrong thumbprint the validation callback returns false,
/// causing <see cref="SslStream.AuthenticateAsClientAsync"/> to throw
/// <see cref="AuthenticationException"/> before any Hello is exchanged.
/// </summary>
[Fact]
public async Task Tls_BadThumbprint_AuthenticationFails()
{
using var cts = new CancellationTokenSource(Timeout);
using var cert = MakeSelfSignedCert();
using var server = new TcpFrameServer(IPAddress.Loopback, 0, "shh", tlsCert: cert, Quiet);
var serverTask = server.RunOneConnectionAsync(new EchoHandler(), cts.Token);
using var client = new TcpClient();
await client.ConnectAsync(IPAddress.Loopback, server.BoundPort);
// Deliberately pin the wrong thumbprint — all zeros.
const string wrongThumbprint = "0000000000000000000000000000000000000000";
var ssl = new SslStream(client.GetStream(), leaveInnerStreamOpen: false,
userCertificateValidationCallback: (_, serverCert, _, _) =>
serverCert is not null &&
string.Equals(serverCert.GetCertHashString(), wrongThumbprint, StringComparison.OrdinalIgnoreCase));
await Should.ThrowAsync<AuthenticationException>(async () =>
await ssl.AuthenticateAsClientAsync("otopcua-historian-sidecar-test", clientCertificates: null,
enabledSslProtocols: SslProtocols.Tls12, checkCertificateRevocation: false));
ssl.Dispose();
// Server will see the broken TLS handshake and end the connection; let it finish.
try { await serverTask; } catch { /* server may throw on the aborted TLS */ }
}
/// <summary>Bad secret: Hello is rejected with Accepted=false and the shared-secret-mismatch reason.</summary>
[Fact]
public async Task BadSecret_HelloRejected()