# 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 | PORTED | src/NATS.Server/Tls/TlsHelper.cs:33 | Added `ParseCertPem(string)` with explicit PEM block scanning/validation (`CERTIFICATE` only), multi-cert bundle parsing, and typed failures on unexpected block types. `LoadCaCertificates` now uses this parser. |
| 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 | PORTED | src/NATS.Server/Configuration/ConfigProcessor.cs:1101 | `ParseOcspPeer` supports short-form bool and long-form map parsing with Go-matching field names and conversion behavior |
| 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 | PORTED | src/NATS.Server/Tls/OcspPeerConfig.cs:105 | `OCSPPeerConfig.DefaultAllowedClockSkew` set to 30 seconds |
| DefaultOCSPResponderTimeout | golang/nats-server/server/certidp/certidp.go:31 | PORTED | src/NATS.Server/Tls/OcspPeerConfig.cs:106 | `OCSPPeerConfig.DefaultOCSPResponderTimeout` set to 2 seconds |
| DefaultTTLUnsetNextUpdate | golang/nats-server/server/certidp/certidp.go:32 | PORTED | src/NATS.Server/Tls/OcspPeerConfig.cs:107 | `OCSPPeerConfig.DefaultTTLUnsetNextUpdate` set to 1 hour |
| StatusAssertion (type) | golang/nats-server/server/certidp/certidp.go:35 | PORTED | src/NATS.Server/Tls/OcspPeerConfig.cs:9 | Added `StatusAssertion` enum with JSON converter and bidirectional string/int maps |
| GetStatusAssertionStr | golang/nats-server/server/certidp/certidp.go:56 | PORTED | src/NATS.Server/Tls/OcspPeerConfig.cs:40 | `StatusAssertionMaps.GetStatusAssertionStr(int)` with unknown fallback |
| ChainLink (struct) | golang/nats-server/server/certidp/certidp.go:93 | PORTED | src/NATS.Server/Tls/OcspPeerConfig.cs:81 | Added `ChainLink` type with `Leaf`, `Issuer`, and `OCSPWebEndpoints` |
| OCSPPeerConfig (struct) | golang/nats-server/server/certidp/certidp.go:100 | PORTED | src/NATS.Server/Tls/OcspPeerConfig.cs:103 | Added `OCSPPeerConfig` with matching fields and defaults |
| NewOCSPPeerConfig | golang/nats-server/server/certidp/certidp.go:110 | PORTED | src/NATS.Server/Tls/OcspPeerConfig.cs:117 | Added `OCSPPeerConfig.NewOCSPPeerConfig()` 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 | PORTED | src/NATS.Server/Tls/OcspPeerConfig.cs:88 | Added `CertInfo` DTO with subject, issuer, fingerprint, and raw fields |
| GenerateFingerprint | golang/nats-server/server/certidp/certidp.go:179 | PORTED | src/NATS.Server/Tls/TlsHelper.cs:95 | Added `GenerateFingerprint` using SHA-256 of raw certificate bytes and base64 encoding |
| getWebEndpoints | golang/nats-server/server/certidp/certidp.go:184 | PORTED | src/NATS.Server/Tls/TlsHelper.cs:101 | Added `GetWebEndpoints` filtering to valid absolute HTTP/HTTPS URIs |
| GetSubjectDNForm | golang/nats-server/server/certidp/certidp.go:203 | PORTED | src/NATS.Server/Tls/TlsHelper.cs:117 | Added subject DN helper returning empty string for null cert |
| GetIssuerDNForm | golang/nats-server/server/certidp/certidp.go:212 | PORTED | src/NATS.Server/Tls/TlsHelper.cs:122 | Added issuer DN helper returning empty string for null cert |
| CertOCSPEligible | golang/nats-server/server/certidp/certidp.go:221 | PORTED | src/NATS.Server/Tls/TlsHelper.cs:142 | Added AIA OCSP responder extraction + HTTP(S) endpoint filtering, and populates `ChainLink.OCSPWebEndpoints` on success |
| GetLeafIssuerCert | golang/nats-server/server/certidp/certidp.go:237 | PORTED | src/NATS.Server/Tls/TlsHelper.cs:165 | Added positional issuer extraction helper (`leafPos + 1`) with bounds/self-signed guards |
| OCSPResponseCurrent | golang/nats-server/server/certidp/certidp.go:250 | PORTED | src/NATS.Server/Tls/TlsHelper.cs:193 | Added OCSP response currency window checks with configurable clock-skew and fallback TTL when `NextUpdate` is unset |
| ValidDelegationCheck | golang/nats-server/server/certidp/certidp.go:288 | PORTED | src/NATS.Server/Tls/TlsHelper.cs:224 | Added delegation validation: direct issuer response accepted, delegated responder requires OCSPSigning EKU |

### 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 | PORTED | src/NATS.Server/Tls/OcspPeerMessages.cs:6 | Ported certidp error string constants (returned and directly-logged variants) into `OcspPeerMessages` |
| Debug message constants | golang/nats-server/server/certidp/messages.go:47 | PORTED | src/NATS.Server/Tls/OcspPeerMessages.cs:36 | Ported certidp debug-format constants used by OCSP peer/cache workflows |
| MsgTLSClientRejectConnection | golang/nats-server/server/certidp/messages.go:81 | PORTED | src/NATS.Server/Tls/OcspPeerMessages.cs:5 | Added literal reject reason constant for client OCSP validation failures |
| MsgTLSServerRejectConnection | golang/nats-server/server/certidp/messages.go:82 | PORTED | src/NATS.Server/Tls/OcspPeerMessages.cs:6 | Added literal reject reason constant for server OCSP validation failures |
| MsgCacheOnline / MsgCacheOffline | golang/nats-server/server/certidp/messages.go:96 | PORTED | src/NATS.Server/Tls/OcspPeerMessages.cs:7 | Added cache online/offline informational message templates |

### 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 | PORTED | src/NATS.Server/Tls/TlsHelper.cs:176 | Added verified-chain issuer resolver using custom-root trust and returning chain element 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-26 | Ported `parseCertPEM` parity by adding strict PEM certificate parser (`CERTIFICATE` blocks only), wiring `LoadCaCertificates` through it, and adding focused TLS helper tests for invalid block rejection and multi-cert bundles. | codex |
| 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 |
| 2026-02-25 | Ported OCSP eligibility/issuer/currentness/delegation helpers, certstore leaf issuer resolution, and certidp error/debug message constants with targeted TLS/Ocsp parity tests | codex |
