Skip to content

[DO NOT MERGE] Connection encryption info – Slice A: public API surface - #4830

Open
priyankatiwari08 wants to merge 6 commits into
mainfrom
dev/automation/encryption-info-slice-a-api
Open

priyankatiwari08 wants to merge 6 commits into
mainfrom
dev/automation/encryption-info-slice-a-api

Conversation

@priyankatiwari08

@priyankatiwari08 priyankatiwari08 commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Slice A of the connection encryption information feature (related to #2353). It adds the public API surface only. SqlConnection.EncryptionInfo and SqlException.EncryptionInfo always return a non-null empty snapshot, with every field null. Nothing in the TdsParser or SNI layers populates the values yet.

For background, scenarios and the intended behaviour, see the design note added in this PR: doc/design-notes/2353-connection-encryption-info.md.

Scope

In this PR (Slice A)

  • New types: SqlConnectionEncryptionInfo, SqlConnectionEncryptionMode and SqlServerCertificateValidationErrors.
  • The SqlConnection.EncryptionInfo and SqlException.EncryptionInfo getters.
  • An internal constructor and an internal Empty instance, so later slices and tests can build populated snapshots.
  • Reference assembly, XML doc snippets, design note and unit tests.

Later slices (stacked on this branch)

  • B: capture the negotiated mode, TLS protocol and cipher suite, and attach the snapshot to SqlException when opening fails.
  • C: capture certificate details (the presented server certificate, validation errors and chain status) in Managed SNI, on both successful and failed attempts.
  • D: Native SNI interop for the certificate details and cipher suite.

Public API

namespace Microsoft.Data.SqlClient
{
    public sealed class SqlConnectionEncryptionInfo
    {
        // No public constructor.
        public SqlConnectionEncryptionMode? NegotiatedEncryptionMode { get; }
        public System.Security.Authentication.SslProtocols? NegotiatedEncryptionProtocol { get; }
        public int? NegotiatedCipherSuiteId { get; }   // IANA TLS cipher suite id selected by the server, e.g. 0x1302 (needs API review)
#if NET
        [CLSCompliant(false)]
        public System.Net.Security.TlsCipherSuite? NegotiatedCipherSuite { get; }   // same value as an enum (.NET only)
#endif
        public SqlServerCertificateValidationErrors? ValidationErrors { get; }
        public System.Security.Cryptography.X509Certificates.X509ChainStatusFlags? ChainStatus { get; }

        // Server-presented leaf certificate (public part only); new caller-owned instance per call, or null.
        public System.Security.Cryptography.X509Certificates.X509Certificate2? GetServerCertificate();
    }

    public enum SqlConnectionEncryptionMode { None = 0, LoginOnly = 1, Full = 2, Strict = 3 }

    [Flags]
    public enum SqlServerCertificateValidationErrors
    {
        None = 0, ServerCertificateMissing = 1, HostNameMismatch = 2, ChainError = 4, PinnedCertificateMismatch = 8
    }

    public sealed partial class SqlConnection
    {
        public SqlConnectionEncryptionInfo EncryptionInfo { get; }   // always non-null
    }

    public sealed partial class SqlException
    {
        public SqlConnectionEncryptionInfo EncryptionInfo { get; }   // always non-null
    }
}

Negotiated cipher suite (needs API review)

NegotiatedCipherSuiteId (int?, all targets) and the .NET-only NegotiatedCipherSuite (TlsCipherSuite?) are a proposal that hasn't been reviewed by the team yet.

  • Value: the IANA TLS cipher suite identifier the server selected in its ServerHello, for example 0x1302 for TLS_AES_256_GCM_SHA384. It is not a Windows ALG_ID. Only the selected suite is reported: not the client's offered list, CAPI2 chain detail, or key-exchange/PQC group information.
  • Why two members: NegotiatedCipherSuiteId has the same CLS-compliant signature on every target, so libraries compiled against the netstandard2.0 reference bind correctly at run time. TlsCipherSuite exists only on .NET and its ushort backing isn't CLS-compliant, so the strongly typed property is .NET-only and marked [CLSCompliant(false)]. Unknown IANA ids are preserved. The internal constructor rejects values outside 0 to 0xFFFF.
  • null means the driver couldn't obtain the value (no TLS, not captured, older SNI, or a failure before it was known). It doesn't mean the server selected no suite. On failure, an observed value is kept, like the protocol.
  • Capture is added in Slices B (managed) and D (native SNI). In this slice the value is always null.

Notes for reviewers

  • No behaviour changes: SqlException serialization, Message and ToString() are unchanged. The snapshot is not serialized, so a deserialized exception returns an empty snapshot.
  • Server certificate is a method: after the threat model review, the certificate hash was replaced with the full server-presented leaf certificate. GetServerCertificate() parses a private DER copy and returns a new X509Certificate2 on every call, which the caller owns and should dispose; it returns null when no certificate was captured or the bytes can't be parsed. It's a method rather than a property because each call allocates a disposable object. The certificate is for diagnostics only, has no private key, and isn't a trust decision. The design note explains the rationale.

Test results (Windows, Debug)

  • Driver (net462;net10.0), ref and not-supported (GenAPI) builds: 0 warnings, 0 errors.
  • SqlConnectionEncryptionInfoTests after the certificate change (f6038d1):
    • net10.0: 56 passed, 0 failed
    • net462: 56 passed, 0 failed

The new tests cover:

  • empty-snapshot invariants
  • getters on new, closed and disposed connections, and on every SqlException factory and InternalClone
  • serialization round trips
  • Message, ToString() and serialized payloads unchanged, with no certificate data in them
  • populated round trips
  • null kept distinct from zero values (None, NoError)
  • GetServerCertificate(): new instance per call, round trip (thumbprint and raw data), no private key, defensive copying, clone isolation, unparsable bytes and null/empty input return null, IsEmpty
  • flags combinations
  • cipher suite: per-target members via reflection, null by default, IANA values (including unknown/GREASE ids, 0 and 0xFFFF) round-trip, enum mapping on .NET, out-of-range values rejected, kept by InternalClone and absent from ToString()
  • enum numeric contracts

Checklist

  • Tests added or updated
  • Public API changes documented (doc/snippets/Microsoft.Data.SqlClient/*.xml)
  • Verified against customer repro (if applicable): not applicable to this API-only slice
  • Ensure no breaking changes introduced (additive API only)

Copilot AI balanced review requested due to automatic review settings October 12, 2026 08:41
@github-project-automation github-project-automation Bot moved this to To triage in SqlClient Board Oct 12, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The permanent public API still has explicitly provisional identifiers, naming, and enum membership requiring final human design approval.

1 open finding
What changed in this PR

Adds the first, API-only slice for exposing connection encryption diagnostics. Values remain empty until later TDS/SNI integration.

Changes:

  • Adds encryption snapshot and enum APIs.
  • Exposes snapshots through SqlConnection and SqlException.
  • Adds reference declarations, XML documentation, and unit coverage.

Existing feedback was unavailable because no PR thread data was supplied.

File Description
SqlConnectionEncryptionInfoTests.cs Tests API contracts and serialization.
SqlServerCertificateValidationErrors.cs Adds validation flags.
SqlException.cs Adds exception snapshot storage.
SqlConnectionEncryptionMode.cs Adds negotiated-mode values.
SqlConnectionEncryptionInfo.cs Implements immutable snapshots.
SqlConnection.cs Exposes the empty snapshot.
ref/​Microsoft.Data.SqlClient.cs Declares the public API.
SqlServerCertificateValidationErrors.xml Documents validation flags.
SqlException.xml Documents exception diagnostics.
SqlConnectionEncryptionMode.xml Documents encryption modes.
SqlConnectionEncryptionInfo.xml Documents snapshot semantics.
SqlConnection.xml Documents connection diagnostics.

🧠 Review effort: Balanced


💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.


#region Helpers

private static void AssertEmpty(SqlConnectionEncryptionInfo? info)
Copilot AI balanced review requested due to automatic review settings October 12, 2026 08:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The certificate hash exposes mutable backing storage, violating the documented immutable-snapshot contract.

3 open findings

🧠 Review effort: Balanced

Comment on lines +68 to +71
public ReadOnlyMemory<byte>? PresentedServerCertificateHash256 =>
_presentedServerCertificateHash256 is null
? (ReadOnlyMemory<byte>?)null
: new ReadOnlyMemory<byte>(_presentedServerCertificateHash256);
private const string ServerVersion = "16.00.1000";
private const string SampleHashHex = "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F";

private static byte[] CreateSampleHash()
@codecov

codecov Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 66.20%. Comparing base (7b1184b) to head (9701ce8).
⚠️ Report is 9 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4830      +/-   ##
==========================================
- Coverage   67.94%   66.20%   -1.74%     
==========================================
  Files         311      306       -5     
  Lines       45399    68344   +22945     
==========================================
+ Hits        30845    45249   +14404     
- Misses      14554    23095    +8541     
Flag Coverage Δ
CI-SqlClient ?
PR-SqlClient-Project 66.20% <100.00%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copilot AI balanced review requested due to automatic review settings October 12, 2026 10:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The hash getter exposes mutable backing storage, violating the snapshot’s documented immutability.

4 open findings
Previously missed (2)

In code that hasn't changed since last review

Medium severity Return a defensive copy to protect the immutable snapshot

src/​Microsoft.Data.SqlClient/​src/​Microsoft/​Data/​SqlClient/​SqlConnectionEncryptionInfo.cs:71

ReadOnlyMemory<byte> does not protect the backing array: callers can recover writable storage with MemoryMarshal.TryGetArray/AsMemory and alter this private array. That changes later reads from the supposedly immutable snapshot and makes the current ReturnedCopy test pass only because it calls ToArray() first. Return a fresh defensive copy from the getter (and update the test to mutate the returned memory through MemoryMarshal) so external mutation cannot alter the snapshot.

Low severity Document the test helper with summary, parameters, and return

src/​Microsoft.Data.SqlClient/​tests/​UnitTests/​Microsoft/​Data/​SqlClient/​SqlConnectionEncryptionInfoTests.cs:35

Add the required XML documentation for this test helper. Repository policy requires test helpers to have a summary plus applicable parameter and return documentation (.github/instructions/testing.instructions.md:180-193).

🧠 Review effort: Balanced

Comment on lines +175 to +177
| **B** | Capture the negotiated mode and TLS protocol, and attach the snapshot to `SqlException` when opening fails. |
| **C** | Capture certificate details (hash, validation errors, and chain status) in Managed SNI, on both successful and failed attempts. |
| **D** | Native SNI interop for the certificate details. |
@priyankatiwari08 priyankatiwari08 added this to the 8.0.0-preview1 milestone Oct 19, 2026
@priyankatiwari08 priyankatiwari08 changed the title [Draft] Connection encryption info – Slice A: public API surface [DO NOT MERGE] [Draft] Connection encryption info – Slice A: public API surface Oct 19, 2026
@mdaigle mdaigle linked an issue Oct 20, 2026 that may be closed by this pull request
Copilot AI balanced review requested due to automatic review settings October 22, 2026 08:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The draft public API, particularly NegotiatedCipherSuite, explicitly requires human API review before approval.

4 open issues

Review effort: Balanced

priyankatiwari08 and others added 4 commits October 26, 2026 21:56
Adds SqlConnectionEncryptionInfo, SqlConnectionEncryptionMode, and
SqlServerCertificateValidationErrors, plus SqlConnection.EncryptionInfo and
SqlException.EncryptionInfo getters. In this slice both getters always return
an empty snapshot; no TdsParser/SNI plumbing yet.

Related to #2353.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…gn note

Replace PresentedServerCertificateThumbprint (string?) with
PresentedServerCertificateHash256 (ReadOnlyMemory<byte>?), per the API
agreed with the SSMS team. The internal constructor validates the hash is
32 bytes and keeps a private copy. Add doc/design-notes/2353 for reviewers.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Adds int? NegotiatedCipherSuite (IANA TLS cipher suite identifier selected by the server) to the public snapshot, ref assembly, XML docs, design note and unit tests. Proposal pending API review.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@priyankatiwari08 priyankatiwari08 removed this from the 8.0.0-preview1 milestone Oct 26, 2026
@priyankatiwari08 priyankatiwari08 added this to the 8.1.0-preview1 milestone Oct 26, 2026
@priyankatiwari08
priyankatiwari08 force-pushed the dev/automation/encryption-info-slice-a-api branch from 02b760b to abfd548 Compare October 26, 2026 16:29
Copilot AI balanced review requested due to automatic review settings October 26, 2026 16:29

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The unresolved exposure of mutable hash backing storage conflicts with the documented immutable-snapshot contract.

4 open issues

Review effort: Balanced

@priyankatiwari08
priyankatiwari08 marked this pull request as ready for review October 27, 2026 07:41
@priyankatiwari08
priyankatiwari08 requested a review from a team as a code owner October 27, 2026 07:41
@priyankatiwari08 priyankatiwari08 changed the title [DO NOT MERGE] [Draft] Connection encryption info – Slice A: public API surface [DO NOT MERGE] Connection encryption info – Slice A: public API surface Oct 27, 2026
…ite? on .NET (review feedback)

- Add NegotiatedCipherSuiteId (int?, CLS-compliant) on every target framework.
  It returns the IANA identifier unchanged, including unnamed/GREASE values.
- On .NET only, NegotiatedCipherSuite exposes the same value as
  System.Net.Security.TlsCipherSuite? and is marked [CLSCompliant(false)].
- No NegotiatedCipherSuite member on net462/netstandard2.0, so the only shared
  member has an identical signature across the netstandard2.0 ref and the
  net10.0 implementation.
- The internal constructor still takes int? (0..0xFFFF). The internal
  NegotiatedCipherSuiteValue is removed.
- Update ref, XML docs, design note and unit tests.

Copilot-Session: c47d6846-090b-4240-9f75-efa50beb8e34
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 28, 2026 09:10

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The PR description and API example still conflict with the implemented cipher-suite properties and must document both properties and their target-framework availability.

5 open issues

Review effort: Balanced

#if NET
/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnectionEncryptionInfo.xml' path='docs/members[@name="SqlConnectionEncryptionInfo"]/NegotiatedCipherSuite/*'/>
[System.CLSCompliantAttribute(false)]
public System.Net.Security.TlsCipherSuite? NegotiatedCipherSuite { get { throw null; } }
… (threat model review)

Remove PresentedServerCertificateHash256 and expose the server-presented leaf
certificate via X509Certificate2? GetServerCertificate(). Each call returns a
new caller-owned instance parsed from a private DER copy, or null when no
certificate was captured or the bytes cannot be parsed. The internal ctor now
takes byte[]? presentedServerCertificate, and PresentedServerCertificateRawData
exposes the captured bytes internally. Update ref, XML docs, design note and
unit tests.

Copilot-Session: c47d6846-090b-4240-9f75-efa50beb8e34
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 30, 2026 07:16

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The permanent public encryption-diagnostics contract requires human API-design sign-off on the cross-framework cipher-suite identifier and .NET-only typed member before finalization.

5 open issues

Review effort: Balanced

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: To triage

Development

Successfully merging this pull request may close these issues.

Feature | Expose connection encryption information to clients

5 participants