Skip to content

Support TLS 1.3 by default with optional min/max TLS enforcement (#240) - #242

Draft
DanPeterson wants to merge 1 commit into
mainfrom
danpeterson-tls13-support
Draft

Support TLS 1.3 by default with optional min/max TLS enforcement (#240)#242
DanPeterson wants to merge 1 commit into
mainfrom
danpeterson-tls13-support

Conversation

@DanPeterson

Copy link
Copy Markdown
Contributor

Summary

Fixes #240. SafeguardDotNet previously hard-pinned SslProtocols.Tls12 on every HTTP and SignalR connection, which prevented TLS 1.3 from ever being negotiated — even against SPP 9.0 appliances that support it.

This PR stops pinning TLS 1.2 and instead lets the OS negotiate the best mutually supported protocol (SslProtocols.None), so TLS 1.3 is used automatically where the client OS and appliance support it. HTTP/1.1 behavior is unchanged.

It also adds optional TLS enforcement via minTlsVersion / maxTlsVersion parameters, mirroring the min/max paradigm proposed for PySafeguard so the SDKs stay consistent.

What changed

  • New public enum SafeguardTlsVersion { Tls12, Tls13 } and internal TlsVersionMapper that maps an optional min/max range onto SslProtocols (both null → None; validates min ≤ max).
  • Optional minTlsVersion / maxTlsVersion (SafeguardTlsVersion?, default null = negotiate) appended to:
    • all Safeguard.Connect overloads
    • all Safeguard.A2A.GetContext overloads
    • the AgentBasedLoginUtils RSTS login helpers
    • the PKCE, Browser, DeviceCode, and GuiLogin login modules
  • The resolved SslProtocols is stored on the authentication mechanism and reused consistently by the HTTP client, event listeners, and A2A SignalR connections.
  • Docs: new "TLS Versions" section in README.md, plus AGENTS.md, api-patterns, and a2a-workflow skill updates.

Usage

// Default: negotiate best protocol (enables TLS 1.3 where available)
var connection = Safeguard.Connect("appliance", "local", "Admin", password);

// Require TLS 1.3 or newer
var strict = Safeguard.Connect("appliance", "local", "Admin", password,
    minTlsVersion: SafeguardTlsVersion.Tls13);

// Pin to exactly TLS 1.2
var legacy = Safeguard.Connect("appliance", "local", "Admin", password,
    minTlsVersion: SafeguardTlsVersion.Tls12, maxTlsVersion: SafeguardTlsVersion.Tls12);

Testing

  • SafeguardDotNet.Core.sln builds with 0 warnings / 0 errors.
  • SafeguardDotNet.Framework.sln (GuiLogin) builds.
  • Unit tests pass (52/52), including 7 new TlsVersionMapper tests covering the negotiate default, single-bound pinning, full range, and the min > max error.

Compatibility notes

  • Appending optional parameters is source-compatible but a binary change — callers must recompile.
  • The default TLS behavior changes from pinned 1.2 to OS-negotiated; on very old client OSes this could allow a protocol below 1.2. Callers can pin minTlsVersion: SafeguardTlsVersion.Tls12 to preserve the old floor.
  • Suggested release: v9.1.0 (minor).

Stop hard-pinning SslProtocols.Tls12 so connections negotiate the best available protocol (TLS 1.3 where the OS and appliance support it) via SslProtocols.None. Add optional minTlsVersion/maxTlsVersion (SafeguardTlsVersion?) parameters across all Connect and A2A.GetContext overloads, the RSTS login helpers, and the PKCE, Browser, DeviceCode, and GuiLogin modules, mirroring the PySafeguard min/max paradigm. The resolved protocol flows through the HTTP client, event listeners, and A2A SignalR connections. HTTP/1.1 behavior is unchanged.
@DanPeterson
DanPeterson requested a review from a team as a code owner August 14, 2026 01:18
@petrsnd
petrsnd marked this pull request as draft August 17, 2026 20:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support SPP 9.0 TLS 1.3: stop pinning SslProtocols.Tls12 and add optional TLS 1.3 enforcement

1 participant