Files
natsnet/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Auth/OcspPeerValidationTests.cs
2026-02-28 12:34:38 -05:00

194 lines
5.7 KiB
C#

using System.Net.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using Shouldly;
using ZB.MOM.NatsNet.Server;
using ZB.MOM.NatsNet.Server.Auth.CertificateIdentityProvider;
namespace ZB.MOM.NatsNet.Server.Tests.Auth;
public sealed class OcspPeerValidationTests : IDisposable
{
private readonly List<X509Certificate2> _certs = [];
[Fact]
public void ParseOCSPPeer_ValidMap_ReturnsConfig()
{
Dictionary<string, object?> map = new()
{
["verify"] = true,
["allowed_clockskew"] = 30.0,
["ca_timeout"] = 5.0,
["cache_ttl_when_next_update_unset"] = 120.0,
["warn_only"] = true,
["unknown_is_good"] = true,
["allow_when_ca_unreachable"] = true,
};
var (config, err) = OcspHandler.ParseOCSPPeer(map);
err.ShouldBeNull();
config.ShouldNotBeNull();
config.Verify.ShouldBeTrue();
config.ClockSkew.ShouldBe(30.0);
config.Timeout.ShouldBe(5.0);
config.TTLUnsetNextUpdate.ShouldBe(120.0);
config.WarnOnly.ShouldBeTrue();
config.UnknownIsGood.ShouldBeTrue();
config.AllowWhenCAUnreachable.ShouldBeTrue();
}
[Fact]
public void PeerFromVerifiedChains_EmptyChains_ReturnsNull()
{
OcspHandler.PeerFromVerifiedChains([]).ShouldBeNull();
}
[Fact]
public void PeerFromVerifiedChains_NonEmptyChains_ReturnsFirstLeaf()
{
var cert = CreateSelfSignedCertificate("CN=peer-first");
var result = OcspHandler.PeerFromVerifiedChains([[cert]]);
result.ShouldNotBeNull();
result!.Subject.ShouldBe(cert.Subject);
}
[Fact]
public void PlugTLSOCSPPeer_ClientWithoutVerify_ReturnsError()
{
var server = NewServer();
var cert = CreateSelfSignedCertificate("CN=client-no-verify");
var config = new OcspTlsConfig
{
Kind = "client",
TlsConfig = new SslServerAuthenticationOptions { ServerCertificate = cert },
TlsOptions = new TlsConfigOpts
{
Verify = false,
OcspPeerConfig = new OcspPeerConfig { Verify = true },
},
Apply = _ => { },
};
var (_, plugged, err) = server.PlugTLSOCSPPeer(config);
plugged.ShouldBeFalse();
err.ShouldNotBeNull();
err!.Message.ShouldContain("mTLS");
}
[Fact]
public void PlugTLSOCSPPeer_ClientWithVerify_ReturnsPlugged()
{
var server = NewServer();
var cert = CreateSelfSignedCertificate("CN=client-verify");
var config = new OcspTlsConfig
{
Kind = "client",
TlsConfig = new SslServerAuthenticationOptions { ServerCertificate = cert },
TlsOptions = new TlsConfigOpts
{
Verify = true,
OcspPeerConfig = new OcspPeerConfig { Verify = true },
},
Apply = _ => { },
};
var (tlsConfig, plugged, err) = server.PlugTLSOCSPPeer(config);
err.ShouldBeNull();
plugged.ShouldBeTrue();
tlsConfig.ShouldNotBeNull();
tlsConfig!.RemoteCertificateValidationCallback.ShouldNotBeNull();
}
[Fact]
public void PlugTLSOCSPPeer_LeafSpoke_ReturnsPlugged()
{
var server = NewServer();
var cert = CreateSelfSignedCertificate("CN=leaf-spoke");
var config = new OcspTlsConfig
{
Kind = "leaf",
IsLeafSpoke = true,
TlsConfig = new SslServerAuthenticationOptions { ServerCertificate = cert },
TlsOptions = new TlsConfigOpts
{
Verify = true,
OcspPeerConfig = new OcspPeerConfig { Verify = true },
},
Apply = _ => { },
};
var (_, plugged, err) = server.PlugTLSOCSPPeer(config);
err.ShouldBeNull();
plugged.ShouldBeTrue();
}
[Fact]
public void TlsServerOCSPValid_SelfSignedChain_ReturnsTrue()
{
var server = NewServer();
var cert = CreateSelfSignedCertificate("CN=selfsigned");
var opts = new OcspPeerConfig { Verify = true };
var valid = server.TlsServerOCSPValid([[cert]], opts);
valid.ShouldBeTrue();
}
[Fact]
public void TlsClientOCSPValid_EmptyChains_ReturnsFalse()
{
var server = NewServer();
var opts = new OcspPeerConfig { Verify = true };
var valid = server.TlsClientOCSPValid([], opts);
valid.ShouldBeFalse();
}
[Fact]
public void CertOCSPGood_EmptyChainLink_ReturnsFalse()
{
var server = NewServer();
var (reason, good) = server.CertOCSPGood(new ChainLink(), new OcspPeerConfig());
good.ShouldBeFalse();
reason.ShouldContain("Empty chainlink");
}
public void Dispose()
{
foreach (var cert in _certs)
{
cert.Dispose();
}
}
private NatsServer NewServer()
{
var (server, err) = NatsServer.NewServer(new ServerOptions());
err.ShouldBeNull();
return server!;
}
private X509Certificate2 CreateSelfSignedCertificate(string subject)
{
var request = new CertificateRequest(
subject,
RSA.Create(2048),
HashAlgorithmName.SHA256,
RSASignaturePadding.Pkcs1);
request.CertificateExtensions.Add(new X509BasicConstraintsExtension(false, false, 0, true));
request.CertificateExtensions.Add(new X509SubjectKeyIdentifierExtension(request.PublicKey, false));
var cert = request.CreateSelfSigned(DateTimeOffset.UtcNow.AddDays(-1), DateTimeOffset.UtcNow.AddDays(30));
_certs.Add(cert);
return cert;
}
}