Files
natsdotnet/gaps/tls-security.md
2026-02-25 15:12:52 -05:00

341 lines
30 KiB
Markdown

# TLS / Security — Gap Analysis
> This file tracks what has and hasn't been ported from Go to .NET for the **TLS / Security** module.
> See [stillmissing.md](stillmissing.md) for the full LOC comparison across all modules.
## LLM Instructions: How to Analyze This Category
### Step 1: Read the Go Reference Files
Read each Go source file listed below. For every file:
1. Extract all **exported types** (structs, interfaces, type aliases)
2. Extract all **exported methods** on those types (receiver functions)
3. Extract all **exported standalone functions**
4. Note **key constants, enums, and protocol states**
5. Note **important unexported helpers** that implement core logic (functions >20 lines)
6. Pay attention to **concurrency patterns** (goroutines, mutexes, channels) — these map to different .NET patterns
### Step 2: Read the .NET Implementation Files
Read all `.cs` files in the .NET directories listed below. For each Go symbol found in Step 1:
1. Search for a matching type, method, or function in .NET
2. If found, compare the behavior: does it handle the same edge cases? Same error paths?
3. If partially implemented, note what's missing
4. If not found, note it as MISSING
### Step 3: Cross-Reference Tests
Compare Go test functions against .NET test methods:
1. For each Go `Test*` function, check if a corresponding .NET `[Fact]` or `[Theory]` exists
2. Note which test scenarios are covered and which are missing
3. Check the parity DB (`docs/test_parity.db`) for existing mappings:
```bash
sqlite3 docs/test_parity.db "SELECT go_test, dotnet_test, confidence FROM test_mappings tm JOIN go_tests gt ON tm.go_test_id=gt.rowid JOIN dotnet_tests dt ON tm.dotnet_test_id=dt.rowid WHERE gt.go_file LIKE '%PATTERN%'"
```
### Step 4: Classify Each Item
Use these status values:
| Status | Meaning |
|--------|---------|
| **PORTED** | Equivalent exists in .NET with matching behavior |
| **PARTIAL** | .NET implementation exists but is incomplete (missing edge cases, error handling, or features) |
| **MISSING** | No .NET equivalent found — needs to be ported |
| **NOT_APPLICABLE** | Go-specific pattern that doesn't apply to .NET (build tags, platform-specific goroutine tricks, etc.) |
| **DEFERRED** | Intentionally skipped for now (document why) |
### Step 5: Fill In the Gap Inventory
Add rows to the Gap Inventory table below. Group by Go source file. Include the Go file and line number so a porting LLM can jump directly to the reference implementation.
### Key Porting Notes for TLS / Security
- This category is at **12% LOC parity** — one of the largest gaps.
- OCSP stapling and peer validation are critical for mutual TLS deployments.
- Cipher suite filtering allows operators to restrict which TLS ciphers are accepted.
- PROXY protocol support enables running NATS behind HAProxy/nginx with real client IPs.
- `certstore` provides OS certificate store access (Windows-specific parts may be NOT_APPLICABLE).
- `certidp` implements certificate identity validation via OCSP responders.
---
## Go Reference Files (Source)
- `golang/nats-server/server/ocsp.go` — OCSP stapling configuration
- `golang/nats-server/server/ocsp_peer.go` — OCSP peer certificate validation
- `golang/nats-server/server/ocsp_responsecache.go` — OCSP response caching
- `golang/nats-server/server/ciphersuites.go` — TLS cipher suite management and filtering
- `golang/nats-server/server/client_proxyproto.go` — PROXY protocol v1/v2 support (HAProxy)
- `golang/nats-server/server/certidp/certidp.go` — Certificate identity provider
- `golang/nats-server/server/certidp/messages.go` — CertIDP message types
- `golang/nats-server/server/certidp/ocsp_responder.go` — OCSP responder client
- `golang/nats-server/server/certstore/certstore.go` — OS certificate store access
- `golang/nats-server/server/certstore/certstore_other.go` — Non-Windows cert store
- `golang/nats-server/server/certstore/certstore_windows.go` — Windows cert store
- `golang/nats-server/server/certstore/errors.go` — Cert store errors
## Go Reference Files (Tests)
- `golang/nats-server/test/ocsp_test.go` (integration)
- `golang/nats-server/test/ocsp_peer_test.go` (integration)
- `golang/nats-server/test/tls_test.go` (integration)
- `golang/nats-server/server/certstore/certstore_windows_test.go`
- `golang/nats-server/server/certidp/*_test.go`
## .NET Implementation Files (Source)
- `src/NATS.Server/Tls/TlsHelper.cs`
- `src/NATS.Server/Tls/TlsCertificateProvider.cs`
- `src/NATS.Server/Tls/TlsConnectionState.cs`
- `src/NATS.Server/Tls/TlsConnectionWrapper.cs`
- `src/NATS.Server/Tls/TlsRateLimiter.cs`
- `src/NATS.Server/Tls/PeekableStream.cs`
- `src/NATS.Server/Tls/OcspConfig.cs`
## .NET Implementation Files (Tests)
- `tests/NATS.Server.Tests/` (TLS-related test files in root)
---
## Gap Inventory
<!-- After analysis, fill in this table. Group rows by Go source file. -->
### ocsp.go — OCSP stapling configuration
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|-----------|:-------------|--------|:----------------|-------|
| OCSPMode (type) | golang/nats-server/server/ocsp.go:46 | PORTED | src/NATS.Server/Tls/OcspConfig.cs:8 | Mapped to `OcspMode` enum with matching values |
| OCSPModeAuto | golang/nats-server/server/ocsp.go:49 | PORTED | src/NATS.Server/Tls/OcspConfig.cs:10 | `OcspMode.Auto = 0` |
| OCSPModeAlways | golang/nats-server/server/ocsp.go:52 | PORTED | src/NATS.Server/Tls/OcspConfig.cs:11 | `OcspMode.Always = 1` |
| OCSPModeNever | golang/nats-server/server/ocsp.go:55 | PORTED | src/NATS.Server/Tls/OcspConfig.cs:13 | `OcspMode.Never = 3` |
| OCSPModeMust | golang/nats-server/server/ocsp.go:60 | PORTED | src/NATS.Server/Tls/OcspConfig.cs:12 | `OcspMode.Must = 2` |
| OCSPMonitor (struct) | golang/nats-server/server/ocsp.go:65 | MISSING | — | No dedicated OCSP monitor struct; .NET delegates to SslStreamCertificateContext for stapling |
| OCSPMonitor.getNextRun | golang/nats-server/server/ocsp.go:80 | MISSING | — | No periodic OCSP refresh loop |
| OCSPMonitor.getStatus | golang/nats-server/server/ocsp.go:102 | MISSING | — | No cache/local/remote OCSP status retrieval |
| OCSPMonitor.getCacheStatus | golang/nats-server/server/ocsp.go:119 | MISSING | — | No in-memory OCSP response caching |
| OCSPMonitor.getLocalStatus | golang/nats-server/server/ocsp.go:125 | MISSING | — | No file-based OCSP status persistence |
| OCSPMonitor.getRemoteStatus | golang/nats-server/server/ocsp.go:160 | MISSING | — | No manual OCSP responder HTTP callout; .NET relies on runtime OCSP |
| OCSPMonitor.run | golang/nats-server/server/ocsp.go:272 | MISSING | — | No background goroutine for OCSP staple renewal |
| OCSPMonitor.stop | golang/nats-server/server/ocsp.go:348 | MISSING | — | No explicit stop channel for OCSP monitor |
| Server.NewOCSPMonitor | golang/nats-server/server/ocsp.go:357 | PARTIAL | src/NATS.Server/Tls/TlsHelper.cs:75 | `BuildCertificateContext` delegates stapling to .NET runtime; missing per-kind config, VerifyConnection callbacks, shutdown-on-revoke |
| Server.setupOCSPStapleStoreDir | golang/nats-server/server/ocsp.go:560 | MISSING | — | No OCSP store directory setup |
| tlsConfigKind (struct) | golang/nats-server/server/ocsp.go:577 | MISSING | — | No per-listener TLS config kind abstraction |
| Server.configureOCSP | golang/nats-server/server/ocsp.go:585 | MISSING | — | No multi-listener OCSP configuration loop |
| Server.enableOCSP | golang/nats-server/server/ocsp.go:680 | MISSING | — | No server-level OCSP enablement orchestrator |
| Server.startOCSPMonitoring | golang/nats-server/server/ocsp.go:717 | MISSING | — | No OCSP monitor goroutine dispatcher |
| Server.reloadOCSP | golang/nats-server/server/ocsp.go:734 | MISSING | — | No OCSP hot-reload support |
| hasOCSPStatusRequest | golang/nats-server/server/ocsp.go:804 | MISSING | — | No MustStaple TLS extension detection |
| OCSPMonitor.writeOCSPStatus | golang/nats-server/server/ocsp.go:840 | MISSING | — | No atomic file write for OCSP status persistence |
| parseCertPEM | golang/nats-server/server/ocsp.go:867 | PARTIAL | src/NATS.Server/Tls/TlsHelper.cs:17 | `LoadCaCertificates` uses `ImportFromPemFile` but does not validate PEM block type |
| getOCSPIssuerLocally | golang/nats-server/server/ocsp.go:892 | MISSING | — | No local issuer resolution from cert bundle |
| getOCSPIssuer | golang/nats-server/server/ocsp.go:932 | MISSING | — | No issuer resolution logic |
| ocspStatusString | golang/nats-server/server/ocsp.go:968 | PORTED | src/NATS.Server/Events/EventTypes.cs:647 | `OcspEventBuilder.ParseStatus` and `OcspStatus` enum |
| validOCSPResponse | golang/nats-server/server/ocsp.go:979 | MISSING | — | No manual OCSP response time validation |
### ocsp_peer.go — OCSP peer certificate validation
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|-----------|:-------------|--------|:----------------|-------|
| parseOCSPPeer | golang/nats-server/server/ocsp_peer.go:29 | MISSING | — | No config-file parsing for OCSP peer options |
| peerFromVerifiedChains | golang/nats-server/server/ocsp_peer.go:130 | MISSING | — | No peer extraction from verified chains |
| Server.plugTLSOCSPPeer | golang/nats-server/server/ocsp_peer.go:138 | PARTIAL | src/NATS.Server/Tls/TlsHelper.cs:36 | .NET uses X509RevocationMode.Online when OcspPeerVerify set; missing full OCSP peer plugin pattern with per-chain validation |
| Server.plugClientTLSOCSPPeer | golang/nats-server/server/ocsp_peer.go:163 | PARTIAL | src/NATS.Server/Tls/TlsHelper.cs:41 | RemoteCertificateValidationCallback with revocation check, but no OCSP-specific chain walking or event publishing |
| Server.plugServerTLSOCSPPeer | golang/nats-server/server/ocsp_peer.go:183 | MISSING | — | No leaf-spoke server-side OCSP peer plug |
| Server.tlsServerOCSPValid | golang/nats-server/server/ocsp_peer.go:209 | MISSING | — | No server-side verified-chain OCSP evaluation |
| Server.tlsClientOCSPValid | golang/nats-server/server/ocsp_peer.go:220 | MISSING | — | No client-side verified-chain OCSP evaluation |
| Server.peerOCSPValid | golang/nats-server/server/ocsp_peer.go:225 | MISSING | — | No multi-chain OCSP eligibility walker |
| Server.certOCSPGood | golang/nats-server/server/ocsp_peer.go:286 | MISSING | — | No per-link OCSP response fetch, cache check, delegation validation, and policy overrides (WarnOnly, AllowWhenCAUnreachable) |
| sendOCSPPeerRejectEvent | golang/nats-server/server/ocsp_peer.go (ref) | PARTIAL | src/NATS.Server/Events/EventTypes.cs:612 | `OcspEventBuilder.BuildPeerReject` exists as data builder but no actual event publishing to system account |
| sendOCSPPeerChainlinkInvalidEvent | golang/nats-server/server/ocsp_peer.go (ref) | PARTIAL | src/NATS.Server/Events/EventTypes.cs:628 | `OcspEventBuilder.BuildChainValidation` exists as data builder but no actual event publishing |
### ocsp_responsecache.go — OCSP response caching
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|-----------|:-------------|--------|:----------------|-------|
| OCSPResponseCacheType (type) | golang/nats-server/server/ocsp_responsecache.go:44 | MISSING | — | No cache type enum (NONE/LOCAL) |
| OCSPResponseCacheConfig | golang/nats-server/server/ocsp_responsecache.go:56 | MISSING | — | No OCSP cache configuration struct |
| NewOCSPResponseCacheConfig | golang/nats-server/server/ocsp_responsecache.go:63 | MISSING | — | No cache config factory |
| OCSPResponseCacheStats | golang/nats-server/server/ocsp_responsecache.go:72 | MISSING | — | No cache statistics tracking |
| OCSPResponseCacheItem | golang/nats-server/server/ocsp_responsecache.go:81 | MISSING | — | No cache item model |
| OCSPResponseCache (interface) | golang/nats-server/server/ocsp_responsecache.go:89 | MISSING | — | No cache interface (Put/Get/Delete/Start/Stop) |
| NoOpCache (struct) | golang/nats-server/server/ocsp_responsecache.go:102 | MISSING | — | No no-op cache implementation |
| LocalCache (struct) | golang/nats-server/server/ocsp_responsecache.go:155 | MISSING | — | No local file-backed OCSP response cache |
| LocalCache.Put | golang/nats-server/server/ocsp_responsecache.go:167 | MISSING | — | No cache insert with S2 compression |
| LocalCache.Get | golang/nats-server/server/ocsp_responsecache.go:201 | MISSING | — | No cache lookup with decompression |
| LocalCache.Delete | golang/nats-server/server/ocsp_responsecache.go:245 | MISSING | — | No cache delete with preserve-revoked policy |
| LocalCache.Start | golang/nats-server/server/ocsp_responsecache.go:272 | MISSING | — | No cache initialization from disk |
| LocalCache.Stop | golang/nats-server/server/ocsp_responsecache.go:281 | MISSING | — | No cache shutdown and save |
| LocalCache.Compress / Decompress | golang/nats-server/server/ocsp_responsecache.go:344 | MISSING | — | No S2 compression for OCSP responses |
| LocalCache.loadCache / saveCache | golang/nats-server/server/ocsp_responsecache.go:371 | MISSING | — | No JSON-based disk persistence |
| Server.initOCSPResponseCache | golang/nats-server/server/ocsp_responsecache.go:508 | MISSING | — | No cache initialization on server startup |
| Server.startOCSPResponseCache | golang/nats-server/server/ocsp_responsecache.go:546 | MISSING | — | No cache start on server startup |
| Server.stopOCSPResponseCache | golang/nats-server/server/ocsp_responsecache.go:564 | MISSING | — | No cache stop on server shutdown |
| parseOCSPResponseCache | golang/nats-server/server/ocsp_responsecache.go:574 | MISSING | — | No config-file parsing for OCSP cache options |
### ciphersuites.go — TLS cipher suite management and filtering
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|-----------|:-------------|--------|:----------------|-------|
| cipherMap (var) | golang/nats-server/server/ciphersuites.go:32 | MISSING | — | No cipher name-to-suite mapping; .NET handles cipher suites via SslServerAuthenticationOptions |
| cipherMapByID (var) | golang/nats-server/server/ciphersuites.go:33 | MISSING | — | No cipher ID-to-suite mapping |
| defaultCipherSuites | golang/nats-server/server/ciphersuites.go:35 | NOT_APPLICABLE | — | .NET TLS stack manages default cipher suites internally; no explicit enumeration needed |
| curvePreferenceMap (var) | golang/nats-server/server/ciphersuites.go:45 | NOT_APPLICABLE | — | .NET does not expose curve preference ordering at application level |
| defaultCurvePreferences | golang/nats-server/server/ciphersuites.go:55 | NOT_APPLICABLE | — | .NET runtime handles curve negotiation; FIPS mode handled by OS/.NET runtime config |
| init() cipher registration | golang/nats-server/server/ciphersuites.go:21 | NOT_APPLICABLE | — | Go-specific init pattern; .NET populates cipher lists differently |
### client_proxyproto.go — PROXY protocol v1/v2 support
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|-----------|:-------------|--------|:----------------|-------|
| proxyProtoV2 constants | golang/nats-server/server/client_proxyproto.go:28 | PORTED | src/NATS.Server/Protocol/ProxyProtocol.cs:54 | All v2 constants mirrored |
| proxyProtoV1 constants | golang/nats-server/server/client_proxyproto.go:63 | PORTED | src/NATS.Server/Protocol/ProxyProtocol.cs:82 | All v1 constants mirrored |
| proxyProtoAddr (struct) | golang/nats-server/server/client_proxyproto.go:81 | PORTED | src/NATS.Server/Protocol/ProxyProtocol.cs:11 | `ProxyAddress` class with SrcIp, SrcPort, DstIp, DstPort |
| proxyProtoAddr.String | golang/nats-server/server/client_proxyproto.go:89 | PORTED | src/NATS.Server/Protocol/ProxyProtocol.cs:20 | `ProxyAddress.ToString()` with IPv6 bracket formatting |
| proxyProtoAddr.Network | golang/nats-server/server/client_proxyproto.go:94 | PORTED | src/NATS.Server/Protocol/ProxyProtocol.cs:18 | `ProxyAddress.Network` property |
| proxyConn (struct) | golang/nats-server/server/client_proxyproto.go:102 | MISSING | — | No connection wrapper; .NET parser is pure buffer-based, integration with accept loop not yet wired |
| proxyConn.RemoteAddr | golang/nats-server/server/client_proxyproto.go:109 | MISSING | — | Part of proxyConn; accept loop integration needed |
| detectProxyProtoVersion | golang/nats-server/server/client_proxyproto.go:116 | PORTED | src/NATS.Server/Protocol/ProxyProtocol.cs:91 | `ProxyProtocolParser.Parse` auto-detects v1/v2 from first 6 bytes |
| readProxyProtoV1Header | golang/nats-server/server/client_proxyproto.go:134 | PORTED | src/NATS.Server/Protocol/ProxyProtocol.cs:117 | `ProxyProtocolParser.ParseV1` — buffer-based, all edge cases handled |
| readProxyProtoHeader | golang/nats-server/server/client_proxyproto.go:226 | PORTED | src/NATS.Server/Protocol/ProxyProtocol.cs:91 | `ProxyProtocolParser.Parse` covers both versions |
| readProxyProtoV2Header | golang/nats-server/server/client_proxyproto.go:274 | PORTED | src/NATS.Server/Protocol/ProxyProtocol.cs:190 | `ProxyProtocolParser.ParseV2` |
| parseProxyProtoV2Header | golang/nats-server/server/client_proxyproto.go:301 | PORTED | src/NATS.Server/Protocol/ProxyProtocol.cs:208 | `ProxyProtocolParser.ParseV2AfterSig` |
| parseIPv4Addr | golang/nats-server/server/client_proxyproto.go:365 | PORTED | src/NATS.Server/Protocol/ProxyProtocol.cs:250 | `ParseIPv4` private method |
| parseIPv6Addr | golang/nats-server/server/client_proxyproto.go:383 | PORTED | src/NATS.Server/Protocol/ProxyProtocol.cs:269 | `ParseIPv6` private method |
| error variables | golang/nats-server/server/client_proxyproto.go:73 | PORTED | src/NATS.Server/Protocol/ProxyProtocol.cs:353 | `ProxyProtocolException` and `ProxyProtocolUnsupportedException` |
### certidp/certidp.go — Certificate identity provider
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|-----------|:-------------|--------|:----------------|-------|
| DefaultAllowedClockSkew | golang/nats-server/server/certidp/certidp.go:30 | MISSING | — | No OCSP clock skew constant |
| DefaultOCSPResponderTimeout | golang/nats-server/server/certidp/certidp.go:31 | MISSING | — | No OCSP responder timeout constant |
| DefaultTTLUnsetNextUpdate | golang/nats-server/server/certidp/certidp.go:32 | MISSING | — | No default TTL when NextUpdate is unset |
| StatusAssertion (type) | golang/nats-server/server/certidp/certidp.go:35 | PARTIAL | src/NATS.Server/Events/EventTypes.cs:595 | `OcspStatus` enum exists (Good, Revoked, Unknown) but no JSON marshal/unmarshal or bidirectional maps |
| GetStatusAssertionStr | golang/nats-server/server/certidp/certidp.go:56 | PORTED | src/NATS.Server/Events/EventTypes.cs:647 | `OcspEventBuilder.ParseStatus` provides string-to-enum; reverse mapping implicit |
| ChainLink (struct) | golang/nats-server/server/certidp/certidp.go:93 | MISSING | — | No chain link struct with Leaf/Issuer/OCSPWebEndpoints |
| OCSPPeerConfig (struct) | golang/nats-server/server/certidp/certidp.go:100 | MISSING | — | No OCSP peer config struct (Verify, Timeout, ClockSkew, WarnOnly, UnknownIsGood, AllowWhenCAUnreachable, TTLUnsetNextUpdate) |
| NewOCSPPeerConfig | golang/nats-server/server/certidp/certidp.go:110 | MISSING | — | No peer config factory |
| Log (struct) | golang/nats-server/server/certidp/certidp.go:123 | NOT_APPLICABLE | — | .NET uses ILogger<T> injection; no need for function-pointer log struct |
| CertInfo (struct) | golang/nats-server/server/certidp/certidp.go:131 | MISSING | — | No cert info DTO for events |
| GenerateFingerprint | golang/nats-server/server/certidp/certidp.go:179 | PARTIAL | src/NATS.Server/Tls/TlsHelper.cs:88 | `GetCertificateHash` uses SHA256 on SPKI (not raw cert as Go does); different hash input |
| getWebEndpoints | golang/nats-server/server/certidp/certidp.go:184 | MISSING | — | No OCSP endpoint URL extraction/filtering |
| GetSubjectDNForm | golang/nats-server/server/certidp/certidp.go:203 | MISSING | — | No subject RDN sequence formatting |
| GetIssuerDNForm | golang/nats-server/server/certidp/certidp.go:212 | MISSING | — | No issuer RDN sequence formatting |
| CertOCSPEligible | golang/nats-server/server/certidp/certidp.go:221 | MISSING | — | No OCSP eligibility check based on AIA extension |
| GetLeafIssuerCert | golang/nats-server/server/certidp/certidp.go:237 | MISSING | — | No positional issuer extraction from chain |
| OCSPResponseCurrent | golang/nats-server/server/certidp/certidp.go:250 | MISSING | — | No OCSP response currency check with clock skew and TTL fallback |
| ValidDelegationCheck | golang/nats-server/server/certidp/certidp.go:288 | MISSING | — | No OCSP response delegation validation per RFC 6960 section 4.2.2.2 |
### certidp/messages.go — CertIDP message types
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|-----------|:-------------|--------|:----------------|-------|
| Error message constants | golang/nats-server/server/certidp/messages.go:17 | MISSING | — | No equivalent error/debug message constants; .NET uses structured logging |
| Debug message constants | golang/nats-server/server/certidp/messages.go:47 | MISSING | — | Debug format strings not ported; .NET logs differently |
| MsgTLSClientRejectConnection | golang/nats-server/server/certidp/messages.go:81 | PARTIAL | src/NATS.Server/Events/EventTypes.cs:520 | Reject event type exists but literal reject reason string not exposed |
| MsgTLSServerRejectConnection | golang/nats-server/server/certidp/messages.go:82 | PARTIAL | src/NATS.Server/Events/EventTypes.cs:520 | Same as above |
| MsgCacheOnline / MsgCacheOffline | golang/nats-server/server/certidp/messages.go:96 | MISSING | — | No cache status notification messages |
### certidp/ocsp_responder.go — OCSP responder client
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|-----------|:-------------|--------|:----------------|-------|
| FetchOCSPResponse | golang/nats-server/server/certidp/ocsp_responder.go:29 | MISSING | — | No HTTP-based OCSP response fetcher (GET with base64 encoding); .NET relies on X509Chain revocation |
| encodeOCSPRequest | golang/nats-server/server/certidp/ocsp_responder.go:89 | MISSING | — | No DER-to-base64-to-URL encoding for OCSP requests |
### certstore/certstore.go — OS certificate store access
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|-----------|:-------------|--------|:----------------|-------|
| StoreType (type) | golang/nats-server/server/certstore/certstore.go:24 | NOT_APPLICABLE | — | .NET has native X509Store; no need for Windows-specific store type enum |
| StoreMap / StoreOSMap | golang/nats-server/server/certstore/certstore.go:34 | NOT_APPLICABLE | — | .NET handles store locations via X509StoreLocation enum |
| MatchByType (type) | golang/nats-server/server/certstore/certstore.go:44 | NOT_APPLICABLE | — | .NET can use X509FindType enum for similar functionality |
| MatchByMap | golang/nats-server/server/certstore/certstore.go:52 | NOT_APPLICABLE | — | .NET equivalent: X509FindType |
| ParseCertStore | golang/nats-server/server/certstore/certstore.go:68 | NOT_APPLICABLE | — | .NET has built-in X509Store with StoreLocation |
| ParseCertMatchBy | golang/nats-server/server/certstore/certstore.go:80 | NOT_APPLICABLE | — | .NET has X509FindType |
| GetLeafIssuer | golang/nats-server/server/certstore/certstore.go:88 | MISSING | — | Could port using X509Chain verification to find issuer |
| credential (interface) | golang/nats-server/server/certstore/certstore.go:99 | NOT_APPLICABLE | — | .NET uses X509Certificate2 with private key; no separate credential interface needed |
### certstore/certstore_other.go — Non-Windows cert store stub
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|-----------|:-------------|--------|:----------------|-------|
| otherKey (struct) | golang/nats-server/server/certstore/certstore_other.go:27 | NOT_APPLICABLE | — | Go build-tag stub for non-Windows; .NET X509Store is cross-platform |
| TLSConfig (stub) | golang/nats-server/server/certstore/certstore_other.go:29 | NOT_APPLICABLE | — | Build-tag stub; not needed in .NET |
### certstore/certstore_windows.go — Windows cert store
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|-----------|:-------------|--------|:----------------|-------|
| Windows CNG/NCrypt constants | golang/nats-server/server/certstore/certstore_windows.go:42 | NOT_APPLICABLE | — | .NET X509Store abstracts away Windows CNG; no P/Invoke needed |
| winCertStore (struct) | golang/nats-server/server/certstore/certstore_windows.go:352 | NOT_APPLICABLE | — | .NET X509Store handles store access natively |
| TLSConfig (Windows) | golang/nats-server/server/certstore/certstore_windows.go:202 | NOT_APPLICABLE | — | .NET can use X509Store + X509Certificate2 directly; no syscall-level TLS config needed |
| winKey (struct) | golang/nats-server/server/certstore/certstore_windows.go:528 | NOT_APPLICABLE | — | .NET X509Certificate2.GetRSAPrivateKey/GetECDsaPrivateKey handles this |
| winSignECDSA / winSignRSA* | golang/nats-server/server/certstore/certstore_windows.go:562 | NOT_APPLICABLE | — | .NET runtime handles signing through X509Certificate2 private key |
| createCACertsPool | golang/nats-server/server/certstore/certstore_windows.go:173 | NOT_APPLICABLE | — | .NET uses X509Certificate2Collection / X509Chain for CA pool |
| certByIssuer / certBySubject / certByThumbprint | golang/nats-server/server/certstore/certstore_windows.go:390 | NOT_APPLICABLE | — | .NET uses X509Store.Certificates.Find() with X509FindType |
| unmarshalECC / winUnmarshalRSA | golang/nats-server/server/certstore/certstore_windows.go:844 | NOT_APPLICABLE | — | .NET BCL handles key deserialization |
### certstore/errors.go — Cert store errors
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|-----------|:-------------|--------|:----------------|-------|
| ErrBadCryptoStoreProvider | golang/nats-server/server/certstore/errors.go:9 | NOT_APPLICABLE | — | .NET X509Store throws its own exceptions |
| ErrBadRSAHashAlgorithm | golang/nats-server/server/certstore/errors.go:12 | NOT_APPLICABLE | — | .NET runtime handles hash algorithm validation |
| ErrBadSigningAlgorithm | golang/nats-server/server/certstore/errors.go:15 | NOT_APPLICABLE | — | .NET runtime handles signing |
| ErrStoreRSASigningError | golang/nats-server/server/certstore/errors.go:18 | NOT_APPLICABLE | — | .NET CryptographicException covers this |
| ErrStoreECDSASigningError | golang/nats-server/server/certstore/errors.go:21 | NOT_APPLICABLE | — | .NET CryptographicException covers this |
| ErrNoPrivateKeyStoreRef | golang/nats-server/server/certstore/errors.go:24 | NOT_APPLICABLE | — | .NET checks HasPrivateKey |
| ErrExtractingPrivateKeyMetadata | golang/nats-server/server/certstore/errors.go:27 | NOT_APPLICABLE | — | .NET handles internally |
| ErrExtractingECCPublicKey | golang/nats-server/server/certstore/errors.go:30 | NOT_APPLICABLE | — | .NET handles internally |
| ErrExtractingRSAPublicKey | golang/nats-server/server/certstore/errors.go:33 | NOT_APPLICABLE | — | .NET handles internally |
| ErrExtractingPublicKey | golang/nats-server/server/certstore/errors.go:36 | NOT_APPLICABLE | — | .NET handles internally |
| ErrBadPublicKeyAlgorithm | golang/nats-server/server/certstore/errors.go:39 | NOT_APPLICABLE | — | .NET handles internally |
| ErrExtractPropertyFromKey | golang/nats-server/server/certstore/errors.go:42 | NOT_APPLICABLE | — | .NET handles internally |
| ErrBadECCCurveName | golang/nats-server/server/certstore/errors.go:45 | NOT_APPLICABLE | — | .NET handles internally |
| ErrFailedCertSearch | golang/nats-server/server/certstore/errors.go:48 | NOT_APPLICABLE | — | .NET X509Store.Find returns empty collection |
| ErrFailedX509Extract | golang/nats-server/server/certstore/errors.go:51 | NOT_APPLICABLE | — | .NET handles internally |
| ErrBadMatchByType | golang/nats-server/server/certstore/errors.go:54 | NOT_APPLICABLE | — | .NET uses enum |
| ErrBadCertStore | golang/nats-server/server/certstore/errors.go:57 | NOT_APPLICABLE | — | .NET uses enum |
| ErrConflictCertFileAndStore | golang/nats-server/server/certstore/errors.go:60 | MISSING | — | Config validation for conflicting cert_file + cert_store not ported |
| ErrBadCertStoreField | golang/nats-server/server/certstore/errors.go:63 | NOT_APPLICABLE | — | Config validation |
| ErrBadCertMatchByField | golang/nats-server/server/certstore/errors.go:66 | NOT_APPLICABLE | — | Config validation |
| ErrBadCertMatchField | golang/nats-server/server/certstore/errors.go:69 | NOT_APPLICABLE | — | Config validation |
| ErrBadCaCertMatchField | golang/nats-server/server/certstore/errors.go:72 | NOT_APPLICABLE | — | Config validation |
| ErrBadCertMatchSkipInvalidField | golang/nats-server/server/certstore/errors.go:75 | NOT_APPLICABLE | — | Config validation |
| ErrOSNotCompatCertStore | golang/nats-server/server/certstore/errors.go:78 | NOT_APPLICABLE | — | .NET X509Store is cross-platform |
---
## Keeping This File Updated
After porting work is completed:
1. **Update status**: Change `MISSING → PORTED` or `PARTIAL → PORTED` for each item completed
2. **Add .NET path**: Fill in the ".NET Equivalent" column with the actual file:line
3. **Re-count LOC**: Update the LOC numbers in `stillmissing.md`:
```bash
# Re-count .NET source LOC for this module
find src/NATS.Server/Tls/ -name '*.cs' -type f -exec cat {} + | wc -l
# Re-count .NET test LOC for this module
# TLS tests are in root test directory — filter manually
```
4. **Add a changelog entry** below with date and summary of what was ported
5. **Update the parity DB** if new test mappings were created:
```bash
sqlite3 docs/test_parity.db "INSERT INTO test_mappings (go_test_id, dotnet_test_id, confidence, notes) VALUES (?, ?, 'manual', 'ported in YYYY-MM-DD session')"
```
## Change Log
| Date | Change | By |
|------|--------|----|
| 2026-02-25 | File created with LLM analysis instructions | auto |
| 2026-02-25 | Completed full gap inventory: 12 Go source files analyzed, 144 symbols classified (20 PORTED, 9 PARTIAL, 70 MISSING, 45 NOT_APPLICABLE, 0 DEFERRED) | claude-opus |