From f9062480f3caa0f9dc4db152e5735f5ccf23cc9a Mon Sep 17 00:00:00 2001 From: v-arellegue Date: Wed, 1 May 2024 17:06:36 -0700 Subject: [PATCH 01/16] Replaced System.Runtim.Caching with Microsoft.Extensions.Caching. --- ...waysEncrypted.AzureKeyVaultProvider.csproj | 2 +- .../ref/Microsoft.Data.SqlClient.csproj | 1 + .../src/Microsoft.Data.SqlClient.csproj | 3 +-- .../netfx/ref/Microsoft.Data.SqlClient.csproj | 1 + .../netfx/src/Microsoft.Data.SqlClient.csproj | 1 + .../ActiveDirectoryAuthenticationProvider.cs | 14 +++++++++---- .../AzureAttestationBasedEnclaveProvider.cs | 12 +++++++---- .../Data/SqlClient/EnclaveProviderBase.cs | 12 +++++++---- .../Data/SqlClient/EnclaveSessionCache.cs | 20 +++++++++++++------ .../SqlClient/SignatureVerificationCache.cs | 18 +++++++++++------ .../Data/SqlClient/SqlQueryMetadataCache.cs | 20 +++++++++++-------- .../Data/SqlClient/SqlSymmetricKeyCache.cs | 14 ++++++++----- .../VirtualSecureModeEnclaveProviderBase.cs | 14 ++++++++----- .../TestFixtures/Setup/CertificateUtility.cs | 11 +++------- 14 files changed, 90 insertions(+), 53 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.csproj b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.csproj index eb08db41c2..84f1cf83eb 100644 --- a/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.csproj +++ b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.csproj @@ -32,6 +32,6 @@ - + diff --git a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj index 9ab6b8d80f..95b1aa2892 100644 --- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj @@ -27,6 +27,7 @@ + diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj index 122fcaa089..054e444fa3 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -975,14 +975,13 @@ - - + diff --git a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj index 19e1e5c7b6..0ad0d67a72 100644 --- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj @@ -16,6 +16,7 @@ + diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index 2dd8d69b08..0fd511889b 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -738,6 +738,7 @@ + $(SystemTextEncodingsWebVersion) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationProvider.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationProvider.cs index 01f265cf50..b4cce18bae 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationProvider.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationProvider.cs @@ -5,13 +5,13 @@ using System; using System.Collections.Concurrent; using System.Linq; -using System.Runtime.Caching; using System.Security.Cryptography; using System.Text; using System.Threading; using System.Threading.Tasks; using Azure.Core; using Azure.Identity; +using Microsoft.Extensions.Caching.Memory; using Microsoft.Identity.Client; using Microsoft.Identity.Client.Extensibility; @@ -27,7 +27,7 @@ public sealed class ActiveDirectoryAuthenticationProvider : SqlAuthenticationPro /// private static ConcurrentDictionary s_pcaMap = new ConcurrentDictionary(); - private static readonly MemoryCache s_accountPwCache = new(nameof(ActiveDirectoryAuthenticationProvider)); + private static readonly MemoryCache s_accountPwCache = new MemoryCache(new MemoryCacheOptions()); private static readonly int s_accountPwCacheTtlInHours = 2; private static readonly string s_nativeClientRedirectUri = "https://login.microsoftonline.com/common/oauth2/nativeclient"; private static readonly string s_defaultScopeSuffix = "/.default"; @@ -280,11 +280,17 @@ previousPw is byte[] previousPwBytes && // We cache the password hash to ensure future connection requests include a validated password // when we check for a cached MSAL account. Otherwise, a connection request with the same username // against the same tenant could succeed with an invalid password when we re-use the cached token. - if (!s_accountPwCache.Add(pwCacheKey, GetHash(parameters.Password), DateTime.UtcNow.AddHours(s_accountPwCacheTtlInHours))) + var options = new MemoryCacheEntryOptions + { + AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(s_accountPwCacheTtlInHours) + }; + // Remove old cached password hash if there is one + if (s_accountPwCache.TryGetValue(pwCacheKey, out string key)) { s_accountPwCache.Remove(pwCacheKey); - s_accountPwCache.Add(pwCacheKey, GetHash(parameters.Password), DateTime.UtcNow.AddHours(s_accountPwCacheTtlInHours)); } + // Add current password hash to cache + s_accountPwCache.Set(pwCacheKey, GetHash(parameters.Password), options); SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token for Active Directory Password auth mode. Expiry Time: {0}", result?.ExpiresOn); } 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 70f6bc5f91..b2d8505248 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs @@ -8,11 +8,11 @@ using System.Collections.Generic; using System.Diagnostics; using System.IdentityModel.Tokens.Jwt; -using System.Runtime.Caching; using System.Security.Claims; using System.Security.Cryptography; using System.Text; using System.Threading; +using Microsoft.Extensions.Caching.Memory; using Microsoft.IdentityModel.JsonWebTokens; using Microsoft.IdentityModel.Logging; using Microsoft.IdentityModel.Protocols; @@ -61,7 +61,7 @@ internal class AzureAttestationEnclaveProvider : EnclaveProviderBase // such as https://sql.azure.attest.com/.well-known/openid-configuration private const string AttestationUrlSuffix = @"/.well-known/openid-configuration"; - private static readonly MemoryCache OpenIdConnectConfigurationCache = new MemoryCache("OpenIdConnectConfigurationCache"); + private static readonly MemoryCache OpenIdConnectConfigurationCache = new MemoryCache(new MemoryCacheOptions()); #endregion #region Internal methods @@ -334,7 +334,7 @@ private static string GetInnerMostExceptionMessage(Exception exception) // It also caches that information for 1 day to avoid DDOS attacks. private OpenIdConnectConfiguration GetOpenIdConfigForSigningKeys(string url, bool forceUpdate) { - OpenIdConnectConfiguration openIdConnectConfig = OpenIdConnectConfigurationCache[url] as OpenIdConnectConfiguration; + OpenIdConnectConfiguration openIdConnectConfig = OpenIdConnectConfigurationCache.Get(url); if (forceUpdate || openIdConnectConfig == null) { // Compute the meta data endpoint @@ -350,7 +350,11 @@ private OpenIdConnectConfiguration GetOpenIdConfigForSigningKeys(string url, boo throw SQL.AttestationFailed(string.Format(Strings.GetAttestationTokenSigningKeysFailed, GetInnerMostExceptionMessage(exception)), exception); } - OpenIdConnectConfigurationCache.Add(url, openIdConnectConfig, DateTime.UtcNow.AddDays(1)); + var options = new MemoryCacheEntryOptions + { + AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(1) + }; + OpenIdConnectConfigurationCache.Set(url, openIdConnectConfig, options); } return openIdConnectConfig; 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 1a2bcaeb03..b0e990265f 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs @@ -5,9 +5,9 @@ #if !NETSTANDARD2_0 using System; -using System.Runtime.Caching; using System.Security.Cryptography; using System.Threading; +using Microsoft.Extensions.Caching.Memory; // Enclave session locking model // 1. For doing the enclave attestation, driver makes either 1, 2 or 3 API calls(in order) @@ -86,7 +86,7 @@ internal abstract class EnclaveProviderBase : SqlColumnEncryptionEnclaveProvider private static readonly Object lockUpdateSessionLock = new Object(); // It is used to save the attestation url and nonce value across API calls - protected static readonly MemoryCache ThreadRetryCache = new MemoryCache("ThreadRetryCache"); + protected static readonly MemoryCache ThreadRetryCache = new MemoryCache(new MemoryCacheOptions()); #endregion #region protected methods @@ -104,7 +104,7 @@ protected void GetEnclaveSessionHelper(EnclaveSessionParameters enclaveSessionPa // In case if on some thread we are running SQL workload which don't require attestation, then in those cases we don't want same thread to wait for event to be signaled. // hence skipping it - string retryThreadID = ThreadRetryCache[Thread.CurrentThread.ManagedThreadId.ToString()] as string; + string retryThreadID = ThreadRetryCache.Get(Thread.CurrentThread.ManagedThreadId.ToString()); if (!string.IsNullOrEmpty(retryThreadID)) { sameThreadRetry = true; @@ -169,7 +169,11 @@ protected void GetEnclaveSessionHelper(EnclaveSessionParameters enclaveSessionPa retryThreadID = Thread.CurrentThread.ManagedThreadId.ToString(); } - ThreadRetryCache.Set(Thread.CurrentThread.ManagedThreadId.ToString(), retryThreadID, DateTime.UtcNow.AddMinutes(ThreadRetryCacheTimeoutInMinutes)); + var options = new MemoryCacheEntryOptions + { + AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(ThreadRetryCacheTimeoutInMinutes) + }; + ThreadRetryCache.Set(Thread.CurrentThread.ManagedThreadId.ToString(), retryThreadID, options); } } } 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 d60c3ad5ba..23563ef3c7 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveSessionCache.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveSessionCache.cs @@ -5,7 +5,7 @@ #if !NETSTANDARD2_0 using System; -using System.Runtime.Caching; +using Microsoft.Extensions.Caching.Memory; using System.Threading; namespace Microsoft.Data.SqlClient @@ -13,7 +13,7 @@ namespace Microsoft.Data.SqlClient // Maintains a cache of SqlEnclaveSession instances internal class EnclaveSessionCache { - private readonly MemoryCache enclaveMemoryCache = new MemoryCache("EnclaveMemoryCache"); + private readonly MemoryCache enclaveMemoryCache = new MemoryCache(new MemoryCacheOptions()); private readonly object enclaveCacheLock = new object(); // Nonce for each message sent by the client to the server to prevent replay attacks by the server, @@ -27,7 +27,7 @@ internal class EnclaveSessionCache internal SqlEnclaveSession GetEnclaveSession(EnclaveSessionParameters enclaveSessionParameters, out long counter) { string cacheKey = GenerateCacheKey(enclaveSessionParameters); - SqlEnclaveSession enclaveSession = enclaveMemoryCache[cacheKey] as SqlEnclaveSession; + SqlEnclaveSession enclaveSession = enclaveMemoryCache.Get(cacheKey); counter = Interlocked.Increment(ref _counter); return enclaveSession; } @@ -43,8 +43,12 @@ internal void InvalidateSession(EnclaveSessionParameters enclaveSessionParameter if (enclaveSession != null && enclaveSession.SessionId == enclaveSessionToInvalidate.SessionId) { - SqlEnclaveSession enclaveSessionRemoved = enclaveMemoryCache.Remove(cacheKey) as SqlEnclaveSession; - if (enclaveSessionRemoved == null) + enclaveMemoryCache.TryGetValue(cacheKey, out SqlEnclaveSession enclaveSessionToRemove); + if (enclaveSessionToRemove != null) + { + enclaveMemoryCache.Remove(cacheKey); + } + else { throw new InvalidOperationException(Strings.EnclaveSessionInvalidationFailed); } @@ -60,7 +64,11 @@ internal SqlEnclaveSession CreateSession(EnclaveSessionParameters enclaveSession lock (enclaveCacheLock) { enclaveSession = new SqlEnclaveSession(sharedSecret, sessionId); - enclaveMemoryCache.Add(cacheKey, enclaveSession, DateTime.UtcNow.AddHours(enclaveCacheTimeOutInHours)); + var options = new MemoryCacheEntryOptions + { + AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(enclaveCacheTimeOutInHours) + }; + enclaveMemoryCache.Set(cacheKey, enclaveSession, options); counter = Interlocked.Increment(ref _counter); } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SignatureVerificationCache.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SignatureVerificationCache.cs index c181637c4b..a1b4c733df 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SignatureVerificationCache.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SignatureVerificationCache.cs @@ -3,9 +3,9 @@ // See the LICENSE file in the project root for more information. using System; -using System.Runtime.Caching; using System.Text; using System.Threading; +using Microsoft.Extensions.Caching.Memory; namespace Microsoft.Data.SqlClient { @@ -35,7 +35,7 @@ internal class ColumnMasterKeyMetadataSignatureVerificationCache private ColumnMasterKeyMetadataSignatureVerificationCache() { - _cache = new MemoryCache(_className); + _cache = new MemoryCache(new MemoryCacheOptions()); _inTrim = 0; } @@ -56,7 +56,7 @@ private ColumnMasterKeyMetadataSignatureVerificationCache() string cacheLookupKey = GetCacheLookupKey(masterKeyPath, allowEnclaveComputations, signature, keyStoreName); - return _cache.Get(cacheLookupKey) as bool?; + return _cache.TryGetValue(cacheLookupKey, out bool value); } /// @@ -79,7 +79,11 @@ internal void AddSignatureVerificationResult(string keyStoreName, string masterK TrimCacheIfNeeded(); // By default evict after 10 days. - _cache.Set(cacheLookupKey, result, DateTimeOffset.UtcNow.AddDays(10)); + var options = new MemoryCacheEntryOptions + { + AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(10) + }; + _cache.Set(cacheLookupKey, result, options); } private void ValidateSignatureNotNullOrEmpty(byte[] signature, string methodName) @@ -115,15 +119,17 @@ private void ValidateStringArgumentNotNullOrEmpty(string stringArgValue, string private void TrimCacheIfNeeded() { // If the size of the cache exceeds the threshold, set that we are in trimming and trim the cache accordingly. - long currentCacheSize = _cache.GetCount(); + long currentCacheSize = _cache.Count; if ((currentCacheSize > _cacheSize + _cacheTrimThreshold) && (0 == Interlocked.CompareExchange(ref _inTrim, 1, 0))) { try { - _cache.Trim((int)(((double)(currentCacheSize - _cacheSize) / (double)currentCacheSize) * 100)); + // Example: 2301 - 2000 = 301; 301 / 2301 = 0.1308 * 100 = 13% compacting + _cache.Compact((int)(((double)(currentCacheSize - _cacheSize) / (double)currentCacheSize) * 100)); } finally { + // Reset _inTrim flag Interlocked.CompareExchange(ref _inTrim, 0, 1); } } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlQueryMetadataCache.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlQueryMetadataCache.cs index 5475eb5a0c..b57fe400ec 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlQueryMetadataCache.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlQueryMetadataCache.cs @@ -7,9 +7,9 @@ using System.Collections.Generic; using System.Data; using System.Diagnostics; -using System.Runtime.Caching; using System.Text; using System.Threading; +using Microsoft.Extensions.Caching.Memory; namespace Microsoft.Data.SqlClient { @@ -34,7 +34,7 @@ sealed internal class SqlQueryMetadataCache private SqlQueryMetadataCache() { - _cache = new MemoryCache("SqlQueryMetadataCache"); + _cache = new MemoryCache(new MemoryCacheOptions()); } internal static SqlQueryMetadataCache GetInstance() @@ -61,7 +61,7 @@ internal bool GetQueryMetadataIfExists(SqlCommand sqlCommand) return false; } - Dictionary cipherMetadataDictionary = _cache.Get(cacheLookupKey) as Dictionary; + Dictionary cipherMetadataDictionary = _cache.Get>(cacheLookupKey); // If we had a cache miss just return false. if (cipherMetadataDictionary is null) @@ -144,7 +144,7 @@ internal bool GetQueryMetadataIfExists(SqlCommand sqlCommand) } ConcurrentDictionary enclaveKeys = - _cache.Get(enclaveLookupKey) as ConcurrentDictionary; + _cache.Get>(enclaveLookupKey); if (enclaveKeys is not null) { sqlCommand.keysToBeSentToEnclave = CreateCopyOfEnclaveKeys(enclaveKeys); @@ -215,7 +215,7 @@ internal void AddQueryMetadata(SqlCommand sqlCommand, bool ignoreQueriesWithRetu } // If the size of the cache exceeds the threshold, set that we are in trimming and trim the cache accordingly. - long currentCacheSize = _cache.GetCount(); + long currentCacheSize = _cache.Count; if ((currentCacheSize > CacheSize + CacheTrimThreshold) && (0 == Interlocked.CompareExchange(ref _inTrim, 1, 0))) { try @@ -226,7 +226,7 @@ internal void AddQueryMetadata(SqlCommand sqlCommand, bool ignoreQueriesWithRetu Thread.Sleep(TimeSpan.FromSeconds(10)); } #endif - _cache.Trim((int)(((double)(currentCacheSize - CacheSize) / (double)currentCacheSize) * 100)); + _cache.Compact((int)(((double)(currentCacheSize - CacheSize) / (double)currentCacheSize) * 100)); } finally { @@ -235,11 +235,15 @@ internal void AddQueryMetadata(SqlCommand sqlCommand, bool ignoreQueriesWithRetu } // By default evict after 10 hours. - _cache.Set(cacheLookupKey, cipherMetadataDictionary, DateTimeOffset.UtcNow.AddHours(10)); + var options = new MemoryCacheEntryOptions + { + AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(10) + }; + _cache.Set>(cacheLookupKey, cipherMetadataDictionary, options); if (sqlCommand.requiresEnclaveComputations) { ConcurrentDictionary keysToBeCached = CreateCopyOfEnclaveKeys(sqlCommand.keysToBeSentToEnclave); - _cache.Set(enclaveLookupKey, keysToBeCached, DateTimeOffset.UtcNow.AddHours(10)); + _cache.Set>(enclaveLookupKey, keysToBeCached, options); } } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSymmetricKeyCache.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSymmetricKeyCache.cs index 663116ed59..049ad720e9 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSymmetricKeyCache.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSymmetricKeyCache.cs @@ -4,8 +4,8 @@ using System; using System.Diagnostics; -using System.Runtime.Caching; using System.Text; +using Microsoft.Extensions.Caching.Memory; namespace Microsoft.Data.SqlClient { @@ -20,7 +20,7 @@ sealed internal class SqlSymmetricKeyCache private SqlSymmetricKeyCache() { - _cache = new MemoryCache("ColumnEncryptionKeyCache"); + _cache = new MemoryCache(new MemoryCacheOptions()); } internal static SqlSymmetricKeyCache GetInstance() @@ -53,7 +53,8 @@ internal SqlClientSymmetricKey GetKey(SqlEncryptionKeyInfo keyInfo, SqlConnectio #endif //DEBUG // Lookup the key in cache - if (!(_cache.Get(cacheLookupKey) is SqlClientSymmetricKey encryptionKey)) + SqlClientSymmetricKey encryptionKey; + if (!(_cache.TryGetValue(cacheLookupKey, out encryptionKey))) { Debug.Assert(SqlConnection.ColumnEncryptionTrustedMasterKeyPaths is not null, @"SqlConnection.ColumnEncryptionTrustedMasterKeyPaths should not be null"); @@ -90,8 +91,11 @@ internal SqlClientSymmetricKey GetKey(SqlEncryptionKeyInfo keyInfo, SqlConnectio { // In case multiple threads reach here at the same time, the first one wins. // The allocated memory will be reclaimed by Garbage Collector. - DateTimeOffset expirationTime = DateTimeOffset.UtcNow.Add(SqlConnection.ColumnEncryptionKeyCacheTtl); - _cache.Add(cacheLookupKey, encryptionKey, expirationTime); + var options = new MemoryCacheEntryOptions + { + AbsoluteExpirationRelativeToNow = SqlConnection.ColumnEncryptionKeyCacheTtl + }; + _cache.Set(cacheLookupKey, encryptionKey, options); } } 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 c60dd01c72..f4c43cc7c9 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs @@ -5,10 +5,10 @@ #if !NETSTANDARD2_0 using System; -using System.Runtime.Caching; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Threading; +using Microsoft.Extensions.Caching.Memory; namespace Microsoft.Data.SqlClient { @@ -16,7 +16,7 @@ internal abstract class VirtualizationBasedSecurityEnclaveProviderBase : Enclave { #region Members - private static readonly MemoryCache rootSigningCertificateCache = new MemoryCache("RootSigningCertificateCache"); + private static readonly MemoryCache rootSigningCertificateCache = new MemoryCache(new MemoryCacheOptions()); #endregion @@ -194,7 +194,7 @@ private void VerifyAttestationInfo(string attestationUrl, HealthReport healthRep private X509Certificate2Collection GetSigningCertificate(string attestationUrl, bool forceUpdate) { attestationUrl = GetAttestationUrl(attestationUrl); - X509Certificate2Collection signingCertificates = (X509Certificate2Collection)rootSigningCertificateCache[attestationUrl]; + X509Certificate2Collection signingCertificates = rootSigningCertificateCache.Get(attestationUrl); if (forceUpdate || signingCertificates == null || AnyCertificatesExpired(signingCertificates)) { byte[] data = MakeRequest(attestationUrl); @@ -209,10 +209,14 @@ private X509Certificate2Collection GetSigningCertificate(string attestationUrl, throw SQL.AttestationFailed(string.Format(Strings.GetAttestationSigningCertificateFailedInvalidCertificate, attestationUrl), exception); } - rootSigningCertificateCache.Add(attestationUrl, certificateCollection, DateTime.Now.AddDays(1)); + var options = new MemoryCacheEntryOptions + { + AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(1) + }; + rootSigningCertificateCache.Set(attestationUrl, certificateCollection, options); } - return (X509Certificate2Collection)rootSigningCertificateCache[attestationUrl]; + return rootSigningCertificateCache.Get(attestationUrl); } // Return the endpoint for given attestation url diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CertificateUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CertificateUtility.cs index 1c054f3769..faa88ac6b6 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CertificateUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CertificateUtility.cs @@ -6,14 +6,13 @@ using System.Collections.Generic; using System.Diagnostics; using System.Reflection; -using System.Runtime.Caching; -using System.Runtime.CompilerServices; -using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; using Azure; using Azure.Identity; using Azure.Security.KeyVault.Keys; +using Microsoft.Extensions.Caching.Memory; + namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted { class CertificateUtility @@ -98,11 +97,7 @@ internal static void CleanSqlClientCache() { object sqlSymmetricKeyCache = SqlSymmetricKeyCacheGetInstance.Invoke(null, null); MemoryCache cache = SqlSymmetricKeyCacheFieldCache.GetValue(sqlSymmetricKeyCache) as MemoryCache; - - foreach (KeyValuePair item in cache) - { - cache.Remove(item.Key); - } + cache.Clear(); } /// From 61a0c95d67d45821986a3f9ace3c071364dd69a1 Mon Sep 17 00:00:00 2001 From: v-arellegue Date: Tue, 7 May 2024 16:13:32 -0700 Subject: [PATCH 02/16] Fix signatureVerificationResult condition to test for null or false value. --- .../src/Microsoft/Data/SqlClient/SignatureVerificationCache.cs | 2 -- .../src/Microsoft/Data/SqlClient/SqlSecurityUtility.cs | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SignatureVerificationCache.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SignatureVerificationCache.cs index a1b4c733df..e9fe322495 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SignatureVerificationCache.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SignatureVerificationCache.cs @@ -49,7 +49,6 @@ private ColumnMasterKeyMetadataSignatureVerificationCache() /// null if the data is not found in cache otherwise returns true/false indicating signature verification success/failure internal bool? GetSignatureVerificationResult(string keyStoreName, string masterKeyPath, bool allowEnclaveComputations, byte[] signature) { - ValidateStringArgumentNotNullOrEmpty(masterKeyPath, _masterkeypathArgumentName, _getSignatureVerificationResultMethodName); ValidateStringArgumentNotNullOrEmpty(keyStoreName, _keyStoreNameArgumentName, _getSignatureVerificationResultMethodName); ValidateSignatureNotNullOrEmpty(signature, _getSignatureVerificationResultMethodName); @@ -69,7 +68,6 @@ private ColumnMasterKeyMetadataSignatureVerificationCache() /// result indicating signature verification success/failure internal void AddSignatureVerificationResult(string keyStoreName, string masterKeyPath, bool allowEnclaveComputations, byte[] signature, bool result) { - ValidateStringArgumentNotNullOrEmpty(masterKeyPath, _masterkeypathArgumentName, _addSignatureVerificationResultMethodName); ValidateStringArgumentNotNullOrEmpty(keyStoreName, _keyStoreNameArgumentName, _addSignatureVerificationResultMethodName); ValidateSignatureNotNullOrEmpty(signature, _addSignatureVerificationResultMethodName); diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSecurityUtility.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSecurityUtility.cs index d9fea6b211..e7b88daf2a 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSecurityUtility.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSecurityUtility.cs @@ -375,7 +375,7 @@ internal static void VerifyColumnMasterKeySignature(string keyStoreName, string else { bool? signatureVerificationResult = ColumnMasterKeyMetadataSignatureVerificationCache.GetSignatureVerificationResult(keyStoreName, keyPath, isEnclaveEnabled, CMKSignature); - if (signatureVerificationResult is null) + if (signatureVerificationResult is null || signatureVerificationResult == false) { // We will simply bubble up the exception from VerifyColumnMasterKeyMetadata function. isValidSignature = provider.VerifyColumnMasterKeyMetadata(keyPath, isEnclaveEnabled, From 4111635c8ccf14c24d39ebdf8627b099d75c4f90 Mon Sep 17 00:00:00 2001 From: v-arellegue Date: Tue, 7 May 2024 16:46:16 -0700 Subject: [PATCH 03/16] Fixed ColumnMasterKeyMetadataSignatureVerificationCache.GetSignatureVerificationResult function to return either true or false only, i.e. not null. --- .../Microsoft/Data/SqlClient/SignatureVerificationCache.cs | 2 +- .../src/Microsoft/Data/SqlClient/SqlSecurityUtility.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SignatureVerificationCache.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SignatureVerificationCache.cs index e9fe322495..0834ce7bc0 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SignatureVerificationCache.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SignatureVerificationCache.cs @@ -47,7 +47,7 @@ private ColumnMasterKeyMetadataSignatureVerificationCache() /// boolean indicating whether the key can be sent to enclave /// Signature for the CMK metadata /// null if the data is not found in cache otherwise returns true/false indicating signature verification success/failure - internal bool? GetSignatureVerificationResult(string keyStoreName, string masterKeyPath, bool allowEnclaveComputations, byte[] signature) + internal bool GetSignatureVerificationResult(string keyStoreName, string masterKeyPath, bool allowEnclaveComputations, byte[] signature) { ValidateStringArgumentNotNullOrEmpty(masterKeyPath, _masterkeypathArgumentName, _getSignatureVerificationResultMethodName); ValidateStringArgumentNotNullOrEmpty(keyStoreName, _keyStoreNameArgumentName, _getSignatureVerificationResultMethodName); diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSecurityUtility.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSecurityUtility.cs index e7b88daf2a..01d2d1bc61 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSecurityUtility.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSecurityUtility.cs @@ -374,8 +374,8 @@ internal static void VerifyColumnMasterKeySignature(string keyStoreName, string } else { - bool? signatureVerificationResult = ColumnMasterKeyMetadataSignatureVerificationCache.GetSignatureVerificationResult(keyStoreName, keyPath, isEnclaveEnabled, CMKSignature); - if (signatureVerificationResult is null || signatureVerificationResult == false) + bool signatureVerificationResult = ColumnMasterKeyMetadataSignatureVerificationCache.GetSignatureVerificationResult(keyStoreName, keyPath, isEnclaveEnabled, CMKSignature); + if (signatureVerificationResult == false) { // We will simply bubble up the exception from VerifyColumnMasterKeyMetadata function. isValidSignature = provider.VerifyColumnMasterKeyMetadata(keyPath, isEnclaveEnabled, @@ -385,7 +385,7 @@ internal static void VerifyColumnMasterKeySignature(string keyStoreName, string } else { - isValidSignature = signatureVerificationResult.Value; + isValidSignature = signatureVerificationResult; } } } From 5bdf5bd683f9a40365971dd985fe06de5a55be15 Mon Sep 17 00:00:00 2001 From: v-arellegue Date: Wed, 8 May 2024 08:31:53 -0700 Subject: [PATCH 04/16] Removed blank line. --- .../tests/ManualTests/AlwaysEncrypted/ConversionTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ConversionTests.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ConversionTests.cs index d0b1bfd076..8e62ee95fc 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ConversionTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ConversionTests.cs @@ -1449,5 +1449,4 @@ public IEnumerator GetEnumerator() IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } - } From ac0db6b597db840cb14f292d56a10faa331a0d42 Mon Sep 17 00:00:00 2001 From: v-arellegue Date: Wed, 8 May 2024 12:26:28 -0700 Subject: [PATCH 05/16] Commit merged main stream. --- NuGet.config | 3 +++ .../netcore/ref/Microsoft.Data.SqlClient.csproj | 1 + .../netcore/src/Microsoft.Data.SqlClient.csproj | 2 +- .../netfx/ref/Microsoft.Data.SqlClient.csproj | 2 +- .../netfx/src/Microsoft.Data.SqlClient.csproj | 2 +- .../Microsoft.Data.SqlClient.Tests.csproj | 10 +++++----- ...Microsoft.Data.SqlClient.ManualTesting.Tests.csproj | 9 ++++----- 7 files changed, 16 insertions(+), 13 deletions(-) diff --git a/NuGet.config b/NuGet.config index 3233e60161..2ab249bac7 100644 --- a/NuGet.config +++ b/NuGet.config @@ -3,5 +3,8 @@ + + + diff --git a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj index 498849102a..7190117fa2 100644 --- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj @@ -17,6 +17,7 @@ + diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj index 57497fd2b0..a503a30acc 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -936,10 +936,10 @@ - + diff --git a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj index 0ad0d67a72..0b43452f7f 100644 --- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj @@ -16,7 +16,7 @@ - + diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index 0fd511889b..71b374dbe2 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -738,7 +738,7 @@ - + $(SystemTextEncodingsWebVersion) diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj index b5ac559337..009cfc50b4 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj @@ -123,10 +123,10 @@ - - PreserveNewest - %(Filename)%(Extension) - + + PreserveNewest + %(Filename)%(Extension) + @@ -137,4 +137,4 @@ xunit.runner.json - + \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj b/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj index f75aef8edb..0e8bbb1571 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj @@ -340,7 +340,6 @@ - @@ -348,10 +347,10 @@ - - PreserveNewest - %(Filename)%(Extension) - + + PreserveNewest + %(Filename)%(Extension) + From b9a63a88202b1e98c34aed77f74b02a0a3fa3a0d Mon Sep 17 00:00:00 2001 From: v-arellegue Date: Wed, 8 May 2024 13:17:36 -0700 Subject: [PATCH 06/16] Added reference to System.Configuration.ConfigurationManager. --- .../Microsoft.Data.SqlClient.ManualTesting.Tests.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj b/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj index 0e8bbb1571..09e10df92c 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj @@ -312,6 +312,7 @@ + From e6a3371945bb66210ced074d506cf672f906278e Mon Sep 17 00:00:00 2001 From: v-arellegue Date: Wed, 8 May 2024 13:25:22 -0700 Subject: [PATCH 07/16] Restore Nuget.config --- NuGet.config | 3 --- 1 file changed, 3 deletions(-) diff --git a/NuGet.config b/NuGet.config index 2ab249bac7..3233e60161 100644 --- a/NuGet.config +++ b/NuGet.config @@ -3,8 +3,5 @@ - - - From dc2bba07e962ebfd2d1b3d2921ba7518ed711083 Mon Sep 17 00:00:00 2001 From: v-arellegue Date: Wed, 8 May 2024 13:27:42 -0700 Subject: [PATCH 08/16] Fixed missed Microsoft.Extensions.Caching version usage from Versions.props. --- ....Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.csproj b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.csproj index 84f1cf83eb..eb08db41c2 100644 --- a/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.csproj +++ b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.csproj @@ -32,6 +32,6 @@ - + From 2178f28b29d21070351a03265f07f3ee3465fb25 Mon Sep 17 00:00:00 2001 From: v-arellegue Date: Wed, 8 May 2024 13:33:20 -0700 Subject: [PATCH 09/16] Add extra line to MDS.Tests.csproj file. --- .../tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj index 009cfc50b4..15249ba3cd 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj @@ -137,4 +137,4 @@ xunit.runner.json - \ No newline at end of file + From 939adb04057efdb1f42feb0f97c8f02c6be1b699 Mon Sep 17 00:00:00 2001 From: v-arellegue Date: Wed, 8 May 2024 14:04:30 -0700 Subject: [PATCH 10/16] Implement missing Clear function of MemoryCache in version 6.0. --- .../TestFixtures/Setup/CertificateUtility.cs | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CertificateUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CertificateUtility.cs index 9f559ca719..203cf579cd 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CertificateUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CertificateUtility.cs @@ -97,7 +97,7 @@ internal static void CleanSqlClientCache() { object sqlSymmetricKeyCache = SqlSymmetricKeyCacheGetInstance.Invoke(null, null); MemoryCache cache = SqlSymmetricKeyCacheFieldCache.GetValue(sqlSymmetricKeyCache) as MemoryCache; - cache.Clear(); + ClearCache(cache); } /// @@ -303,5 +303,25 @@ public static void ChangeServerTceSetting(bool fEnable, SqlConnectionStringBuild } } } + + private static void ClearCache(MemoryCache cache) + { + // Get all keys in the cache + var cacheEntriesCollectionDefinition = typeof(MemoryCache).GetProperty("EntriesCollection", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + var cacheEntriesCollection = cacheEntriesCollectionDefinition.GetValue(cache) as dynamic; + List cacheCollectionValues = new List(); + + foreach (var cacheItem in cacheEntriesCollection) + { + ICacheEntry cacheItemValue = cacheItem.GetType().GetProperty("Value").GetValue(cacheItem, null); + cacheCollectionValues.Add(cacheItemValue); + } + + // Remove each cache entry + foreach (var cacheItem in cacheCollectionValues) + { + cache.Remove(cacheItem.Key); + } + } } } From 5506b39d1a77cd36010769f9a59fbdb354f67611 Mon Sep 17 00:00:00 2001 From: v-arellegue Date: Wed, 8 May 2024 15:10:00 -0700 Subject: [PATCH 11/16] Used Explicit type declaration of MemoryCacheOptions variable. Replaced Remove and Set memory cache logic with CreateEntry. --- .../ActiveDirectoryAuthenticationProvider.cs | 12 +++--------- .../AzureAttestationBasedEnclaveProvider.cs | 2 +- .../Microsoft/Data/SqlClient/EnclaveProviderBase.cs | 2 +- .../Microsoft/Data/SqlClient/EnclaveSessionCache.cs | 2 +- .../Data/SqlClient/SignatureVerificationCache.cs | 2 +- .../Data/SqlClient/SqlQueryMetadataCache.cs | 2 +- .../Microsoft/Data/SqlClient/SqlSymmetricKeyCache.cs | 2 +- .../VirtualSecureModeEnclaveProviderBase.cs | 2 +- 8 files changed, 10 insertions(+), 16 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationProvider.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationProvider.cs index 1d5888b1bc..adc8e4e809 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationProvider.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationProvider.cs @@ -270,17 +270,11 @@ previousPw is byte[] previousPwBytes && // We cache the password hash to ensure future connection requests include a validated password // when we check for a cached MSAL account. Otherwise, a connection request with the same username // against the same tenant could succeed with an invalid password when we re-use the cached token. - var options = new MemoryCacheEntryOptions + using (ICacheEntry entry = s_accountPwCache.CreateEntry(pwCacheKey)) { - AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(s_accountPwCacheTtlInHours) + entry.Value = GetHash(parameters.Password); + entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(s_accountPwCacheTtlInHours); }; - // Remove old cached password hash if there is one - if (s_accountPwCache.TryGetValue(pwCacheKey, out string key)) - { - s_accountPwCache.Remove(pwCacheKey); - } - // Add current password hash to cache - s_accountPwCache.Set(pwCacheKey, GetHash(parameters.Password), options); SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token for Active Directory Password auth mode. Expiry Time: {0}", result?.ExpiresOn); } 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 2be376ffd3..3c51716828 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs @@ -348,7 +348,7 @@ private OpenIdConnectConfiguration GetOpenIdConfigForSigningKeys(string url, boo throw SQL.AttestationFailed(string.Format(Strings.GetAttestationTokenSigningKeysFailed, GetInnerMostExceptionMessage(exception)), exception); } - var options = new MemoryCacheEntryOptions + MemoryCacheEntryOptions options = new MemoryCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(1) }; 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 221215b2b7..b666819dde 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs @@ -167,7 +167,7 @@ protected void GetEnclaveSessionHelper(EnclaveSessionParameters enclaveSessionPa retryThreadID = Thread.CurrentThread.ManagedThreadId.ToString(); } - var options = new MemoryCacheEntryOptions + MemoryCacheEntryOptions options = new MemoryCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(ThreadRetryCacheTimeoutInMinutes) }; 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 0992bf4122..5673395e11 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveSessionCache.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveSessionCache.cs @@ -62,7 +62,7 @@ internal SqlEnclaveSession CreateSession(EnclaveSessionParameters enclaveSession lock (enclaveCacheLock) { enclaveSession = new SqlEnclaveSession(sharedSecret, sessionId); - var options = new MemoryCacheEntryOptions + MemoryCacheEntryOptions options = new MemoryCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(enclaveCacheTimeOutInHours) }; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SignatureVerificationCache.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SignatureVerificationCache.cs index 0834ce7bc0..a756cec158 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SignatureVerificationCache.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SignatureVerificationCache.cs @@ -77,7 +77,7 @@ internal void AddSignatureVerificationResult(string keyStoreName, string masterK TrimCacheIfNeeded(); // By default evict after 10 days. - var options = new MemoryCacheEntryOptions + MemoryCacheEntryOptions options = new MemoryCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(10) }; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlQueryMetadataCache.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlQueryMetadataCache.cs index b57fe400ec..964e46aca3 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlQueryMetadataCache.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlQueryMetadataCache.cs @@ -235,7 +235,7 @@ internal void AddQueryMetadata(SqlCommand sqlCommand, bool ignoreQueriesWithRetu } // By default evict after 10 hours. - var options = new MemoryCacheEntryOptions + MemoryCacheEntryOptions options = new MemoryCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(10) }; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSymmetricKeyCache.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSymmetricKeyCache.cs index 049ad720e9..fb9ea2997d 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSymmetricKeyCache.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSymmetricKeyCache.cs @@ -91,7 +91,7 @@ internal SqlClientSymmetricKey GetKey(SqlEncryptionKeyInfo keyInfo, SqlConnectio { // In case multiple threads reach here at the same time, the first one wins. // The allocated memory will be reclaimed by Garbage Collector. - var options = new MemoryCacheEntryOptions + MemoryCacheEntryOptions options = new MemoryCacheEntryOptions { AbsoluteExpirationRelativeToNow = SqlConnection.ColumnEncryptionKeyCacheTtl }; 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 cef2e9df70..ab327aa689 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs @@ -207,7 +207,7 @@ private X509Certificate2Collection GetSigningCertificate(string attestationUrl, throw SQL.AttestationFailed(string.Format(Strings.GetAttestationSigningCertificateFailedInvalidCertificate, attestationUrl), exception); } - var options = new MemoryCacheEntryOptions + MemoryCacheEntryOptions options = new MemoryCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(1) }; From 24c344a59b0bf443285df32ea7553801381802ef Mon Sep 17 00:00:00 2001 From: v-arellegue Date: Fri, 10 May 2024 11:00:02 -0700 Subject: [PATCH 12/16] Refactor ClearCache function so it works in .Net8.0 and .Net6.0. --- .../TestFixtures/Setup/CertificateUtility.cs | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CertificateUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CertificateUtility.cs index 203cf579cd..13603e5235 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CertificateUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CertificateUtility.cs @@ -3,6 +3,8 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; @@ -306,21 +308,24 @@ public static void ChangeServerTceSetting(bool fEnable, SqlConnectionStringBuild private static void ClearCache(MemoryCache cache) { - // Get all keys in the cache - var cacheEntriesCollectionDefinition = typeof(MemoryCache).GetProperty("EntriesCollection", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); - var cacheEntriesCollection = cacheEntriesCollectionDefinition.GetValue(cache) as dynamic; - List cacheCollectionValues = new List(); - - foreach (var cacheItem in cacheEntriesCollection) + // Get the Clear method of the cache and use it if available. This is available in .NET 8. + MethodInfo clearMethod = cache.GetType().GetMethod("Clear", BindingFlags.Instance | BindingFlags.Public); + if (clearMethod != null) { - ICacheEntry cacheItemValue = cacheItem.GetType().GetProperty("Value").GetValue(cacheItem, null); - cacheCollectionValues.Add(cacheItemValue); + clearMethod.Invoke(cache, null); } - - // Remove each cache entry - foreach (var cacheItem in cacheCollectionValues) + else { - cache.Remove(cacheItem.Key); + // Otherwise, use the Remove function to remove all entries using all keys in the cache + PropertyInfo cacheEntriesCollectionDefinition = typeof(MemoryCache).GetProperty("EntriesCollection", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + ICollection cacheEntriesCollection = (ICollection)cacheEntriesCollectionDefinition.GetValue(cache); + List cacheCollectionValues = new List(); + + foreach (object cacheItem in cacheEntriesCollection) + { + ICacheEntry cacheItemValue = (ICacheEntry)cacheItem.GetType().GetProperty("Value").GetValue(cacheItem, null); + cache.Remove(cacheItemValue.Key); + } } } } From a817a06d8f5b4d7210d5ae51d1cb86bce1ec37fd Mon Sep 17 00:00:00 2001 From: v-arellegue Date: Fri, 10 May 2024 14:49:46 -0700 Subject: [PATCH 13/16] Update comment to trigger pipeline run. --- .../AlwaysEncrypted/TestFixtures/Setup/CertificateUtility.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CertificateUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CertificateUtility.cs index 13603e5235..999255da8b 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CertificateUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CertificateUtility.cs @@ -316,7 +316,7 @@ private static void ClearCache(MemoryCache cache) } else { - // Otherwise, use the Remove function to remove all entries using all keys in the cache + // Otherwise, use the Remove function to remove all entries using all keys in the cache gathered using reflection. PropertyInfo cacheEntriesCollectionDefinition = typeof(MemoryCache).GetProperty("EntriesCollection", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); ICollection cacheEntriesCollection = (ICollection)cacheEntriesCollectionDefinition.GetValue(cache); List cacheCollectionValues = new List(); From 98cd0707a838155d3f8ab22a272aad08e7c60ec3 Mon Sep 17 00:00:00 2001 From: v-arellegue Date: Mon, 27 May 2024 11:08:15 -0700 Subject: [PATCH 14/16] Replaced System.Runtime.Caching in M.D.S.nuspec file with Microsoft.Extensions.Caching.Memory. --- tools/specs/Microsoft.Data.SqlClient.nuspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/specs/Microsoft.Data.SqlClient.nuspec b/tools/specs/Microsoft.Data.SqlClient.nuspec index b1666d4459..9bd200d049 100644 --- a/tools/specs/Microsoft.Data.SqlClient.nuspec +++ b/tools/specs/Microsoft.Data.SqlClient.nuspec @@ -46,7 +46,7 @@ When using NuGet 3.x this package requires at least version 3.4. - + @@ -56,7 +56,7 @@ When using NuGet 3.x this package requires at least version 3.4. - + From 9789f092b3c15d40bc9bb73aa77bcce3f5816a02 Mon Sep 17 00:00:00 2001 From: v-arellegue Date: Tue, 28 May 2024 16:22:48 -0700 Subject: [PATCH 15/16] Applied suggestions from PR review. --- .../src/Microsoft/Data/SqlClient/SignatureVerificationCache.cs | 3 +-- .../AlwaysEncrypted/TestFixtures/Setup/CertificateUtility.cs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SignatureVerificationCache.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SignatureVerificationCache.cs index a756cec158..e54e88f23e 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SignatureVerificationCache.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SignatureVerificationCache.cs @@ -46,7 +46,6 @@ private ColumnMasterKeyMetadataSignatureVerificationCache() /// Key Path for CMK /// boolean indicating whether the key can be sent to enclave /// Signature for the CMK metadata - /// null if the data is not found in cache otherwise returns true/false indicating signature verification success/failure internal bool GetSignatureVerificationResult(string keyStoreName, string masterKeyPath, bool allowEnclaveComputations, byte[] signature) { ValidateStringArgumentNotNullOrEmpty(masterKeyPath, _masterkeypathArgumentName, _getSignatureVerificationResultMethodName); @@ -123,7 +122,7 @@ private void TrimCacheIfNeeded() try { // Example: 2301 - 2000 = 301; 301 / 2301 = 0.1308 * 100 = 13% compacting - _cache.Compact((int)(((double)(currentCacheSize - _cacheSize) / (double)currentCacheSize) * 100)); + _cache.Compact((((double)(currentCacheSize - _cacheSize) / (double)currentCacheSize) * 100)); } finally { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CertificateUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CertificateUtility.cs index 999255da8b..d695c2c9dd 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CertificateUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CertificateUtility.cs @@ -308,7 +308,7 @@ public static void ChangeServerTceSetting(bool fEnable, SqlConnectionStringBuild private static void ClearCache(MemoryCache cache) { - // Get the Clear method of the cache and use it if available. This is available in .NET 8. + // Get the Clear method of the cache and use it if available. This is available in Microsoft.Extensions.Caching 8.0 MethodInfo clearMethod = cache.GetType().GetMethod("Clear", BindingFlags.Instance | BindingFlags.Public); if (clearMethod != null) { From 0d93c1cabc6aabf221a1cc31a5030de69a82a740 Mon Sep 17 00:00:00 2001 From: v-arellegue Date: Wed, 29 May 2024 12:28:27 -0700 Subject: [PATCH 16/16] Updated the package version of Microsoft.Extensions.Caching.Memory to 8.0.0 for target framework .Net8.0. --- tools/specs/Microsoft.Data.SqlClient.nuspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/specs/Microsoft.Data.SqlClient.nuspec b/tools/specs/Microsoft.Data.SqlClient.nuspec index 9bd200d049..1e4f5e5a09 100644 --- a/tools/specs/Microsoft.Data.SqlClient.nuspec +++ b/tools/specs/Microsoft.Data.SqlClient.nuspec @@ -46,7 +46,7 @@ When using NuGet 3.x this package requires at least version 3.4. - +