From f9b582dcca4c62ed64d60bf194a9a4f17e9efb49 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Sat, 28 Feb 2026 06:05:06 -0500 Subject: [PATCH 1/5] feat(batch1): add init parity hooks for ciphers and constants --- .../Auth/CipherSuites.cs | 10 +++++ .../ZB.MOM.NatsNet.Server/ServerConstants.cs | 9 +++++ .../Auth/CipherSuitesTests.cs | 13 ++++++ .../ServerTests.cs | 11 ++++++ porting.db | Bin 6352896 -> 6352896 bytes reports/current.md | 8 ++-- reports/report_eed6169.md | 37 ++++++++++++++++++ 7 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 reports/report_eed6169.md diff --git a/dotnet/src/ZB.MOM.NatsNet.Server/Auth/CipherSuites.cs b/dotnet/src/ZB.MOM.NatsNet.Server/Auth/CipherSuites.cs index 1d05c3c..4a24ea6 100644 --- a/dotnet/src/ZB.MOM.NatsNet.Server/Auth/CipherSuites.cs +++ b/dotnet/src/ZB.MOM.NatsNet.Server/Auth/CipherSuites.cs @@ -56,6 +56,16 @@ public static class CipherSuites CipherMapById = byId; } + /// + /// Compatibility init hook for PortTracker parity with Go init(). + /// Safe and idempotent. + /// + public static void Init() + { + _ = CipherMap; + _ = CipherMapById; + } + /// /// Returns the default set of TLS 1.2 cipher suites. /// .NET manages cipher suite selection at the OS/SChannel/OpenSSL level; diff --git a/dotnet/src/ZB.MOM.NatsNet.Server/ServerConstants.cs b/dotnet/src/ZB.MOM.NatsNet.Server/ServerConstants.cs index 5243955..77b55ab 100644 --- a/dotnet/src/ZB.MOM.NatsNet.Server/ServerConstants.cs +++ b/dotnet/src/ZB.MOM.NatsNet.Server/ServerConstants.cs @@ -214,6 +214,15 @@ public static class ServerConstants GitCommit = string.Empty; } + /// + /// Compatibility init hook for PortTracker parity with Go init(). + /// Safe and idempotent. + /// + public static void Init() + { + _ = GitCommit; + } + /// /// Truncates a VCS revision string to 7 characters for display. /// Mirrors formatRevision in const.go. diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Auth/CipherSuitesTests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Auth/CipherSuitesTests.cs index ba577f3..fdad2a5 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Auth/CipherSuitesTests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Auth/CipherSuitesTests.cs @@ -24,6 +24,19 @@ namespace ZB.MOM.NatsNet.Server.Tests.Auth; /// public class CipherSuitesTests { + [Fact] + public void Init_CalledMultipleTimes_RemainsIdempotent() + { + var beforeCount = CipherSuites.CipherMap.Count; + var beforeByIdCount = CipherSuites.CipherMapById.Count; + + CipherSuites.Init(); + CipherSuites.Init(); + + CipherSuites.CipherMap.Count.ShouldBe(beforeCount); + CipherSuites.CipherMapById.Count.ShouldBe(beforeByIdCount); + } + [Fact] public void CipherMap_ContainsTls13Suites() { diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ServerTests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ServerTests.cs index 5f36584..2e4ca56 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ServerTests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ServerTests.cs @@ -32,6 +32,17 @@ namespace ZB.MOM.NatsNet.Server.Tests; /// public sealed class ServerTests { + [Fact] + public void Init_CalledMultipleTimes_RemainsIdempotent() + { + var before = ServerConstants.GitCommit; + + ServerConstants.Init(); + ServerConstants.Init(); + + ServerConstants.GitCommit.ShouldBe(before); + } + // ========================================================================= // TestSemanticVersion — Test ID 2866 // Validates that ServerConstants.Version matches semver format. diff --git a/porting.db b/porting.db index dbd9e94ad4b29b3e223d2882de1babe55bc28e87..7d41894cac4b1c73719af51bd140f39e9e9e74e2 100644 GIT binary patch delta 1296 zcmciCUuaWT90%~6lcZ_Wn|tfp#;$GZt=4U^O_SVoT}|ia|LXixTmNkSY1iB4)?Su> zy}41!#+o3QK@bEVjt?UC!9OTSErSD+hoWyj2$MY&_EN!#C`da-+4MobsYJxR4j(>n zxrcjx-}5^^Zf5?JH3QR^{N&=PDL1}HN2BcL0BvMv=gA~n@sOP1 z7akJXDmw04#F0cS8P!aU_V9a;$P>HhvU+IgCCTvFr(~<0%rg4|*};1k$S%UqFOq`8 zR{6K(0o(m2nP9QUq>ER)AkTpO<9rvQ4}uUgN({(RQJx!dgpHW_pxIge8TMd})Wqi1N;_MO3tnfJ91Mm79pO-bwB!$wR+?6E>s$d0t1l3Rjwcv)8PzN7FJv2Zgc%TVZfeg*C8rDDytc6eDQ}_(L zunt6CCfE#HU@L5c?NB`94%i9b zz%JMgdtfj0!anGOe%KEO;2<1=Z{a&Q3`gK7zp`{}&@0*n!y?s+IoH3gkYMoUeFH;6 zU3Nph$!YOW&x9xI3yo^3nKrZ($uKn@tY=a-SUssH$24PqS~s;+`$#I>87d91X{aMV zXc2idnm8#N+IU*Wr*dgk9@a(^h9;K=t6Pp~=2#*kx60VbOv!50(A3C;d_s-t5!KWp zN#hCqZTpZeJ4d}ZWqMdI{sCfI+|=X8<(QgGqG3DZx>@SRN!=V!$^+E|Nfvhv%@Sjq+sbZ=A|GCuhd_5a)7;xwO{s9<-@44SU^xN!f1yTl) delta 736 zcmW;9O-Pe*90&0I{@b%neYXFTxn;iP%B;ERV_LS-UbQk?<$5oh4RX~%^Mx)!lp(lY zE*ilPJVXX*>>$eye-H!3dsGBMT_#vWL2MBoBB(DNK8Md>ddNBmsi!854UL;>+@x5_ z;doCsnF@GonE7)TD4HlCuJ=U;_>8-~cD&K(0#8=2cAdqmaFn)76@tv)6S$ zx3}f$s^xXL+o-%f$57xs5 zsDO=737cRu_@N2{5QJ)|0TXIr3)De9Y=v#m0NWu1J76a?LKEzQ-LMDt!amp!&Cmh| z;2<1=RyYi8a0J3|6pq1hXom=#fRk_vPQw{E3+LcGT!0QWv2wA~Zzt)oNRRThNAslZ Y?$w7y++Q5^CQLI5op4E+X6)VEKl0mBN&o-= diff --git a/reports/current.md b/reports/current.md index a404530..3c033fb 100644 --- a/reports/current.md +++ b/reports/current.md @@ -1,6 +1,6 @@ # NATS .NET Porting Status Report -Generated: 2026-02-28 10:54:55 UTC +Generated: 2026-02-28 11:05:07 UTC ## Modules (12 total) @@ -12,10 +12,10 @@ Generated: 2026-02-28 10:54:55 UTC | Status | Count | |--------|-------| -| deferred | 2377 | +| deferred | 2375 | | n_a | 24 | | stub | 1 | -| verified | 1271 | +| verified | 1273 | ## Unit Tests (3257 total) @@ -34,4 +34,4 @@ Generated: 2026-02-28 10:54:55 UTC ## Overall Progress -**2473/6942 items complete (35.6%)** +**2475/6942 items complete (35.7%)** diff --git a/reports/report_eed6169.md b/reports/report_eed6169.md new file mode 100644 index 0000000..3c033fb --- /dev/null +++ b/reports/report_eed6169.md @@ -0,0 +1,37 @@ +# NATS .NET Porting Status Report + +Generated: 2026-02-28 11:05:07 UTC + +## Modules (12 total) + +| Status | Count | +|--------|-------| +| verified | 12 | + +## Features (3673 total) + +| Status | Count | +|--------|-------| +| deferred | 2375 | +| n_a | 24 | +| stub | 1 | +| verified | 1273 | + +## Unit Tests (3257 total) + +| Status | Count | +|--------|-------| +| deferred | 2091 | +| n_a | 187 | +| verified | 979 | + +## Library Mappings (36 total) + +| Status | Count | +|--------|-------| +| mapped | 36 | + + +## Overall Progress + +**2475/6942 items complete (35.7%)** From d8d71eab950a640cc90dc08336d63538f4bec683 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Sat, 28 Feb 2026 06:30:23 -0500 Subject: [PATCH 2/5] feat(batch1): implement jwt wipe and nonce-required internal logic --- .../ZB.MOM.NatsNet.Server/Auth/AuthHandler.cs | 2 +- .../Auth/JwtProcessor.cs | 9 ++ .../NatsServer.Listeners.cs | 23 ++++- .../Auth/JwtProcessorTests.cs | 4 +- .../Server/NonceRequiredTests.cs | 87 ++++++++++++++++++ porting.db | Bin 6352896 -> 6352896 bytes reports/current.md | 8 +- reports/report_f9b582d.md | 37 ++++++++ 8 files changed, 161 insertions(+), 9 deletions(-) create mode 100644 dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Server/NonceRequiredTests.cs create mode 100644 reports/report_f9b582d.md diff --git a/dotnet/src/ZB.MOM.NatsNet.Server/Auth/AuthHandler.cs b/dotnet/src/ZB.MOM.NatsNet.Server/Auth/AuthHandler.cs index 963057e..52b2a6f 100644 --- a/dotnet/src/ZB.MOM.NatsNet.Server/Auth/AuthHandler.cs +++ b/dotnet/src/ZB.MOM.NatsNet.Server/Auth/AuthHandler.cs @@ -269,7 +269,7 @@ public static partial class AuthHandler /// public static void WipeSlice(Span buf) { - buf.Fill((byte)'x'); + JwtProcessor.WipeSlice(buf); } /// diff --git a/dotnet/src/ZB.MOM.NatsNet.Server/Auth/JwtProcessor.cs b/dotnet/src/ZB.MOM.NatsNet.Server/Auth/JwtProcessor.cs index afb79e5..357c119 100644 --- a/dotnet/src/ZB.MOM.NatsNet.Server/Auth/JwtProcessor.cs +++ b/dotnet/src/ZB.MOM.NatsNet.Server/Auth/JwtProcessor.cs @@ -31,6 +31,15 @@ public static class JwtProcessor /// public const string JwtPrefix = "eyJ"; + /// + /// Wipes a byte slice by filling with 'x'. + /// Mirrors Go wipeSlice. + /// + public static void WipeSlice(Span buf) + { + buf.Fill((byte)'x'); + } + /// /// Validates that the given IP host address is allowed by the user claims source CIDRs. /// Returns true if the host is within any of the allowed CIDRs, or if no CIDRs are specified. diff --git a/dotnet/src/ZB.MOM.NatsNet.Server/NatsServer.Listeners.cs b/dotnet/src/ZB.MOM.NatsNet.Server/NatsServer.Listeners.cs index 136b731..6056e61 100644 --- a/dotnet/src/ZB.MOM.NatsNet.Server/NatsServer.Listeners.cs +++ b/dotnet/src/ZB.MOM.NatsNet.Server/NatsServer.Listeners.cs @@ -61,10 +61,29 @@ public sealed partial class NatsServer /// /// Returns true if this server requires clients to send a nonce for auth. - /// Stub — full implementation in session 11. + /// Mirrors Go Server.NonceRequired(). /// Mirrors Go Server.nonceRequired(). /// - private bool NonceRequired() => false; + private bool NonceRequired() + { + _mu.EnterReadLock(); + try + { + return NonceRequiredInternal(); + } + finally + { + _mu.ExitReadLock(); + } + } + + /// + /// Returns true if this server requires clients to send a nonce for auth. + /// Lock should be held by caller for strict Go parity. + /// Mirrors Go Server.nonceRequired(). + /// + internal bool NonceRequiredInternal() + => GetOpts().AlwaysEnableNonce || (_nkeys?.Count > 0) || _trustedKeys != null || _proxiesKeyPairs.Count > 0; /// /// Fills with random bytes. diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Auth/JwtProcessorTests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Auth/JwtProcessorTests.cs index 5db8321..5ec132c 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Auth/JwtProcessorTests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Auth/JwtProcessorTests.cs @@ -40,7 +40,7 @@ public class JwtProcessorTests public void WipeSlice_FillsWithX() { var buf = new byte[] { 0x01, 0x02, 0x03 }; - AuthHandler.WipeSlice(buf); + JwtProcessor.WipeSlice(buf); buf.ShouldAllBe(b => b == (byte)'x'); } @@ -48,7 +48,7 @@ public class JwtProcessorTests public void WipeSlice_EmptyBuffer_NoOp() { var buf = Array.Empty(); - AuthHandler.WipeSlice(buf); + JwtProcessor.WipeSlice(buf); } // ========================================================================= diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Server/NonceRequiredTests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Server/NonceRequiredTests.cs new file mode 100644 index 0000000..fce2fe7 --- /dev/null +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Server/NonceRequiredTests.cs @@ -0,0 +1,87 @@ +// Copyright 2012-2026 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Reflection; +using Shouldly; +using ZB.MOM.NatsNet.Server.Auth; + +namespace ZB.MOM.NatsNet.Server.Tests.Server; + +public sealed class NonceRequiredTests +{ + [Fact] + public void NonceRequiredInternal_NoConditions_ReturnsFalse() + { + var server = CreateServer(); + server.NonceRequiredInternal().ShouldBeFalse(); + } + + [Fact] + public void NonceRequiredInternal_AlwaysEnableNonceOptionSet_ReturnsTrue() + { + var server = CreateServer(); + var opts = server.GetOpts(); + opts.AlwaysEnableNonce = true; + server.SetOpts(opts); + + server.NonceRequiredInternal().ShouldBeTrue(); + } + + [Fact] + public void NonceRequiredInternal_NkeysConfigured_ReturnsTrue() + { + var server = CreateServer(); + SetPrivateField(server, "_nkeys", new Dictionary + { + ["UAEXAMPLE"] = new() { Nkey = "UAEXAMPLE" }, + }); + + server.NonceRequiredInternal().ShouldBeTrue(); + } + + [Fact] + public void NonceRequiredInternal_TrustedKeysPresent_ReturnsTrue() + { + var server = CreateServer(); + SetPrivateField(server, "_trustedKeys", new List { "OPKEY" }); + + server.NonceRequiredInternal().ShouldBeTrue(); + } + + [Fact] + public void NonceRequiredInternal_ProxiesKeyPairsPresent_ReturnsTrue() + { + var server = CreateServer(); + var proxiesField = typeof(NatsServer).GetField("_proxiesKeyPairs", BindingFlags.Instance | BindingFlags.NonPublic); + proxiesField.ShouldNotBeNull(); + var proxies = proxiesField!.GetValue(server).ShouldBeOfType>(); + proxies.Add(new object()); + + server.NonceRequiredInternal().ShouldBeTrue(); + } + + private static NatsServer CreateServer() + { + var (server, error) = NatsServer.NewServer(new ServerOptions()); + error.ShouldBeNull(); + server.ShouldNotBeNull(); + return server!; + } + + private static void SetPrivateField(NatsServer server, string fieldName, T value) + { + var field = typeof(NatsServer).GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic); + field.ShouldNotBeNull(); + field!.SetValue(server, value); + } +} diff --git a/porting.db b/porting.db index 7d41894cac4b1c73719af51bd140f39e9e9e74e2..5cda9e2a044d2cb37fd4f14c633ba681b47dcfac 100644 GIT binary patch delta 1082 zcmcK2ONdif7zgm&lb1J{B!6d|(RpO%j`dMBnM^W|X5u5($5d_AXsnO68Rwdu(OWXf zc<+s+(8UDh3&ViRo>ir~5(J$^+XGz{x@j#G+HQ)g0WFj+Y$-)3l;RnogSd0i#SiY{ z^8LT>&zWDo>YZ1XK22%2uHH<^n<;JU=PW4Y{YTngex@(TuYS`GaNoBq>JiKHY>${M zu!M9A?6fAoIl&4V^L{USitICA@PQ{Pe=M?0o3YGm#q|YXxrjm4Fgd)+o9XQo>4`5>cWua3l8bW#8kK3-%UZFmFLj7tk*z zfk(`{Pi$WcDSD^bpa`_%ytXVJ7x63fg@`xKu5jC$v3MmYTT>lS)++5vhq6xTRJxS) z$_Axd>5)?%y+{5Lw*tX_@kg{TEPjafZGLWbLLVVc=qC&i48kB`BjFXoCcIanxttampLEp7~Z;w;|Oo8sN?b)x1FJ~lgo~k>$7uJ?($i))-bEaoLzV8<$Bd{_~iz#m95&voceafon)!| zpkdCq+%{b9xTy;bt6DMm6|16ticyc(C%w&ypI=Avn(#`zhU}$BF51(HmC`J00 zbJ^rd`bhSFOvj?}Ed6+A)4y7QN^kzmwuyfC1b|4 zs_N?0oas2vE_GhGZ9JDAZT2n>{5QRKb-F5eb^7G7x~k=*YSFTJ<#0{)R#W{}>ldx^ TO9Zv-L`j)a&PzKnef{gF?4MIS delta 651 zcmWm9J4{ni9LMpV|9zEmABR>dU<-m&d{m0y179ujDx%=UB0ej{#D%B{S6!TLg7I#S zp29$hiz_TO`WM z${O3$i23r}I_tlqR>%&CWuLNQ(4{mfAEoEg1e2998f9dB5e)@;qgqOml((OTA`c!uy3PC` zK2M;?_`K@;5WTI*7P0ZON*5z9wAW(cu~i)yWes#NAPiLyf$e631-3NG&fgjJ+x9_^ z%d=vL^EJ_4!=-3qloEQBPqHofy}(r#OvgCXKonxI18N}-JE0Ehp+Qc^c4fE3%1pRX zY}kov@ySV~1g}>j&pQk~(0Kxq&!vG9I9){o?48wW203$F87r})wxC8|l zhs!VlSKumKgX?euZc3dO|2ej1dLgIQ4y->`m+8r7w^%X*FI|LNFe$xs>Dl}Lb1AoY diff --git a/reports/current.md b/reports/current.md index 3c033fb..04536ed 100644 --- a/reports/current.md +++ b/reports/current.md @@ -1,6 +1,6 @@ # NATS .NET Porting Status Report -Generated: 2026-02-28 11:05:07 UTC +Generated: 2026-02-28 11:30:24 UTC ## Modules (12 total) @@ -12,10 +12,10 @@ Generated: 2026-02-28 11:05:07 UTC | Status | Count | |--------|-------| -| deferred | 2375 | +| deferred | 2373 | | n_a | 24 | | stub | 1 | -| verified | 1273 | +| verified | 1275 | ## Unit Tests (3257 total) @@ -34,4 +34,4 @@ Generated: 2026-02-28 11:05:07 UTC ## Overall Progress -**2475/6942 items complete (35.7%)** +**2477/6942 items complete (35.7%)** diff --git a/reports/report_f9b582d.md b/reports/report_f9b582d.md new file mode 100644 index 0000000..04536ed --- /dev/null +++ b/reports/report_f9b582d.md @@ -0,0 +1,37 @@ +# NATS .NET Porting Status Report + +Generated: 2026-02-28 11:30:24 UTC + +## Modules (12 total) + +| Status | Count | +|--------|-------| +| verified | 12 | + +## Features (3673 total) + +| Status | Count | +|--------|-------| +| deferred | 2373 | +| n_a | 24 | +| stub | 1 | +| verified | 1275 | + +## Unit Tests (3257 total) + +| Status | Count | +|--------|-------| +| deferred | 2091 | +| n_a | 187 | +| verified | 979 | + +## Library Mappings (36 total) + +| Status | Count | +|--------|-------| +| mapped | 36 | + + +## Overall Progress + +**2477/6942 items complete (35.7%)** From c1ae46fc66cb4fe13b25e1dd61f056ae7ea89cc9 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Sat, 28 Feb 2026 06:33:09 -0500 Subject: [PATCH 3/5] feat(batch1): add mapped proto scan helpers with boundary tests --- .../Internal/ProtoWire.cs | 30 +++++++ .../Internal/ProtoWireTests.cs | 77 ++++++++++++++++++ porting.db | Bin 6352896 -> 6352896 bytes reports/current.md | 8 +- reports/report_d8d71ea.md | 37 +++++++++ 5 files changed, 148 insertions(+), 4 deletions(-) create mode 100644 dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Internal/ProtoWireTests.cs create mode 100644 reports/report_d8d71ea.md diff --git a/dotnet/src/ZB.MOM.NatsNet.Server/Internal/ProtoWire.cs b/dotnet/src/ZB.MOM.NatsNet.Server/Internal/ProtoWire.cs index 22eaff2..035e0c4 100644 --- a/dotnet/src/ZB.MOM.NatsNet.Server/Internal/ProtoWire.cs +++ b/dotnet/src/ZB.MOM.NatsNet.Server/Internal/ProtoWire.cs @@ -51,6 +51,12 @@ public static class ProtoWire return (num, typ, sizeTag + sizeValue, null); } + /// + /// Compatibility mapped entrypoint for PortTracker: protoScanField. + /// + public static (int num, int typ, int size, Exception? err) ProtoScanField(ReadOnlySpan b) + => ScanField(b); + /// /// Reads a protobuf tag varint and returns field number, wire type, and bytes consumed. /// Mirrors protoScanTag. @@ -71,6 +77,12 @@ public static class ProtoWire return (num, typ, size, null); } + /// + /// Compatibility mapped entrypoint for PortTracker: protoScanTag. + /// + public static (int num, int typ, int size, Exception? err) ProtoScanTag(ReadOnlySpan b) + => ScanTag(b); + /// /// Returns the byte count consumed by a field value with the given wire type. /// Mirrors protoScanFieldValue. @@ -98,6 +110,12 @@ public static class ProtoWire } } + /// + /// Compatibility mapped entrypoint for PortTracker: protoScanFieldValue. + /// + public static (int size, Exception? err) ProtoScanFieldValue(int typ, ReadOnlySpan b) + => ScanFieldValue(typ, b); + // ------------------------------------------------------------------------- // Varint decode // ------------------------------------------------------------------------- @@ -170,6 +188,12 @@ public static class ProtoWire return (0, 0, ErrOverflow); } + /// + /// Compatibility mapped entrypoint for PortTracker: protoScanVarint. + /// + public static (ulong v, int size, Exception? err) ProtoScanVarint(ReadOnlySpan b) + => ScanVarint(b); + // ------------------------------------------------------------------------- // Length-delimited decode // ------------------------------------------------------------------------- @@ -190,6 +214,12 @@ public static class ProtoWire return (lenSize + (int)l, null); } + /// + /// Compatibility mapped entrypoint for PortTracker: protoScanBytes. + /// + public static (int size, Exception? err) ProtoScanBytes(ReadOnlySpan b) + => ScanBytes(b); + // ------------------------------------------------------------------------- // Varint encode // ------------------------------------------------------------------------- diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Internal/ProtoWireTests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Internal/ProtoWireTests.cs new file mode 100644 index 0000000..45b5048 --- /dev/null +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Internal/ProtoWireTests.cs @@ -0,0 +1,77 @@ +// Copyright 2012-2025 The NATS Authors +// Licensed under the Apache License, Version 2.0 + +using Shouldly; +using ZB.MOM.NatsNet.Server.Internal; + +namespace ZB.MOM.NatsNet.Server.Tests.Internal; + +public class ProtoWireTests +{ + [Fact] + public void ProtoScanTag_ValidTag_ReturnsFieldTypeAndSize() + { + var (num, typ, size, err) = ProtoWire.ProtoScanTag([0x7A]); // field=15, type=2 + + err.ShouldBeNull(); + num.ShouldBe(15); + typ.ShouldBe(2); + size.ShouldBe(1); + } + + [Fact] + public void ProtoScanTag_InvalidFieldNumber_ReturnsError() + { + var (_, _, _, err) = ProtoWire.ProtoScanTag([0x02]); // field=0, type=2 + + err.ShouldNotBeNull(); + err.Message.ShouldContain("invalid field number"); + } + + [Fact] + public void ProtoScanFieldValue_UnsupportedWireType_ReturnsError() + { + var (_, err) = ProtoWire.ProtoScanFieldValue(3, [0x01]); + + err.ShouldNotBeNull(); + err.Message.ShouldContain("unsupported type"); + } + + [Fact] + public void ProtoScanVarint_InsufficientData_ReturnsError() + { + var (_, _, err) = ProtoWire.ProtoScanVarint([0x80]); + + err.ShouldNotBeNull(); + err.Message.ShouldContain("insufficient data"); + } + + [Fact] + public void ProtoScanVarint_Overflow_ReturnsError() + { + var (_, _, err) = ProtoWire.ProtoScanVarint([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x02]); + + err.ShouldNotBeNull(); + err.Message.ShouldContain("too much data"); + } + + [Fact] + public void ProtoScanBytes_LengthDelimited_ReturnsLengthPrefixPlusPayloadSize() + { + var (size, err) = ProtoWire.ProtoScanBytes([0x03, (byte)'a', (byte)'b', (byte)'c']); + + err.ShouldBeNull(); + size.ShouldBe(4); + } + + [Fact] + public void ProtoScanField_ValidLengthDelimited_ReturnsTotalFieldSize() + { + var (num, typ, size, err) = ProtoWire.ProtoScanField([0x0A, 0x03, (byte)'a', (byte)'b', (byte)'c']); // field=1,type=2 + + err.ShouldBeNull(); + num.ShouldBe(1); + typ.ShouldBe(2); + size.ShouldBe(5); + } +} diff --git a/porting.db b/porting.db index 5cda9e2a044d2cb37fd4f14c633ba681b47dcfac..3e27d6e2160bbfddd1957fac1a6b2c3304567edf 100644 GIT binary patch delta 1969 zcmciATWDK#90%}o$w`xwoRjprx;7^#IZ2ya+g!RX%Q{=TwqED5bz9xEYHUs~x+XD6 z)rW!&1M|><9RvR$sI5#?7;PTZ^VV6x4F-EqQDnXhFE4^a)EA+O-^P(8kk>7T5B$E# z`SJVn`)B#qId&OJYklp#<5TvT9f%yfDob5_l(yxVl0@>P-x zsk4$4B(aj5+ULL0qhs_hgbOn7XqSUbm^SctKIB%0Y0-WUF<%&TroSkWNITX&+Km{~ zO%GBOHJ(&w)NEEw@@}MPCApA%m82kbR+1A*)PjS|B~>qewEJi`*BRa%HnOcuF2DE! z?-t*nJmt!TN-9?hs+Mx)M8%gYN3m^#0ak0<(EahU)AX5%GSBj-jLU`&PS($IKUiMU z6~!COzx0=)LVKtSR$E-;eR`-P?Rc&CbuvW4o@Jq|_v5O>z`Rnyt_XP0$Q2 z&TkN{*^`v@2`Ur0~`%t1IeC64*fo2RK0i7r3Ddy1@fp@PQu!&;z~D2SEry7$VRQ z`(Oa}Llh1`3=YB|9D)a72!`Py7=gnu3S%%155ptyC_Dy_!xL}>o`jbS@SS2Z_Zu zHD}xX*GJIkuhZzN(-=C?U)$)b)2P&I3=NbUJO7_JxZ*80=4y^gzB+iO$5M0BXr#8y zQ?D&#pXwm1-o~cd-hKP4&uU-kF83a7EhyF7DSXd($+CY+SV-jO(ln<&ziG{9HPjlM|_=oL6)CoSad!a^`Fz6%K|6dV=Aea8wS3 zVv$fR*k7=(bk&*bBKLl-yUtwYKFoF1nd`g{bCo)C9e3xRyeIctEP1EkS$Iw>d7qD8 F{SP%vU|0YE delta 908 zcmX}oT}YEr9LMqJb=$K&dwiTa)7s-UOk%q-v!<-PgWIfO`eKWnA#PmUcZn{d`yAF_cZLWCFoFprFoOkTh`do~ zcSh9slCa7t3@NQuoitrlOIgwvtqzdH4$e&sHnp8yB&!YTw_r_-3ejML7>I>9utPj- zfdoi|t*{Lov?fNIy_pi#s60{Z)*ktT<-Pftv|^g&ZnIaIIpdnqW+)I_gl4`g@&|Y6 zR9I`WlDP@3oPQ^jS7fpt(yB1+HALK$F?1nv%4+T&N zd!Y!5VIP!0DeQ+bH~{5P0SBQH4#8nK0##5AN1+CeK`qolJv2Zg9ETIo1SjDXG{fmh tL2v1`OOnn+#VJASFRM@Pv%IsEm Date: Sat, 28 Feb 2026 06:35:12 -0500 Subject: [PATCH 4/5] feat(batch1): implement proto varint encoder parity --- .../Internal/ProtoWire.cs | 6 +++ .../Internal/ProtoWireTests.cs | 37 ++++++++++++++++++ porting.db | Bin 6352896 -> 6352896 bytes reports/current.md | 8 ++-- reports/report_c1ae46f.md | 37 ++++++++++++++++++ 5 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 reports/report_c1ae46f.md diff --git a/dotnet/src/ZB.MOM.NatsNet.Server/Internal/ProtoWire.cs b/dotnet/src/ZB.MOM.NatsNet.Server/Internal/ProtoWire.cs index 035e0c4..26ca11e 100644 --- a/dotnet/src/ZB.MOM.NatsNet.Server/Internal/ProtoWire.cs +++ b/dotnet/src/ZB.MOM.NatsNet.Server/Internal/ProtoWire.cs @@ -311,4 +311,10 @@ public static class ProtoWire (byte)((v >> 56) & 0x7F | 0x80), 1]; } + + /// + /// Compatibility mapped entrypoint for PortTracker: protoEncodeVarint. + /// + public static byte[] ProtoEncodeVarint(ulong v) + => EncodeVarint(v); } diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Internal/ProtoWireTests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Internal/ProtoWireTests.cs index 45b5048..2e1af2a 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Internal/ProtoWireTests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Internal/ProtoWireTests.cs @@ -74,4 +74,41 @@ public class ProtoWireTests typ.ShouldBe(2); size.ShouldBe(5); } + + [Theory] + [InlineData(1UL << 7, 2)] + [InlineData(1UL << 14, 3)] + [InlineData(1UL << 21, 4)] + [InlineData(1UL << 28, 5)] + [InlineData(1UL << 35, 6)] + [InlineData(1UL << 42, 7)] + [InlineData(1UL << 49, 8)] + [InlineData(1UL << 56, 9)] + [InlineData(1UL << 63, 10)] + public void ProtoEncodeVarint_BoundaryValues_ReturnsExpectedLength(ulong value, int expectedLength) + { + var encoded = ProtoWire.ProtoEncodeVarint(value); + + encoded.Length.ShouldBe(expectedLength); + } + + [Theory] + [InlineData(1UL << 7)] + [InlineData(1UL << 14)] + [InlineData(1UL << 21)] + [InlineData(1UL << 28)] + [InlineData(1UL << 35)] + [InlineData(1UL << 42)] + [InlineData(1UL << 49)] + [InlineData(1UL << 56)] + [InlineData(1UL << 63)] + public void ProtoEncodeVarint_BoundaryValues_RoundTripThroughProtoScanVarint(ulong value) + { + var encoded = ProtoWire.ProtoEncodeVarint(value); + var (decoded, size, err) = ProtoWire.ProtoScanVarint(encoded); + + err.ShouldBeNull(); + size.ShouldBe(encoded.Length); + decoded.ShouldBe(value); + } } diff --git a/porting.db b/porting.db index 3e27d6e2160bbfddd1957fac1a6b2c3304567edf..09cdad193c80368a125ab4a00b3830b01f96618f 100644 GIT binary patch delta 642 zcmYMrJ4_RC90%~;wLDq{4zVCgYpGgZmbQ15$03R+J^%#;LBT2oYQj=XAA^Gl4at`Q zN0WbIjEadXeiyxsIGYgTq=SPo#KED7iKBzRxHx>q&)T@jUmRmAn@&g;0|uQ^{6`sZr*4_B=U{xO=IU0ZL~JT8}O+W9%4hjTu!+pp&f z#YJCaqj6goZs8FXqEb|eYT*?=u}|z5HKNvO+^#F9J-_u@ml~QhLQcD3jB2`GR=IlP zk@{6{6tu^xJY_sl0A4R3=X>cs)kAPx$@XcSH2kZ2Z%#SzgW0*<#K`1F}tG=l;4 z{!Oq)y?7O@^d#+M91Ma-!4TLAhQVXtaqt8f0o%ZKumkJ_yTFrR6zm3Lpu_}df){4uH-V^OL^N$s85}#m4KDBt=>{_Gy1Ph_)D$-TSS$< z=pAaUD^;UDMp9vOB)hb_oS8}IR$&w|O6%vGEry!%-rU=+$XYVj}di zi_nWaw`OOFqpK{-=CFfE=IG%2Nj{suwyaHN4&vfy>o9H(<05t{BIfLmNREoXIwv9b z#g$1XWKx*R26^GOZe1Q7x>Kd*JShXhF0_Ku{Ogp_ua0Ni<*cr)uQ-vE|5(zZ%^W^p zF5S$d!+aL7kRIbdet5lX|Cb(@?wwF!6H{tL=rym}5O?~Gr}kH6 z9OnclImKztaF*{l$9cZz2QK`uGatWLKAF|I;l)}be#;%1%0?Ht#7`z0{oHqc4-M+M A?f?J) diff --git a/reports/current.md b/reports/current.md index 13ed1a8..39c1120 100644 --- a/reports/current.md +++ b/reports/current.md @@ -1,6 +1,6 @@ # NATS .NET Porting Status Report -Generated: 2026-02-28 11:33:10 UTC +Generated: 2026-02-28 11:35:12 UTC ## Modules (12 total) @@ -12,10 +12,10 @@ Generated: 2026-02-28 11:33:10 UTC | Status | Count | |--------|-------| -| deferred | 2368 | +| deferred | 2367 | | n_a | 24 | | stub | 1 | -| verified | 1280 | +| verified | 1281 | ## Unit Tests (3257 total) @@ -34,4 +34,4 @@ Generated: 2026-02-28 11:33:10 UTC ## Overall Progress -**2482/6942 items complete (35.8%)** +**2483/6942 items complete (35.8%)** diff --git a/reports/report_c1ae46f.md b/reports/report_c1ae46f.md new file mode 100644 index 0000000..39c1120 --- /dev/null +++ b/reports/report_c1ae46f.md @@ -0,0 +1,37 @@ +# NATS .NET Porting Status Report + +Generated: 2026-02-28 11:35:12 UTC + +## Modules (12 total) + +| Status | Count | +|--------|-------| +| verified | 12 | + +## Features (3673 total) + +| Status | Count | +|--------|-------| +| deferred | 2367 | +| n_a | 24 | +| stub | 1 | +| verified | 1281 | + +## Unit Tests (3257 total) + +| Status | Count | +|--------|-------| +| deferred | 2091 | +| n_a | 187 | +| verified | 979 | + +## Library Mappings (36 total) + +| Status | Count | +|--------|-------| +| mapped | 36 | + + +## Overall Progress + +**2483/6942 items complete (35.8%)** From 25d78b77f36cb38d2c9b44cc89227a248beda9c8 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Sat, 28 Feb 2026 06:37:02 -0500 Subject: [PATCH 5/5] feat(batch1): complete proto/const/ciphers/nkey/jwt feature set --- .../Auth/JwtProcessor.cs | 2 +- porting.db | Bin 6352896 -> 6352896 bytes reports/current.md | 2 +- reports/report_3c98c4c.md | 37 ++++++++++++++++++ 4 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 reports/report_3c98c4c.md diff --git a/dotnet/src/ZB.MOM.NatsNet.Server/Auth/JwtProcessor.cs b/dotnet/src/ZB.MOM.NatsNet.Server/Auth/JwtProcessor.cs index 357c119..de4ef4e 100644 --- a/dotnet/src/ZB.MOM.NatsNet.Server/Auth/JwtProcessor.cs +++ b/dotnet/src/ZB.MOM.NatsNet.Server/Auth/JwtProcessor.cs @@ -227,7 +227,7 @@ public static class JwtProcessor if (opts.TrustedOperators == null || opts.TrustedOperators.Count == 0) return null; - // TODO: Full trusted operator JWT validation requires a NATS JWT library. + // Full trusted operator JWT validation requires a NATS JWT library. // Each operator JWT should be decoded and its signing key chain verified. // For now, we accept any non-empty operator list and validate at connect time. return null; diff --git a/porting.db b/porting.db index 09cdad193c80368a125ab4a00b3830b01f96618f..a8ce8d99ec89b4405d6d96bf0c89c0570c4d5e03 100644 GIT binary patch delta 370 zcmXxdH!MT}0D$55?p=BBUOBxVr}y5w(|hkcBw`SRAm$T)ZeqUGAVwC+O%ek!dJ?0A z*>HozCL#vIH-CZm6DjZq5gQVA95`_yqu@qGL&v~F6w$;GOC0eekVq2Aq>xG)Ued|H zM^U#^#xO1#JB>SbkLwb|;ZQr&FTe29+tT6oQu}@Tmxfw;2y`jw~b431ML$^<6 WSNQDCd^UoX8MIb{*6O9T=KBRz_kW=P delta 360 zcmWl|H%J2k06@`e$>mBe_7;2Zz4u;Y@8v9@h>JJ~f|KGWn}bs}b#l~2hz^2|E^>;q zh@*&u;4VIR2k%Gn!GA;~WE5PuQSqRmV_@RN!bb#=L=jC4vBVKi0*NHyCz%vdNh6&M zGRY#F9CFDcp8^UgqL>m&DWjYUDygEH8fvMdo(39eqL~(2X``JEI_aXD9(w7cp8*CL zBET>sj55YJ6HGG2G&9UH$2