From 44af17b7d98ca8086be35f5adcd3829856a1b88e Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Thu, 9 Jul 2026 05:47:09 +0100 Subject: [PATCH 01/10] Do not pass ECDiffieHellman directly to CreateEnclaveSession This method will need to be publicly exposed via netstandard2.0 (which doesn't provide the type.) --- .../SqlColumnEncryptionEnclaveProvider.xml | 4 ++-- .../Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs | 6 +++--- .../src/Microsoft/Data/SqlClient/EnclaveDelegate.Crypto.cs | 4 ++-- .../Data/SqlClient/NoneAttestationEnclaveProvider.cs | 6 +++--- .../Data/SqlClient/SqlColumnEncryptionEnclaveProvider.cs | 4 ++-- .../Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs | 6 +++--- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlColumnEncryptionEnclaveProvider.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlColumnEncryptionEnclaveProvider.xml index 4276c98b9f..4b4762439a 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlColumnEncryptionEnclaveProvider.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlColumnEncryptionEnclaveProvider.xml @@ -17,8 +17,8 @@ The information the provider uses to attest the enclave and generate a symmetric key for the session. The format of this information is specific to the enclave attestation protocol. - - A Diffie-Hellman algorithm object that encapsulates a client-side key pair. + + A set of parameters for the attestation process, including the client's Diffie-Hellman key pair. The set of parameters required for an enclave session. diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs index 6d26429122..fa1fa21e39 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. @@ -80,7 +80,7 @@ internal override SqlEnclaveAttestationParameters GetAttestationParameters(strin } // When overridden in a derived class, performs enclave attestation, generates a symmetric key for the session, creates a an enclave session and stores the session information in the cache. - internal override void CreateEnclaveSession(byte[] attestationInfo, ECDiffieHellman clientDHKey, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength, out SqlEnclaveSession sqlEnclaveSession, out long counter) + internal override void CreateEnclaveSession(byte[] attestationInfo, SqlEnclaveAttestationParameters attestationParameters, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength, out SqlEnclaveSession sqlEnclaveSession, out long counter) { sqlEnclaveSession = null; counter = 0; @@ -103,7 +103,7 @@ internal override void CreateEnclaveSession(byte[] attestationInfo, ECDiffieHell VerifyAzureAttestationInfo(enclaveSessionParameters.AttestationUrl, attestInfo.EnclaveType, attestInfo.AttestationToken.AttestationToken, attestInfo.Identity, nonce); // Set up shared secret and validate signature - byte[] sharedSecret = GetSharedSecret(attestInfo.Identity, nonce, attestInfo.EnclaveType, attestInfo.EnclaveDHInfo, clientDHKey); + byte[] sharedSecret = GetSharedSecret(attestInfo.Identity, nonce, attestInfo.EnclaveType, attestInfo.EnclaveDHInfo, attestationParameters.ClientDiffieHellmanKey); // add session to cache sqlEnclaveSession = AddEnclaveSessionToCache(enclaveSessionParameters, sharedSecret, attestInfo.SessionId, out counter); diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.Crypto.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.Crypto.cs index 8b461087bd..8b3c5b5360 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.Crypto.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.Crypto.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. @@ -47,7 +47,7 @@ internal void CreateEnclaveSession(SqlConnectionAttestationProtocol attestationP sqlColumnEncryptionEnclaveProvider.CreateEnclaveSession( attestationInfo, - attestationParameters.ClientDiffieHellmanKey, + attestationParameters, enclaveSessionParameters, customData, customDataLength, diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/NoneAttestationEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/NoneAttestationEnclaveProvider.cs index fabd69c976..d24c389d65 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/NoneAttestationEnclaveProvider.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/NoneAttestationEnclaveProvider.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. @@ -31,7 +31,7 @@ internal override SqlEnclaveAttestationParameters GetAttestationParameters(strin } // When overridden in a derived class, performs enclave attestation, generates a symmetric key for the session, creates an enclave session and stores the session information in the cache. - internal override void CreateEnclaveSession(byte[] attestationInfo, ECDiffieHellman clientDHKey, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength, out SqlEnclaveSession sqlEnclaveSession, out long counter) + internal override void CreateEnclaveSession(byte[] attestationInfo, SqlEnclaveAttestationParameters attestationParameters, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength, out SqlEnclaveSession sqlEnclaveSession, out long counter) { // for None attestation: enclave does not send public key, and sends an empty attestation info // The only non-trivial content it sends is the session setup info (DH pubkey of enclave) @@ -77,7 +77,7 @@ internal override void CreateEnclaveSession(byte[] attestationInfo, ECDiffieHell byte[] sharedSecret; using ECDiffieHellman ecdh = KeyConverter.CreateECDiffieHellmanFromPublicKeyBlob(trustedModuleDHPublicKey); - sharedSecret = KeyConverter.DeriveKey(clientDHKey, ecdh.PublicKey); + sharedSecret = KeyConverter.DeriveKey(attestationParameters.ClientDiffieHellmanKey, ecdh.PublicKey); long sessionId = BitConverter.ToInt64(enclaveSessionHandle, 0); sqlEnclaveSession = AddEnclaveSessionToCache(enclaveSessionParameters, sharedSecret, sessionId, out counter); diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.cs index 21a62a80c3..2079097233 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. @@ -16,7 +16,7 @@ internal abstract class SqlColumnEncryptionEnclaveProvider internal abstract SqlEnclaveAttestationParameters GetAttestationParameters(string attestationUrl, byte[] customData, int customDataLength); /// - internal abstract void CreateEnclaveSession(byte[] enclaveAttestationInfo, ECDiffieHellman clientDiffieHellmanKey, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength, out SqlEnclaveSession sqlEnclaveSession, out long counter); + internal abstract void CreateEnclaveSession(byte[] enclaveAttestationInfo, SqlEnclaveAttestationParameters attestationParameters, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength, out SqlEnclaveSession sqlEnclaveSession, out long counter); /// internal abstract void InvalidateEnclaveSession(EnclaveSessionParameters enclaveSessionParameters, SqlEnclaveSession enclaveSession); diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs index a3b4545d03..1c19172b09 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. @@ -101,7 +101,7 @@ internal override SqlEnclaveAttestationParameters GetAttestationParameters(strin } // When overridden in a derived class, performs enclave attestation, generates a symmetric key for the session, creates a an enclave session and stores the session information in the cache. - internal override void CreateEnclaveSession(byte[] attestationInfo, ECDiffieHellman clientDHKey, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength, out SqlEnclaveSession sqlEnclaveSession, out long counter) + internal override void CreateEnclaveSession(byte[] attestationInfo, SqlEnclaveAttestationParameters attestationParameters, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength, out SqlEnclaveSession sqlEnclaveSession, out long counter) { sqlEnclaveSession = null; counter = 0; @@ -123,7 +123,7 @@ internal override void CreateEnclaveSession(byte[] attestationInfo, ECDiffieHell VerifyAttestationInfo(enclaveSessionParameters.AttestationUrl, info.HealthReport, info.EnclaveReportPackage); // Set up shared secret and validate signature - byte[] sharedSecret = GetSharedSecret(info.Identity, info.EnclaveDHInfo, clientDHKey); + byte[] sharedSecret = GetSharedSecret(info.Identity, info.EnclaveDHInfo, attestationParameters.ClientDiffieHellmanKey); // add session to cache sqlEnclaveSession = AddEnclaveSessionToCache(enclaveSessionParameters, sharedSecret, info.SessionId, out counter); From 73503db49045ad5f009f7435a425032a74a1e6c5 Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Thu, 9 Jul 2026 05:52:23 +0100 Subject: [PATCH 02/10] Move SqlEnclaveSession constructor from EnclaveSessionCache.CreateSession This is to support the later CreateEnclaveSession refactor. NB: this means that SqlEnclaveSession is no longer constructed under enclaveCacheLock. This was unnecessary from the outset. --- .../SqlClient/AzureAttestationBasedEnclaveProvider.cs | 3 ++- .../src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs | 6 +++--- .../src/Microsoft/Data/SqlClient/EnclaveSessionCache.cs | 8 ++------ .../Data/SqlClient/NoneAttestationEnclaveProvider.cs | 7 ++----- .../SqlClient/VirtualSecureModeEnclaveProviderBase.cs | 3 ++- 5 files changed, 11 insertions(+), 16 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs index fa1fa21e39..bc6a68dc7a 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs @@ -106,7 +106,8 @@ internal override void CreateEnclaveSession(byte[] attestationInfo, SqlEnclaveAt byte[] sharedSecret = GetSharedSecret(attestInfo.Identity, nonce, attestInfo.EnclaveType, attestInfo.EnclaveDHInfo, attestationParameters.ClientDiffieHellmanKey); // add session to cache - sqlEnclaveSession = AddEnclaveSessionToCache(enclaveSessionParameters, sharedSecret, attestInfo.SessionId, out counter); + sqlEnclaveSession = new SqlEnclaveSession(sharedSecret, attestInfo.SessionId); + AddEnclaveSessionToCache(enclaveSessionParameters, sqlEnclaveSession, out counter); } else { diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs index c81f04471c..7bb2bd3f34 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. @@ -207,9 +207,9 @@ protected SqlEnclaveSession GetEnclaveSessionFromCache(EnclaveSessionParameters } // Helper method for adding the enclave session to the session cache - protected SqlEnclaveSession AddEnclaveSessionToCache(EnclaveSessionParameters enclaveSessionParameters, byte[] sharedSecret, long sessionId, out long counter) + protected void AddEnclaveSessionToCache(EnclaveSessionParameters enclaveSessionParameters, SqlEnclaveSession enclaveSession, out long counter) { - return SessionCache.CreateSession(enclaveSessionParameters, sharedSecret, sessionId, out counter); + SessionCache.CreateSession(enclaveSessionParameters, enclaveSession, out counter); } } #endregion diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveSessionCache.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveSessionCache.cs index c3845244f3..f64647d232 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveSessionCache.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveSessionCache.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. @@ -55,19 +55,15 @@ internal void InvalidateSession(EnclaveSessionParameters enclaveSessionParameter } // Creates a new SqlEnclaveSession and adds it to the cache - internal SqlEnclaveSession CreateSession(EnclaveSessionParameters enclaveSessionParameters, byte[] sharedSecret, long sessionId, out long counter) + internal void CreateSession(EnclaveSessionParameters enclaveSessionParameters, SqlEnclaveSession enclaveSession, out long counter) { string cacheKey = GenerateCacheKey(enclaveSessionParameters); - SqlEnclaveSession enclaveSession = null; lock (enclaveCacheLock) { - enclaveSession = new SqlEnclaveSession(sharedSecret, sessionId); enclaveMemoryCache.Set(cacheKey, enclaveSession, absoluteExpirationRelativeToNow: s_enclaveCacheTimeout); counter = Interlocked.Increment(ref _counter); } - - return enclaveSession; } // Generates the cache key for the enclave session cache diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/NoneAttestationEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/NoneAttestationEnclaveProvider.cs index d24c389d65..034cc9fba8 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/NoneAttestationEnclaveProvider.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/NoneAttestationEnclaveProvider.cs @@ -79,12 +79,9 @@ internal override void CreateEnclaveSession(byte[] attestationInfo, SqlEnclaveAt using ECDiffieHellman ecdh = KeyConverter.CreateECDiffieHellmanFromPublicKeyBlob(trustedModuleDHPublicKey); sharedSecret = KeyConverter.DeriveKey(attestationParameters.ClientDiffieHellmanKey, ecdh.PublicKey); long sessionId = BitConverter.ToInt64(enclaveSessionHandle, 0); - sqlEnclaveSession = AddEnclaveSessionToCache(enclaveSessionParameters, sharedSecret, sessionId, out counter); - if (sqlEnclaveSession is null) - { - throw SQL.AttestationFailed(Strings.FailToCreateEnclaveSession); - } + sqlEnclaveSession = new SqlEnclaveSession(sharedSecret, sessionId); + AddEnclaveSessionToCache(enclaveSessionParameters, sqlEnclaveSession, out counter); } } finally diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs index 1c19172b09..966c94225e 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs @@ -126,7 +126,8 @@ internal override void CreateEnclaveSession(byte[] attestationInfo, SqlEnclaveAt byte[] sharedSecret = GetSharedSecret(info.Identity, info.EnclaveDHInfo, attestationParameters.ClientDiffieHellmanKey); // add session to cache - sqlEnclaveSession = AddEnclaveSessionToCache(enclaveSessionParameters, sharedSecret, info.SessionId, out counter); + sqlEnclaveSession = new SqlEnclaveSession(sharedSecret, info.SessionId); + AddEnclaveSessionToCache(enclaveSessionParameters, sqlEnclaveSession, out counter); } else { From 05c5853fc8b04b9aa5782056813e11fb0cd88bd4 Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Thu, 9 Jul 2026 05:57:59 +0100 Subject: [PATCH 03/10] Create new CreateEnclaveSessionCore method on the base class CreateEnclaveSession will be refactored to use this. --- .../SqlClient/AzureAttestationBasedEnclaveProvider.cs | 10 ++++++++++ .../Microsoft/Data/SqlClient/EnclaveProviderBase.cs | 2 ++ .../Data/SqlClient/NoneAttestationEnclaveProvider.cs | 10 ++++++++++ .../SqlClient/VirtualSecureModeEnclaveProviderBase.cs | 10 ++++++++++ 4 files changed, 32 insertions(+) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs index bc6a68dc7a..4760b99477 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs @@ -79,6 +79,16 @@ internal override SqlEnclaveAttestationParameters GetAttestationParameters(strin return new SqlEnclaveAttestationParameters(AzureBasedAttestationProtocolId, attestationParam, clientDHKey); } + protected override SqlEnclaveSession CreateEnclaveSessionCore( + byte[] enclaveAttestationInfo, + SqlEnclaveAttestationParameters attestationParameters, + EnclaveSessionParameters enclaveSessionParameters, + byte[] customData, + int customDataLength) + { + throw new NotImplementedException(); + } + // When overridden in a derived class, performs enclave attestation, generates a symmetric key for the session, creates a an enclave session and stores the session information in the cache. internal override void CreateEnclaveSession(byte[] attestationInfo, SqlEnclaveAttestationParameters attestationParameters, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength, out SqlEnclaveSession sqlEnclaveSession, out long counter) { diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs index 7bb2bd3f34..9749f2ff3b 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs @@ -194,6 +194,8 @@ protected void UpdateEnclaveSessionLockStatus(SqlEnclaveSession sqlEnclaveSessio } } + protected abstract SqlEnclaveSession CreateEnclaveSessionCore(byte[] enclaveAttestationInfo, SqlEnclaveAttestationParameters attestationParameters, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength); + // Helper method to remove the enclave session from the cache protected void InvalidateEnclaveSessionHelper(EnclaveSessionParameters enclaveSessionParameters, SqlEnclaveSession enclaveSessionToInvalidate) { diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/NoneAttestationEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/NoneAttestationEnclaveProvider.cs index 034cc9fba8..05d78fc44f 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/NoneAttestationEnclaveProvider.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/NoneAttestationEnclaveProvider.cs @@ -30,6 +30,16 @@ internal override SqlEnclaveAttestationParameters GetAttestationParameters(strin return new SqlEnclaveAttestationParameters(NoneAttestationProtocolId, Array.Empty(), clientDHKey); } + protected override SqlEnclaveSession CreateEnclaveSessionCore( + byte[] enclaveAttestationInfo, + SqlEnclaveAttestationParameters attestationParameters, + EnclaveSessionParameters enclaveSessionParameters, + byte[] customData, + int customDataLength) + { + throw new NotImplementedException(); + } + // When overridden in a derived class, performs enclave attestation, generates a symmetric key for the session, creates an enclave session and stores the session information in the cache. internal override void CreateEnclaveSession(byte[] attestationInfo, SqlEnclaveAttestationParameters attestationParameters, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength, out SqlEnclaveSession sqlEnclaveSession, out long counter) { diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs index 966c94225e..4ff50097e8 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs @@ -100,6 +100,16 @@ internal override SqlEnclaveAttestationParameters GetAttestationParameters(strin return new SqlEnclaveAttestationParameters(VsmHGSProtocolId, Array.Empty(), clientDHKey); } + protected override SqlEnclaveSession CreateEnclaveSessionCore( + byte[] enclaveAttestationInfo, + SqlEnclaveAttestationParameters attestationParameters, + EnclaveSessionParameters enclaveSessionParameters, + byte[] customData, + int customDataLength) + { + throw new NotImplementedException(); + } + // When overridden in a derived class, performs enclave attestation, generates a symmetric key for the session, creates a an enclave session and stores the session information in the cache. internal override void CreateEnclaveSession(byte[] attestationInfo, SqlEnclaveAttestationParameters attestationParameters, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength, out SqlEnclaveSession sqlEnclaveSession, out long counter) { From 52104c199e32b7b120c80f11eac518155c1311aa Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Thu, 9 Jul 2026 06:52:45 +0100 Subject: [PATCH 04/10] Move core logic to CreateEnclaveSessionCore --- .../AzureAttestationBasedEnclaveProvider.cs | 48 +++++------ .../NoneAttestationEnclaveProvider.cs | 81 ++++++++++--------- .../VirtualSecureModeEnclaveProviderBase.cs | 45 ++++++----- 3 files changed, 88 insertions(+), 86 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs index 4760b99477..8e38347d74 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs @@ -86,7 +86,27 @@ protected override SqlEnclaveSession CreateEnclaveSessionCore( byte[] customData, int customDataLength) { - throw new NotImplementedException(); + if (!string.IsNullOrEmpty(enclaveSessionParameters.AttestationUrl) && customData != null && customDataLength > 0) + { + byte[] nonce = customData; + + IdentityModelEventSource.ShowPII = true; + + // Deserialize the payload + AzureAttestationInfo attestInfo = new AzureAttestationInfo(enclaveAttestationInfo); + + // Validate the attestation info + VerifyAzureAttestationInfo(enclaveSessionParameters.AttestationUrl, attestInfo.EnclaveType, attestInfo.AttestationToken.AttestationToken, attestInfo.Identity, nonce); + + // Set up shared secret and validate signature + byte[] sharedSecret = GetSharedSecret(attestInfo.Identity, nonce, attestInfo.EnclaveType, attestInfo.EnclaveDHInfo, attestationParameters.ClientDiffieHellmanKey); + + return new SqlEnclaveSession(sharedSecret, attestInfo.SessionId); + } + else + { + throw SQL.AttestationFailed(Strings.FailToCreateEnclaveSession); + } } // When overridden in a derived class, performs enclave attestation, generates a symmetric key for the session, creates a an enclave session and stores the session information in the cache. @@ -100,29 +120,9 @@ internal override void CreateEnclaveSession(byte[] attestationInfo, SqlEnclaveAt sqlEnclaveSession = GetEnclaveSessionFromCache(enclaveSessionParameters, out counter); if (sqlEnclaveSession == null) { - if (!string.IsNullOrEmpty(enclaveSessionParameters.AttestationUrl) && customData != null && customDataLength > 0) - { - byte[] nonce = customData; - - IdentityModelEventSource.ShowPII = true; - - // Deserialize the payload - AzureAttestationInfo attestInfo = new AzureAttestationInfo(attestationInfo); - - // Validate the attestation info - VerifyAzureAttestationInfo(enclaveSessionParameters.AttestationUrl, attestInfo.EnclaveType, attestInfo.AttestationToken.AttestationToken, attestInfo.Identity, nonce); - - // Set up shared secret and validate signature - byte[] sharedSecret = GetSharedSecret(attestInfo.Identity, nonce, attestInfo.EnclaveType, attestInfo.EnclaveDHInfo, attestationParameters.ClientDiffieHellmanKey); - - // add session to cache - sqlEnclaveSession = new SqlEnclaveSession(sharedSecret, attestInfo.SessionId); - AddEnclaveSessionToCache(enclaveSessionParameters, sqlEnclaveSession, out counter); - } - else - { - throw SQL.AttestationFailed(Strings.FailToCreateEnclaveSession); - } + // Add session to cache + sqlEnclaveSession = CreateEnclaveSessionCore(attestationInfo, attestationParameters, enclaveSessionParameters, customData, customDataLength); + AddEnclaveSessionToCache(enclaveSessionParameters, sqlEnclaveSession, out counter); } } finally diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/NoneAttestationEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/NoneAttestationEnclaveProvider.cs index 05d78fc44f..8b880a3815 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/NoneAttestationEnclaveProvider.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/NoneAttestationEnclaveProvider.cs @@ -37,15 +37,50 @@ protected override SqlEnclaveSession CreateEnclaveSessionCore( byte[] customData, int customDataLength) { - throw new NotImplementedException(); + // For None attestation: enclave does not send public key, and sends an empty attestation info. + // The only non-trivial content it sends is the session setup info (DH pubkey of enclave.) + + // Read AttestationInfo + int attestationInfoOffset = 0; + uint sizeOfTrustedModuleAttestationInfoBuffer = BitConverter.ToUInt32(enclaveAttestationInfo, attestationInfoOffset); + attestationInfoOffset += sizeof(UInt32); + int sizeOfTrustedModuleAttestationInfoBufferInt = checked((int)sizeOfTrustedModuleAttestationInfoBuffer); + Debug.Assert(sizeOfTrustedModuleAttestationInfoBuffer == 0); + + // read secure session info + uint sizeOfSecureSessionInfoResponse = BitConverter.ToUInt32(enclaveAttestationInfo, attestationInfoOffset); + attestationInfoOffset += sizeof(UInt32); + + byte[] enclaveSessionHandle = new byte[EnclaveSessionHandleSize]; + Buffer.BlockCopy(enclaveAttestationInfo, attestationInfoOffset, enclaveSessionHandle, 0, EnclaveSessionHandleSize); + attestationInfoOffset += EnclaveSessionHandleSize; + + uint sizeOfTrustedModuleDHPublicKeyBuffer = BitConverter.ToUInt32(enclaveAttestationInfo, attestationInfoOffset); + attestationInfoOffset += sizeof(UInt32); + uint sizeOfTrustedModuleDHPublicKeySignatureBuffer = BitConverter.ToUInt32(enclaveAttestationInfo, attestationInfoOffset); + attestationInfoOffset += sizeof(UInt32); + int sizeOfTrustedModuleDHPublicKeyBufferInt = checked((int)sizeOfTrustedModuleDHPublicKeyBuffer); + + byte[] trustedModuleDHPublicKey = new byte[sizeOfTrustedModuleDHPublicKeyBuffer]; + Buffer.BlockCopy(enclaveAttestationInfo, attestationInfoOffset, trustedModuleDHPublicKey, 0, + sizeOfTrustedModuleDHPublicKeyBufferInt); + attestationInfoOffset += sizeOfTrustedModuleDHPublicKeyBufferInt; + + byte[] trustedModuleDHPublicKeySignature = new byte[sizeOfTrustedModuleDHPublicKeySignatureBuffer]; + Buffer.BlockCopy(enclaveAttestationInfo, attestationInfoOffset, trustedModuleDHPublicKeySignature, 0, + checked((int)sizeOfTrustedModuleDHPublicKeySignatureBuffer)); + + byte[] sharedSecret; + using ECDiffieHellman ecdh = KeyConverter.CreateECDiffieHellmanFromPublicKeyBlob(trustedModuleDHPublicKey); + sharedSecret = KeyConverter.DeriveKey(attestationParameters.ClientDiffieHellmanKey, ecdh.PublicKey); + long sessionId = BitConverter.ToInt64(enclaveSessionHandle, 0); + + return new SqlEnclaveSession(sharedSecret, sessionId); } // When overridden in a derived class, performs enclave attestation, generates a symmetric key for the session, creates an enclave session and stores the session information in the cache. internal override void CreateEnclaveSession(byte[] attestationInfo, SqlEnclaveAttestationParameters attestationParameters, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength, out SqlEnclaveSession sqlEnclaveSession, out long counter) { - // for None attestation: enclave does not send public key, and sends an empty attestation info - // The only non-trivial content it sends is the session setup info (DH pubkey of enclave) - sqlEnclaveSession = null; counter = 0; try @@ -55,42 +90,8 @@ internal override void CreateEnclaveSession(byte[] attestationInfo, SqlEnclaveAt if (sqlEnclaveSession == null) { - // Read AttestationInfo - int attestationInfoOffset = 0; - uint sizeOfTrustedModuleAttestationInfoBuffer = BitConverter.ToUInt32(attestationInfo, attestationInfoOffset); - attestationInfoOffset += sizeof(UInt32); - int sizeOfTrustedModuleAttestationInfoBufferInt = checked((int)sizeOfTrustedModuleAttestationInfoBuffer); - Debug.Assert(sizeOfTrustedModuleAttestationInfoBuffer == 0); - - // read secure session info - uint sizeOfSecureSessionInfoResponse = BitConverter.ToUInt32(attestationInfo, attestationInfoOffset); - attestationInfoOffset += sizeof(UInt32); - - byte[] enclaveSessionHandle = new byte[EnclaveSessionHandleSize]; - Buffer.BlockCopy(attestationInfo, attestationInfoOffset, enclaveSessionHandle, 0, EnclaveSessionHandleSize); - attestationInfoOffset += EnclaveSessionHandleSize; - - uint sizeOfTrustedModuleDHPublicKeyBuffer = BitConverter.ToUInt32(attestationInfo, attestationInfoOffset); - attestationInfoOffset += sizeof(UInt32); - uint sizeOfTrustedModuleDHPublicKeySignatureBuffer = BitConverter.ToUInt32(attestationInfo, attestationInfoOffset); - attestationInfoOffset += sizeof(UInt32); - int sizeOfTrustedModuleDHPublicKeyBufferInt = checked((int)sizeOfTrustedModuleDHPublicKeyBuffer); - - byte[] trustedModuleDHPublicKey = new byte[sizeOfTrustedModuleDHPublicKeyBuffer]; - Buffer.BlockCopy(attestationInfo, attestationInfoOffset, trustedModuleDHPublicKey, 0, - sizeOfTrustedModuleDHPublicKeyBufferInt); - attestationInfoOffset += sizeOfTrustedModuleDHPublicKeyBufferInt; - - byte[] trustedModuleDHPublicKeySignature = new byte[sizeOfTrustedModuleDHPublicKeySignatureBuffer]; - Buffer.BlockCopy(attestationInfo, attestationInfoOffset, trustedModuleDHPublicKeySignature, 0, - checked((int)sizeOfTrustedModuleDHPublicKeySignatureBuffer)); - - byte[] sharedSecret; - using ECDiffieHellman ecdh = KeyConverter.CreateECDiffieHellmanFromPublicKeyBlob(trustedModuleDHPublicKey); - sharedSecret = KeyConverter.DeriveKey(attestationParameters.ClientDiffieHellmanKey, ecdh.PublicKey); - long sessionId = BitConverter.ToInt64(enclaveSessionHandle, 0); - - sqlEnclaveSession = new SqlEnclaveSession(sharedSecret, sessionId); + sqlEnclaveSession = CreateEnclaveSessionCore(attestationInfo, attestationParameters, enclaveSessionParameters, customData, customDataLength); + AddEnclaveSessionToCache(enclaveSessionParameters, sqlEnclaveSession, out counter); } } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs index 4ff50097e8..76c8be9ecb 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs @@ -107,7 +107,27 @@ protected override SqlEnclaveSession CreateEnclaveSessionCore( byte[] customData, int customDataLength) { - throw new NotImplementedException(); + if (!string.IsNullOrEmpty(enclaveSessionParameters.AttestationUrl)) + { + // Deserialize the payload + AttestationInfo info = new AttestationInfo(enclaveAttestationInfo); + + // Verify enclave policy matches expected policy + VerifyEnclavePolicy(info.EnclaveReportPackage); + + // Perform Attestation per VSM protocol + VerifyAttestationInfo(enclaveSessionParameters.AttestationUrl, info.HealthReport, info.EnclaveReportPackage); + + // Set up shared secret and validate signature + byte[] sharedSecret = GetSharedSecret(info.Identity, info.EnclaveDHInfo, attestationParameters.ClientDiffieHellmanKey); + + // add session to cache + return new SqlEnclaveSession(sharedSecret, info.SessionId); + } + else + { + throw SQL.AttestationFailed(Strings.FailToCreateEnclaveSession); + } } // When overridden in a derived class, performs enclave attestation, generates a symmetric key for the session, creates a an enclave session and stores the session information in the cache. @@ -121,28 +141,9 @@ internal override void CreateEnclaveSession(byte[] attestationInfo, SqlEnclaveAt sqlEnclaveSession = GetEnclaveSessionFromCache(enclaveSessionParameters, out counter); if (sqlEnclaveSession == null) { - if (!string.IsNullOrEmpty(enclaveSessionParameters.AttestationUrl)) - { - // Deserialize the payload - AttestationInfo info = new AttestationInfo(attestationInfo); - - // Verify enclave policy matches expected policy - VerifyEnclavePolicy(info.EnclaveReportPackage); - - // Perform Attestation per VSM protocol - VerifyAttestationInfo(enclaveSessionParameters.AttestationUrl, info.HealthReport, info.EnclaveReportPackage); - - // Set up shared secret and validate signature - byte[] sharedSecret = GetSharedSecret(info.Identity, info.EnclaveDHInfo, attestationParameters.ClientDiffieHellmanKey); + sqlEnclaveSession = CreateEnclaveSessionCore(attestationInfo, attestationParameters, enclaveSessionParameters, customData, customDataLength); - // add session to cache - sqlEnclaveSession = new SqlEnclaveSession(sharedSecret, info.SessionId); - AddEnclaveSessionToCache(enclaveSessionParameters, sqlEnclaveSession, out counter); - } - else - { - throw SQL.AttestationFailed(Strings.FailToCreateEnclaveSession); - } + AddEnclaveSessionToCache(enclaveSessionParameters, sqlEnclaveSession, out counter); } } finally From 827c55186cf2e76f136dc68ccb97cde5a526f516 Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Thu, 9 Jul 2026 06:57:07 +0100 Subject: [PATCH 05/10] Move CreateEnclaveSession to base class --- .../AzureAttestationBasedEnclaveProvider.cs | 26 ------------------- .../Data/SqlClient/EnclaveProviderBase.cs | 26 +++++++++++++++++++ .../NoneAttestationEnclaveProvider.cs | 23 ---------------- .../VirtualSecureModeEnclaveProviderBase.cs | 22 ---------------- 4 files changed, 26 insertions(+), 71 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs index 8e38347d74..de6ffa000b 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs @@ -109,32 +109,6 @@ protected override SqlEnclaveSession CreateEnclaveSessionCore( } } - // When overridden in a derived class, performs enclave attestation, generates a symmetric key for the session, creates a an enclave session and stores the session information in the cache. - internal override void CreateEnclaveSession(byte[] attestationInfo, SqlEnclaveAttestationParameters attestationParameters, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength, out SqlEnclaveSession sqlEnclaveSession, out long counter) - { - sqlEnclaveSession = null; - counter = 0; - try - { - ThreadRetryCache.Remove(Thread.CurrentThread.ManagedThreadId.ToString()); - sqlEnclaveSession = GetEnclaveSessionFromCache(enclaveSessionParameters, out counter); - if (sqlEnclaveSession == null) - { - // Add session to cache - sqlEnclaveSession = CreateEnclaveSessionCore(attestationInfo, attestationParameters, enclaveSessionParameters, customData, customDataLength); - AddEnclaveSessionToCache(enclaveSessionParameters, sqlEnclaveSession, out counter); - } - } - finally - { - // As per current design, we want to minimize the number of create session calls. To achieve this we block all the GetEnclaveSession calls until the first call to - // GetEnclaveSession -> GetAttestationParameters -> CreateEnclaveSession completes or the event timeout happen. - // Case 1: When the first request successfully creates the session, then all outstanding GetEnclaveSession will use the current session. - // Case 2: When the first request unable to create the enclave session (may be due to some error or the first request doesn't require enclave computation) then in those case we set the event timeout to 0. - UpdateEnclaveSessionLockStatus(sqlEnclaveSession); - } - } - // When overridden in a derived class, looks up and evicts an enclave session from the enclave session cache, if the provider implements session caching. internal override void InvalidateEnclaveSession(EnclaveSessionParameters enclaveSessionParameters, SqlEnclaveSession enclaveSessionToInvalidate) { diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs index 9749f2ff3b..e338b1905c 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs @@ -173,6 +173,32 @@ protected void GetEnclaveSessionHelper(EnclaveSessionParameters enclaveSessionPa } } + // When overridden in a derived class, performs enclave attestation, generates a symmetric key for the session, creates a an enclave session and stores the session information in the cache. + internal override void CreateEnclaveSession(byte[] attestationInfo, SqlEnclaveAttestationParameters attestationParameters, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength, out SqlEnclaveSession sqlEnclaveSession, out long counter) + { + sqlEnclaveSession = null; + counter = 0; + try + { + ThreadRetryCache.Remove(Thread.CurrentThread.ManagedThreadId.ToString()); + sqlEnclaveSession = GetEnclaveSessionFromCache(enclaveSessionParameters, out counter); + if (sqlEnclaveSession == null) + { + // Add session to cache + sqlEnclaveSession = CreateEnclaveSessionCore(attestationInfo, attestationParameters, enclaveSessionParameters, customData, customDataLength); + AddEnclaveSessionToCache(enclaveSessionParameters, sqlEnclaveSession, out counter); + } + } + finally + { + // As per current design, we want to minimize the number of create session calls. To achieve this we block all the GetEnclaveSession calls until the first call to + // GetEnclaveSession -> GetAttestationParameters -> CreateEnclaveSession completes or the event timeout happen. + // Case 1: When the first request successfully creates the session, then all outstanding GetEnclaveSession will use the current session. + // Case 2: When the first request unable to create the enclave session (may be due to some error or the first request doesn't require enclave computation) then in those case we set the event timeout to 0. + UpdateEnclaveSessionLockStatus(sqlEnclaveSession); + } + } + // Reset the session lock status protected void UpdateEnclaveSessionLockStatus(SqlEnclaveSession sqlEnclaveSession) { diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/NoneAttestationEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/NoneAttestationEnclaveProvider.cs index 8b880a3815..05a7f7f7ae 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/NoneAttestationEnclaveProvider.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/NoneAttestationEnclaveProvider.cs @@ -78,29 +78,6 @@ protected override SqlEnclaveSession CreateEnclaveSessionCore( return new SqlEnclaveSession(sharedSecret, sessionId); } - // When overridden in a derived class, performs enclave attestation, generates a symmetric key for the session, creates an enclave session and stores the session information in the cache. - internal override void CreateEnclaveSession(byte[] attestationInfo, SqlEnclaveAttestationParameters attestationParameters, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength, out SqlEnclaveSession sqlEnclaveSession, out long counter) - { - sqlEnclaveSession = null; - counter = 0; - try - { - ThreadRetryCache.Remove(Thread.CurrentThread.ManagedThreadId.ToString()); - sqlEnclaveSession = GetEnclaveSessionFromCache(enclaveSessionParameters, out counter); - - if (sqlEnclaveSession == null) - { - sqlEnclaveSession = CreateEnclaveSessionCore(attestationInfo, attestationParameters, enclaveSessionParameters, customData, customDataLength); - - AddEnclaveSessionToCache(enclaveSessionParameters, sqlEnclaveSession, out counter); - } - } - finally - { - UpdateEnclaveSessionLockStatus(sqlEnclaveSession); - } - } - /// /// When overridden in a derived class, looks up and evicts an enclave session from the enclave session cache, if the provider implements session caching. /// diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs index 76c8be9ecb..4e5eeb3934 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs @@ -130,28 +130,6 @@ protected override SqlEnclaveSession CreateEnclaveSessionCore( } } - // When overridden in a derived class, performs enclave attestation, generates a symmetric key for the session, creates a an enclave session and stores the session information in the cache. - internal override void CreateEnclaveSession(byte[] attestationInfo, SqlEnclaveAttestationParameters attestationParameters, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength, out SqlEnclaveSession sqlEnclaveSession, out long counter) - { - sqlEnclaveSession = null; - counter = 0; - try - { - ThreadRetryCache.Remove(Thread.CurrentThread.ManagedThreadId.ToString()); - sqlEnclaveSession = GetEnclaveSessionFromCache(enclaveSessionParameters, out counter); - if (sqlEnclaveSession == null) - { - sqlEnclaveSession = CreateEnclaveSessionCore(attestationInfo, attestationParameters, enclaveSessionParameters, customData, customDataLength); - - AddEnclaveSessionToCache(enclaveSessionParameters, sqlEnclaveSession, out counter); - } - } - finally - { - UpdateEnclaveSessionLockStatus(sqlEnclaveSession); - } - } - // When overridden in a derived class, looks up and evicts an enclave session from the enclave session cache, if the provider implements session caching. internal override void InvalidateEnclaveSession(EnclaveSessionParameters enclaveSessionParameters, SqlEnclaveSession enclaveSessionToInvalidate) { From bf1f48df75ce99b5225604e6228268b0c9c81fec Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Thu, 9 Jul 2026 19:54:38 +0100 Subject: [PATCH 06/10] Move InvalidateEnclaveSession to base class --- .../SqlClient/AzureAttestationBasedEnclaveProvider.cs | 6 ------ .../Microsoft/Data/SqlClient/EnclaveProviderBase.cs | 5 +++++ .../Data/SqlClient/NoneAttestationEnclaveProvider.cs | 10 ---------- .../SqlClient/VirtualSecureModeEnclaveProviderBase.cs | 7 ------- 4 files changed, 5 insertions(+), 23 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs index de6ffa000b..446ec03032 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs @@ -108,12 +108,6 @@ protected override SqlEnclaveSession CreateEnclaveSessionCore( throw SQL.AttestationFailed(Strings.FailToCreateEnclaveSession); } } - - // When overridden in a derived class, looks up and evicts an enclave session from the enclave session cache, if the provider implements session caching. - internal override void InvalidateEnclaveSession(EnclaveSessionParameters enclaveSessionParameters, SqlEnclaveSession enclaveSessionToInvalidate) - { - InvalidateEnclaveSessionHelper(enclaveSessionParameters, enclaveSessionToInvalidate); - } #endregion #region Internal Class diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs index e338b1905c..fbb62476a2 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs @@ -199,6 +199,11 @@ internal override void CreateEnclaveSession(byte[] attestationInfo, SqlEnclaveAt } } + internal override void InvalidateEnclaveSession(EnclaveSessionParameters enclaveSessionParameters, SqlEnclaveSession enclaveSessionToInvalidate) + { + InvalidateEnclaveSessionHelper(enclaveSessionParameters, enclaveSessionToInvalidate); + } + // Reset the session lock status protected void UpdateEnclaveSessionLockStatus(SqlEnclaveSession sqlEnclaveSession) { diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/NoneAttestationEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/NoneAttestationEnclaveProvider.cs index 05a7f7f7ae..aebb160fd9 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/NoneAttestationEnclaveProvider.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/NoneAttestationEnclaveProvider.cs @@ -77,15 +77,5 @@ protected override SqlEnclaveSession CreateEnclaveSessionCore( return new SqlEnclaveSession(sharedSecret, sessionId); } - - /// - /// When overridden in a derived class, looks up and evicts an enclave session from the enclave session cache, if the provider implements session caching. - /// - /// The set of parameters required for enclave session. - /// The session to be invalidated. - internal override void InvalidateEnclaveSession(EnclaveSessionParameters enclaveSessionParameters, SqlEnclaveSession enclaveSessionToInvalidate) - { - InvalidateEnclaveSessionHelper(enclaveSessionParameters, enclaveSessionToInvalidate); - } } } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs index 4e5eeb3934..621b8e9f27 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs @@ -129,13 +129,6 @@ protected override SqlEnclaveSession CreateEnclaveSessionCore( throw SQL.AttestationFailed(Strings.FailToCreateEnclaveSession); } } - - // When overridden in a derived class, looks up and evicts an enclave session from the enclave session cache, if the provider implements session caching. - internal override void InvalidateEnclaveSession(EnclaveSessionParameters enclaveSessionParameters, SqlEnclaveSession enclaveSessionToInvalidate) - { - InvalidateEnclaveSessionHelper(enclaveSessionParameters, enclaveSessionToInvalidate); - } - #endregion #region Private helpers From bca4325fe5503f22d01702b76f79d99937749155 Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Thu, 9 Jul 2026 20:41:48 +0100 Subject: [PATCH 07/10] Remove single-use helper methods These are not used consistently, even within the same method; remove the layer of indirection --- .../Data/SqlClient/EnclaveProviderBase.cs | 23 +++---------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs index fbb62476a2..5ddc303b00 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs @@ -181,12 +181,12 @@ internal override void CreateEnclaveSession(byte[] attestationInfo, SqlEnclaveAt try { ThreadRetryCache.Remove(Thread.CurrentThread.ManagedThreadId.ToString()); - sqlEnclaveSession = GetEnclaveSessionFromCache(enclaveSessionParameters, out counter); + sqlEnclaveSession = SessionCache.GetEnclaveSession(enclaveSessionParameters, out counter); if (sqlEnclaveSession == null) { // Add session to cache sqlEnclaveSession = CreateEnclaveSessionCore(attestationInfo, attestationParameters, enclaveSessionParameters, customData, customDataLength); - AddEnclaveSessionToCache(enclaveSessionParameters, sqlEnclaveSession, out counter); + SessionCache.CreateSession(enclaveSessionParameters, sqlEnclaveSession, out counter); } } finally @@ -201,7 +201,7 @@ internal override void CreateEnclaveSession(byte[] attestationInfo, SqlEnclaveAt internal override void InvalidateEnclaveSession(EnclaveSessionParameters enclaveSessionParameters, SqlEnclaveSession enclaveSessionToInvalidate) { - InvalidateEnclaveSessionHelper(enclaveSessionParameters, enclaveSessionToInvalidate); + SessionCache.InvalidateSession(enclaveSessionParameters, enclaveSessionToInvalidate); } // Reset the session lock status @@ -227,23 +227,6 @@ protected void UpdateEnclaveSessionLockStatus(SqlEnclaveSession sqlEnclaveSessio protected abstract SqlEnclaveSession CreateEnclaveSessionCore(byte[] enclaveAttestationInfo, SqlEnclaveAttestationParameters attestationParameters, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength); - // Helper method to remove the enclave session from the cache - protected void InvalidateEnclaveSessionHelper(EnclaveSessionParameters enclaveSessionParameters, SqlEnclaveSession enclaveSessionToInvalidate) - { - SessionCache.InvalidateSession(enclaveSessionParameters, enclaveSessionToInvalidate); - } - - // Helper method for getting the enclave session from the session cache - protected SqlEnclaveSession GetEnclaveSessionFromCache(EnclaveSessionParameters enclaveSessionParameters, out long counter) - { - return SessionCache.GetEnclaveSession(enclaveSessionParameters, out counter); - } - - // Helper method for adding the enclave session to the session cache - protected void AddEnclaveSessionToCache(EnclaveSessionParameters enclaveSessionParameters, SqlEnclaveSession enclaveSession, out long counter) - { - SessionCache.CreateSession(enclaveSessionParameters, enclaveSession, out counter); - } } #endregion } From d0e06d93601048bd4f97e3e52473793d57c29c8c Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Thu, 9 Jul 2026 20:44:13 +0100 Subject: [PATCH 08/10] Narrow visibility of UpdateEnclaveSessionLockStatus --- .../src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs index 5ddc303b00..65404212f1 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs @@ -205,7 +205,7 @@ internal override void InvalidateEnclaveSession(EnclaveSessionParameters enclave } // Reset the session lock status - protected void UpdateEnclaveSessionLockStatus(SqlEnclaveSession sqlEnclaveSession) + private void UpdateEnclaveSessionLockStatus(SqlEnclaveSession sqlEnclaveSession) { // As per current design, we want to minimize the number of create session calls. To achieve this we block all the GetEnclaveSession calls until the first call to // GetEnclaveSession -> GetAttestationParameters -> CreateEnclaveSession completes or the event timeout happens. @@ -226,7 +226,6 @@ protected void UpdateEnclaveSessionLockStatus(SqlEnclaveSession sqlEnclaveSessio } protected abstract SqlEnclaveSession CreateEnclaveSessionCore(byte[] enclaveAttestationInfo, SqlEnclaveAttestationParameters attestationParameters, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength); - } #endregion } From 962a791cac107a9dfd82d1f0e14ab182a12e1e11 Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Thu, 9 Jul 2026 20:46:13 +0100 Subject: [PATCH 09/10] Reorder methods and update comments --- .../src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs | 7 ++++--- .../src/Microsoft/Data/SqlClient/EnclaveSessionCache.cs | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs index 65404212f1..ae3c39f66d 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs @@ -88,6 +88,9 @@ internal abstract class EnclaveProviderBase : SqlColumnEncryptionEnclaveProvider #endregion #region protected methods + // When overridden in a derived class, performs enclave attestation, generates a symmetric key for the session and creates an enclave session. + protected abstract SqlEnclaveSession CreateEnclaveSessionCore(byte[] enclaveAttestationInfo, SqlEnclaveAttestationParameters attestationParameters, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength); + // Helper method to get the enclave session from the cache if present protected void GetEnclaveSessionHelper(EnclaveSessionParameters enclaveSessionParameters, bool shouldGenerateNonce, bool isRetry, out SqlEnclaveSession sqlEnclaveSession, out long counter, out byte[] customData, out int customDataLength) { @@ -173,7 +176,7 @@ protected void GetEnclaveSessionHelper(EnclaveSessionParameters enclaveSessionPa } } - // When overridden in a derived class, performs enclave attestation, generates a symmetric key for the session, creates a an enclave session and stores the session information in the cache. + // When overridden in a derived class, calls CreateEnclaveSessionCore to create an enclave session and stores the session information in the cache. internal override void CreateEnclaveSession(byte[] attestationInfo, SqlEnclaveAttestationParameters attestationParameters, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength, out SqlEnclaveSession sqlEnclaveSession, out long counter) { sqlEnclaveSession = null; @@ -224,8 +227,6 @@ private void UpdateEnclaveSessionLockStatus(SqlEnclaveSession sqlEnclaveSession) } } } - - protected abstract SqlEnclaveSession CreateEnclaveSessionCore(byte[] enclaveAttestationInfo, SqlEnclaveAttestationParameters attestationParameters, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength); } #endregion } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveSessionCache.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveSessionCache.cs index f64647d232..6dfeee8ba7 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveSessionCache.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveSessionCache.cs @@ -54,7 +54,7 @@ internal void InvalidateSession(EnclaveSessionParameters enclaveSessionParameter } } - // Creates a new SqlEnclaveSession and adds it to the cache + // Adds a new SqlEnclaveSession to the cache internal void CreateSession(EnclaveSessionParameters enclaveSessionParameters, SqlEnclaveSession enclaveSession, out long counter) { string cacheKey = GenerateCacheKey(enclaveSessionParameters); From 3fa3182341cef7c20b93fa34145ef67a1a7bda45 Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Sat, 11 Jul 2026 01:06:51 +0100 Subject: [PATCH 10/10] Cleanup comments/usings --- .../src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs | 2 +- .../Data/SqlClient/SqlColumnEncryptionEnclaveProvider.cs | 2 -- .../Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs index ae3c39f66d..be5ecd743c 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs @@ -176,7 +176,7 @@ protected void GetEnclaveSessionHelper(EnclaveSessionParameters enclaveSessionPa } } - // When overridden in a derived class, calls CreateEnclaveSessionCore to create an enclave session and stores the session information in the cache. + // Calls CreateEnclaveSessionCore to create an enclave session and stores the session information in the cache. internal override void CreateEnclaveSession(byte[] attestationInfo, SqlEnclaveAttestationParameters attestationParameters, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength, out SqlEnclaveSession sqlEnclaveSession, out long counter) { sqlEnclaveSession = null; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.cs index 2079097233..265d262193 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Security.Cryptography; - namespace Microsoft.Data.SqlClient { /// diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs index 621b8e9f27..03ad23c83a 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs @@ -121,7 +121,6 @@ protected override SqlEnclaveSession CreateEnclaveSessionCore( // Set up shared secret and validate signature byte[] sharedSecret = GetSharedSecret(info.Identity, info.EnclaveDHInfo, attestationParameters.ClientDiffieHellmanKey); - // add session to cache return new SqlEnclaveSession(sharedSecret, info.SessionId); } else