using System.Security.Cryptography; using System.Text.Json; using NATS.Server.Configuration; using NATS.Server.TestUtilities; using NATS.Server.Tls; namespace NATS.Server.Transport.Tests; public class TlsOcspParityBatch1Tests { [Fact] public void OCSPPeerConfig_defaults_match_go_reference() { var cfg = OCSPPeerConfig.NewOCSPPeerConfig(); cfg.Verify.ShouldBeFalse(); cfg.Timeout.ShouldBe(2d); cfg.ClockSkew.ShouldBe(30d); cfg.WarnOnly.ShouldBeFalse(); cfg.UnknownIsGood.ShouldBeFalse(); cfg.AllowWhenCAUnreachable.ShouldBeFalse(); cfg.TTLUnsetNextUpdate.ShouldBe(3600d); } [Fact] public void OCSPPeerConfig_parse_map_parses_supported_fields() { var cfg = OCSPPeerConfig.Parse(new Dictionary { ["verify"] = true, ["allowed_clockskew"] = "45s", ["ca_timeout"] = 1.5d, ["cache_ttl_when_next_update_unset"] = 120L, ["warn_only"] = true, ["unknown_is_good"] = true, ["allow_when_ca_unreachable"] = true, }); cfg.Verify.ShouldBeTrue(); cfg.ClockSkew.ShouldBe(45d); cfg.Timeout.ShouldBe(1.5d); cfg.TTLUnsetNextUpdate.ShouldBe(120d); cfg.WarnOnly.ShouldBeTrue(); cfg.UnknownIsGood.ShouldBeTrue(); cfg.AllowWhenCAUnreachable.ShouldBeTrue(); } [Fact] public void OCSPPeerConfig_parse_unknown_field_throws() { var ex = Should.Throw(() => OCSPPeerConfig.Parse(new Dictionary { ["bogus"] = true })); ex.Message.ShouldContain("unknown field [bogus]"); } [Fact] public void ConfigProcessor_parses_ocsp_peer_short_form() { var opts = ConfigProcessor.ProcessConfig(""" tls { ocsp_peer: true } """); opts.OcspPeerVerify.ShouldBeTrue(); } [Fact] public void ConfigProcessor_parses_ocsp_peer_long_form_verify() { var opts = ConfigProcessor.ProcessConfig(""" tls { ocsp_peer { verify: true ca_timeout: 2s allowed_clockskew: 30s } } """); opts.OcspPeerVerify.ShouldBeTrue(); } [Fact] public void GenerateFingerprint_uses_raw_certificate_sha256() { var (cert, _) = TestCertHelper.GenerateTestCert(); var expected = Convert.ToBase64String(SHA256.HashData(cert.RawData)); TlsHelper.GenerateFingerprint(cert).ShouldBe(expected); } [Fact] public void GetWebEndpoints_filters_non_web_uris() { var urls = TlsHelper.GetWebEndpoints( ["http://a.example", "https://b.example", "ftp://bad.example", "not a uri"]); urls.Count.ShouldBe(2); urls[0].Scheme.ShouldBe(Uri.UriSchemeHttp); urls[1].Scheme.ShouldBe(Uri.UriSchemeHttps); } [Fact] public void Subject_and_issuer_dn_helpers_return_values_and_empty_for_null() { var (cert, _) = TestCertHelper.GenerateTestCert(); TlsHelper.GetSubjectDNForm(cert).ShouldNotBeNullOrWhiteSpace(); TlsHelper.GetIssuerDNForm(cert).ShouldNotBeNullOrWhiteSpace(); TlsHelper.GetSubjectDNForm(null).ShouldBe(string.Empty); TlsHelper.GetIssuerDNForm(null).ShouldBe(string.Empty); } [Fact] public void StatusAssertion_json_converter_uses_string_values_and_unknown_fallback() { var revokedJson = JsonSerializer.Serialize(StatusAssertion.Revoked); revokedJson.ShouldBe("\"revoked\""); var unknown = JsonSerializer.Deserialize("\"nonsense\""); unknown.ShouldBe(StatusAssertion.Unknown); } [Fact] public void OcspPeer_messages_match_go_literals() { OcspPeerMessages.MsgTLSClientRejectConnection.ShouldBe("client not OCSP valid"); OcspPeerMessages.MsgTLSServerRejectConnection.ShouldBe("server not OCSP valid"); OcspPeerMessages.MsgCacheOnline.ShouldBe("OCSP peer cache online, type [%s]"); OcspPeerMessages.MsgCacheOffline.ShouldBe("OCSP peer cache offline, type [%s]"); } }