From 71b31a17ea023831efed7134ffaf550549cf537f Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Sun, 5 Apr 2026 21:02:29 +0100 Subject: [PATCH 1/4] Replace SqlClientEncryptionAlgorithmFactoryList This implemented theoretical support for allowing clients to register their own cryptographic algorithms. This was never used, so has been replaced with a static mapping. Placed its replacement into the AlwaysEncrypted namespace. --- .../src/Microsoft.Data.SqlClient.csproj | 6 +- .../netfx/src/Microsoft.Data.SqlClient.csproj | 6 +- .../EncryptionAlgorithmFactoryList.cs | 37 ++++++++ .../SqlAeadAes256CbcHmac256Factory.cs | 4 +- ...SqlClientEncryptionAlgorithmFactoryList.cs | 84 ------------------- .../Data/SqlClient/SqlSecurityUtility.cs | 5 +- 6 files changed, 49 insertions(+), 93 deletions(-) create mode 100644 src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AlwaysEncrypted/EncryptionAlgorithmFactoryList.cs delete mode 100644 src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlClientEncryptionAlgorithmFactoryList.cs 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 c2fdb27f3f..dbb9403faf 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -225,6 +225,9 @@ Microsoft\Data\SqlClient\AlwaysEncrypted\EncryptedColumnEncryptionKeyParameters.cs + + Microsoft\Data\SqlClient\AlwaysEncrypted\EncryptionAlgorithmFactoryList.cs + Microsoft\Data\Sql\SqlDataSourceEnumerator.cs @@ -693,9 +696,6 @@ Microsoft\Data\SqlClient\SqlClientEncryptionAlgorithmFactory.cs - - Microsoft\Data\SqlClient\SqlClientEncryptionAlgorithmFactoryList.cs - Microsoft\Data\SqlClient\SqlClientEncryptionType.cs 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 eb14ccc985..2dc9cdefb8 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -310,6 +310,9 @@ Microsoft\Data\SqlClient\AlwaysEncrypted\EncryptedColumnEncryptionKeyParameters.cs + + Microsoft\Data\SqlClient\AlwaysEncrypted\EncryptionAlgorithmFactoryList.cs + Microsoft\Data\SqlClient\AlwaysEncryptedEnclaveProviderUtils.cs @@ -676,9 +679,6 @@ Microsoft\Data\SqlClient\SqlClientEncryptionAlgorithmFactory.cs - - Microsoft\Data\SqlClient\SqlClientEncryptionAlgorithmFactoryList.cs - Microsoft\Data\SqlClient\SqlClientEncryptionType.cs diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AlwaysEncrypted/EncryptionAlgorithmFactoryList.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AlwaysEncrypted/EncryptionAlgorithmFactoryList.cs new file mode 100644 index 0000000000..11e401f838 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AlwaysEncrypted/EncryptionAlgorithmFactoryList.cs @@ -0,0 +1,37 @@ +// 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. + +#nullable enable + +namespace Microsoft.Data.SqlClient.AlwaysEncrypted; + +/// +/// Implements a global mapping from a SQL Server cryptographic algorithm's name to its implementation. +/// +internal static class EncryptionAlgorithmFactoryList +{ + /// + /// Get the available list of algorithms as a comma separated list with algorithm names + /// wrapped in single quotes. + /// + public const string RegisteredCipherAlgorithmNames = $"'{SqlAeadAes256CbcHmac256Algorithm.AlgorithmName}'"; + + /// + /// Gets the implementation for a given algorithm and instantiates it using the provided root key and the encryption type. + /// + /// The root key to use. + /// Encryption type (read from SQL Server.) + /// Name of the cryptographic algorithm. + /// Specified cryptographic algorithm's implementation. + public static void GetAlgorithm(SqlClientSymmetricKey key, byte type, string algorithmName, out SqlClientEncryptionAlgorithm encryptionAlgorithm) + { + SqlClientEncryptionAlgorithmFactory factory = algorithmName switch + { + SqlAeadAes256CbcHmac256Algorithm.AlgorithmName => SqlAeadAes256CbcHmac256Factory.Instance, + _ => throw SQL.UnknownColumnEncryptionAlgorithm(algorithmName, RegisteredCipherAlgorithmNames) + }; + + encryptionAlgorithm = factory.Create(key, (SqlClientEncryptionType)type, algorithmName); + } +} diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlAeadAes256CbcHmac256Factory.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlAeadAes256CbcHmac256Factory.cs index 7501e7ba08..e704c97da4 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlAeadAes256CbcHmac256Factory.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlAeadAes256CbcHmac256Factory.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. @@ -20,6 +20,8 @@ internal class SqlAeadAes256CbcHmac256Factory : SqlClientEncryptionAlgorithmFact private readonly ConcurrentDictionary _encryptionAlgorithms = new ConcurrentDictionary(concurrencyLevel: 4 * Environment.ProcessorCount /* default value in ConcurrentDictionary*/, capacity: 2); + public static SqlAeadAes256CbcHmac256Factory Instance => field ??= new(); + /// /// Creates an instance of AeadAes256CbcHmac256Algorithm class with a given key /// diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlClientEncryptionAlgorithmFactoryList.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlClientEncryptionAlgorithmFactoryList.cs deleted file mode 100644 index 8266b5ce15..0000000000 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlClientEncryptionAlgorithmFactoryList.cs +++ /dev/null @@ -1,84 +0,0 @@ -// 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. - -using System; -using System.Collections.Concurrent; -using System.Diagnostics; -using System.Text; - -namespace Microsoft.Data.SqlClient -{ - /// - /// Implements a global directory of all the encryption algorithms registered with client. - /// - sealed internal class SqlClientEncryptionAlgorithmFactoryList - { - private readonly ConcurrentDictionary _encryptionAlgoFactoryList; - private static readonly SqlClientEncryptionAlgorithmFactoryList _singletonInstance = new SqlClientEncryptionAlgorithmFactoryList(); - - private SqlClientEncryptionAlgorithmFactoryList() - { - _encryptionAlgoFactoryList = new ConcurrentDictionary(concurrencyLevel: 4 * Environment.ProcessorCount /* default value in ConcurrentDictionary*/, capacity: 2); - - // Add wellknown algorithm - _encryptionAlgoFactoryList.TryAdd(SqlAeadAes256CbcHmac256Algorithm.AlgorithmName, new SqlAeadAes256CbcHmac256Factory()); - } - - internal static SqlClientEncryptionAlgorithmFactoryList GetInstance() - { - return _singletonInstance; - } - - /// - /// Get the registered list of algorithms as a comma separated list with algorithm names - /// wrapped in single quotes. - /// - internal string GetRegisteredCipherAlgorithmNames() - { - StringBuilder builder = new StringBuilder(); - bool firstElem = true; - foreach (string key in _encryptionAlgoFactoryList.Keys) - { - if (firstElem) - { - builder.Append("'"); - firstElem = false; - } - else - { - builder.Append(", '"); - } - builder.Append(key); - builder.Append("'"); - } - - return builder.ToString(); - } - - /// - /// Gets the algorithm handle instance for a given algorithm and instantiates it using the provided key and the encryption type. - /// - /// - /// - /// - /// - internal void GetAlgorithm(SqlClientSymmetricKey key, byte type, string algorithmName, out SqlClientEncryptionAlgorithm encryptionAlgorithm) - { - encryptionAlgorithm = null; - - SqlClientEncryptionAlgorithmFactory factory = null; - if (!_encryptionAlgoFactoryList.TryGetValue(algorithmName, out factory)) - { - throw SQL.UnknownColumnEncryptionAlgorithm(algorithmName, - SqlClientEncryptionAlgorithmFactoryList.GetInstance().GetRegisteredCipherAlgorithmNames()); - } - - Debug.Assert(factory != null, "Null Algorithm Factory class detected"); - - // If the factory exists, following method will Create an algorithm object. If this fails, - // it will raise an exception. - encryptionAlgorithm = factory.Create(key, (SqlClientEncryptionType)type, algorithmName); - } - } -} 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 ae20c27254..253ab92db8 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSecurityUtility.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSecurityUtility.cs @@ -9,6 +9,7 @@ using System.Security.Cryptography; using System.Text; using Microsoft.Data.Common; +using Microsoft.Data.SqlClient.AlwaysEncrypted; namespace Microsoft.Data.SqlClient { @@ -98,7 +99,7 @@ private static string ValidateAndGetEncryptionAlgorithmName(byte cipherAlgorithm { if (cipherAlgorithmName == null) { - throw SQL.NullColumnEncryptionAlgorithm(SqlClientEncryptionAlgorithmFactoryList.GetInstance().GetRegisteredCipherAlgorithmNames()); + throw SQL.NullColumnEncryptionAlgorithm(EncryptionAlgorithmFactoryList.RegisteredCipherAlgorithmNames); } return cipherAlgorithmName; @@ -206,7 +207,7 @@ internal static void DecryptSymmetricKey(SqlCipherMetadata md, SqlConnection con md.CipherAlgorithm = null; SqlClientEncryptionAlgorithm cipherAlgorithm = null; string algorithmName = ValidateAndGetEncryptionAlgorithmName(md.CipherAlgorithmId, md.CipherAlgorithmName); // may throw - SqlClientEncryptionAlgorithmFactoryList.GetInstance().GetAlgorithm(symKey, md.EncryptionType, algorithmName, out cipherAlgorithm); // will validate algorithm name and type + EncryptionAlgorithmFactoryList.GetAlgorithm(symKey, md.EncryptionType, algorithmName, out cipherAlgorithm); // will validate algorithm name and type Debug.Assert(cipherAlgorithm is not null); md.CipherAlgorithm = cipherAlgorithm; md.EncryptionKeyInfo = encryptionkeyInfoChosen; From 1aac07a1f816cc8fcb026c195937cb4d0a769e0b Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Sun, 5 Apr 2026 21:08:27 +0100 Subject: [PATCH 2/4] Move and rename SqlClientEncryptionAlgorithmFactory Moved into the AlwaysEncrypted namespace, enabled nullability annotations, renamed --- .../src/Microsoft.Data.SqlClient.csproj | 6 ++--- .../netfx/src/Microsoft.Data.SqlClient.csproj | 6 ++--- .../EncryptionAlgorithmFactory.cs | 24 +++++++++++++++++++ .../EncryptionAlgorithmFactoryList.cs | 2 +- .../SqlAeadAes256CbcHmac256Factory.cs | 3 ++- .../SqlClientEncryptionAlgorithmFactory.cs | 23 ------------------ 6 files changed, 33 insertions(+), 31 deletions(-) create mode 100644 src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AlwaysEncrypted/EncryptionAlgorithmFactory.cs delete mode 100644 src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlClientEncryptionAlgorithmFactory.cs 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 dbb9403faf..fea48a69e7 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -225,6 +225,9 @@ Microsoft\Data\SqlClient\AlwaysEncrypted\EncryptedColumnEncryptionKeyParameters.cs + + Microsoft\Data\SqlClient\AlwaysEncrypted\EncryptionAlgorithmFactory.cs + Microsoft\Data\SqlClient\AlwaysEncrypted\EncryptionAlgorithmFactoryList.cs @@ -693,9 +696,6 @@ Microsoft\Data\SqlClient\SqlClientEncryptionAlgorithm.cs - - Microsoft\Data\SqlClient\SqlClientEncryptionAlgorithmFactory.cs - Microsoft\Data\SqlClient\SqlClientEncryptionType.cs 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 2dc9cdefb8..f04d8fc4c4 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -310,6 +310,9 @@ Microsoft\Data\SqlClient\AlwaysEncrypted\EncryptedColumnEncryptionKeyParameters.cs + + Microsoft\Data\SqlClient\AlwaysEncrypted\EncryptionAlgorithmFactory.cs + Microsoft\Data\SqlClient\AlwaysEncrypted\EncryptionAlgorithmFactoryList.cs @@ -676,9 +679,6 @@ Microsoft\Data\SqlClient\SqlClientEncryptionAlgorithm.cs - - Microsoft\Data\SqlClient\SqlClientEncryptionAlgorithmFactory.cs - Microsoft\Data\SqlClient\SqlClientEncryptionType.cs diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AlwaysEncrypted/EncryptionAlgorithmFactory.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AlwaysEncrypted/EncryptionAlgorithmFactory.cs new file mode 100644 index 0000000000..545a581883 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AlwaysEncrypted/EncryptionAlgorithmFactory.cs @@ -0,0 +1,24 @@ +// 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. + +#nullable enable + +namespace Microsoft.Data.SqlClient.AlwaysEncrypted; + +/// +/// Abstract base class for all TCE cryptographic algorithm factory classes. Factory classes create instances of a cryptographic algorithm +/// with a given key. At runtime when we determine a particular column is marked for TCE, based on the cryptographic algorithm we invoke +/// the corresponding factory class and retrieve an implementation of an cryptographic algorithm. +/// +internal abstract class EncryptionAlgorithmFactory +{ + /// + /// Creates a cryptographic algorithm with a given key. + /// + /// Root key that should be passed to the cryptographic algorithm to be created + /// Encryption Type, some algorithms will need this + /// Cryptographic algorithm name. Needed for extracting version bits + /// Return a newly created SqlClientEncryptionAlgorithm instance + internal abstract SqlClientEncryptionAlgorithm Create(SqlClientSymmetricKey encryptionKey, SqlClientEncryptionType encryptionType, string encryptionAlgorithm); +} diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AlwaysEncrypted/EncryptionAlgorithmFactoryList.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AlwaysEncrypted/EncryptionAlgorithmFactoryList.cs index 11e401f838..e0b7e6d4c6 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AlwaysEncrypted/EncryptionAlgorithmFactoryList.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AlwaysEncrypted/EncryptionAlgorithmFactoryList.cs @@ -26,7 +26,7 @@ internal static class EncryptionAlgorithmFactoryList /// Specified cryptographic algorithm's implementation. public static void GetAlgorithm(SqlClientSymmetricKey key, byte type, string algorithmName, out SqlClientEncryptionAlgorithm encryptionAlgorithm) { - SqlClientEncryptionAlgorithmFactory factory = algorithmName switch + EncryptionAlgorithmFactory factory = algorithmName switch { SqlAeadAes256CbcHmac256Algorithm.AlgorithmName => SqlAeadAes256CbcHmac256Factory.Instance, _ => throw SQL.UnknownColumnEncryptionAlgorithm(algorithmName, RegisteredCipherAlgorithmNames) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlAeadAes256CbcHmac256Factory.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlAeadAes256CbcHmac256Factory.cs index e704c97da4..964a9ae130 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlAeadAes256CbcHmac256Factory.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlAeadAes256CbcHmac256Factory.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Microsoft.Data.SqlClient.AlwaysEncrypted; using System; using System.Collections.Concurrent; using System.Diagnostics; @@ -12,7 +13,7 @@ namespace Microsoft.Data.SqlClient /// /// This is a factory class for AEAD_AES_256_CBC_HMAC_SHA256 /// - internal class SqlAeadAes256CbcHmac256Factory : SqlClientEncryptionAlgorithmFactory + internal class SqlAeadAes256CbcHmac256Factory : EncryptionAlgorithmFactory { /// /// Factory classes caches the SqlAeadAes256CbcHmac256EncryptionKey objects to avoid computation of the derived keys diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlClientEncryptionAlgorithmFactory.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlClientEncryptionAlgorithmFactory.cs deleted file mode 100644 index 14f0f9816b..0000000000 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlClientEncryptionAlgorithmFactory.cs +++ /dev/null @@ -1,23 +0,0 @@ -// 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. - -namespace Microsoft.Data.SqlClient -{ - /// - /// Abstract base class for all TCE encryption algorithm factory classes. Factory classes create instances of an encryption algorithm - /// with a given key. At runtime when we determine a particular column is marked for TCE, based on the encryption algorithm we invoke - /// the corresponding factory class and retrieve an object to an encryption algorithm. - /// - internal abstract class SqlClientEncryptionAlgorithmFactory - { - /// - /// Creates an encryption algorithm with a given key. - /// - /// encryption key that should be passed to the encryption algorithm to be created - /// Encryption Type, some algorithms will need this - /// Encryption algorithm name. Needed for extracting version bits - /// Return a newly created SqlClientEncryptionAlgorithm instance - internal abstract SqlClientEncryptionAlgorithm Create(SqlClientSymmetricKey encryptionKey, SqlClientEncryptionType encryptionType, string encryptionAlgorithm); - } -} From 0915a49f25c5db4f281a94fb08abe636af18945f Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Sun, 5 Apr 2026 22:17:47 +0100 Subject: [PATCH 3/4] Misc. code style changes to SqlAeadAes256CbcHmac256Factory This includes: * Rename to AeadAes256CbcHmac256Factory. * Enable nullability annotations. * Whitespace/comment changes. * Move the version constant into the algorithm itself (and out of the factory.) * Minor improvement to StringBuilder concatenation. --- .../EncryptionAlgorithmFactoryList.cs | 2 +- .../Data/SqlClient/EnclaveDelegate.cs | 6 +- .../SqlAeadAes256CbcHmac256Algorithm.cs | 12 +++- .../SqlAeadAes256CbcHmac256Factory.cs | 55 ++++++++----------- .../AlwaysEncryptedTests/Utility.cs | 12 ++-- .../AlwaysEncrypted/NativeAeadBaseline.cs | 6 +- 6 files changed, 46 insertions(+), 47 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AlwaysEncrypted/EncryptionAlgorithmFactoryList.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AlwaysEncrypted/EncryptionAlgorithmFactoryList.cs index e0b7e6d4c6..0b28dd9ef1 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AlwaysEncrypted/EncryptionAlgorithmFactoryList.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AlwaysEncrypted/EncryptionAlgorithmFactoryList.cs @@ -28,7 +28,7 @@ public static void GetAlgorithm(SqlClientSymmetricKey key, byte type, string alg { EncryptionAlgorithmFactory factory = algorithmName switch { - SqlAeadAes256CbcHmac256Algorithm.AlgorithmName => SqlAeadAes256CbcHmac256Factory.Instance, + SqlAeadAes256CbcHmac256Algorithm.AlgorithmName => AeadAes256CbcHmac256Factory.Instance, _ => throw SQL.UnknownColumnEncryptionAlgorithm(algorithmName, RegisteredCipherAlgorithmNames) }; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs index 79b88d6788..f0439725cf 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.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 @@ namespace Microsoft.Data.SqlClient // @TODO: This isn't a delegate... it's a utility class internal sealed partial class EnclaveDelegate { - private static readonly SqlAeadAes256CbcHmac256Factory s_sqlAeadAes256CbcHmac256Factory = new SqlAeadAes256CbcHmac256Factory(); + private static readonly AeadAes256CbcHmac256Factory s_aeadAes256CbcHmac256Factory = new AeadAes256CbcHmac256Factory(); private static readonly EnclaveDelegate s_enclaveDelegate = new EnclaveDelegate(); private readonly object _lock; @@ -152,7 +152,7 @@ private byte[] EncryptBytePackage(byte[] bytePackage, byte[] sessionKey, string try { SqlClientSymmetricKey symmetricKey = new SqlClientSymmetricKey(sessionKey); - SqlClientEncryptionAlgorithm sqlClientEncryptionAlgorithm = s_sqlAeadAes256CbcHmac256Factory.Create( + SqlClientEncryptionAlgorithm sqlClientEncryptionAlgorithm = s_aeadAes256CbcHmac256Factory.Create( symmetricKey, SqlClientEncryptionType.Randomized, SqlAeadAes256CbcHmac256Algorithm.AlgorithmName diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlAeadAes256CbcHmac256Algorithm.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlAeadAes256CbcHmac256Algorithm.cs index 1035d87062..307330af0a 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlAeadAes256CbcHmac256Algorithm.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlAeadAes256CbcHmac256Algorithm.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. @@ -22,6 +22,14 @@ internal class SqlAeadAes256CbcHmac256Algorithm : SqlClientEncryptionAlgorithm /// internal const string AlgorithmName = @"AEAD_AES_256_CBC_HMAC_SHA256"; + /// + /// Algorithm version + /// + /// + /// For now, only one version exists. In future, this may be derived from the algorithm name. + /// + public const byte CurrentVersion = 0x01; + /// /// Key size in bytes /// @@ -77,7 +85,7 @@ internal class SqlAeadAes256CbcHmac256Algorithm : SqlClientEncryptionAlgorithm /// /// Byte array with algorithm version used for authentication tag computation. /// - private static readonly byte[] _version = new byte[] { 0x01 }; + private static readonly byte[] _version = [ CurrentVersion ]; /// /// Byte array with algorithm version size used for authentication tag computation. diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlAeadAes256CbcHmac256Factory.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlAeadAes256CbcHmac256Factory.cs index 964a9ae130..7a87a33e91 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlAeadAes256CbcHmac256Factory.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlAeadAes256CbcHmac256Factory.cs @@ -8,36 +8,37 @@ using System.Diagnostics; using System.Text; +#nullable enable + namespace Microsoft.Data.SqlClient { /// - /// This is a factory class for AEAD_AES_256_CBC_HMAC_SHA256 + /// This is a factory class for AEAD_AES_256_CBC_HMAC_SHA256. /// - internal class SqlAeadAes256CbcHmac256Factory : EncryptionAlgorithmFactory + internal class AeadAes256CbcHmac256Factory : EncryptionAlgorithmFactory { /// - /// Factory classes caches the SqlAeadAes256CbcHmac256EncryptionKey objects to avoid computation of the derived keys + /// Factory classes cache the objects to avoid recomputation of the derived keys. /// private readonly ConcurrentDictionary _encryptionAlgorithms = - new ConcurrentDictionary(concurrencyLevel: 4 * Environment.ProcessorCount /* default value in ConcurrentDictionary*/, capacity: 2); + new(concurrencyLevel: 4 * Environment.ProcessorCount /* default value in ConcurrentDictionary */, capacity: 2); - public static SqlAeadAes256CbcHmac256Factory Instance => field ??= new(); + public static AeadAes256CbcHmac256Factory Instance => field ??= new(); /// - /// Creates an instance of AeadAes256CbcHmac256Algorithm class with a given key + /// Creates an instance of the class with a given root key. /// - /// Root key - /// Encryption Type. Expected values are either Deterministic or Randomized. - /// Encryption Algorithm. - /// + /// Root key. + /// Encryption type. Expected values are either Deterministic or Randomized. + /// Cryptographic algorithm. + /// An implementation of the AEAD_AES_256_CBC_HMAC_SHA256 cryptographic algorithm. internal override SqlClientEncryptionAlgorithm Create(SqlClientSymmetricKey encryptionKey, SqlClientEncryptionType encryptionType, string encryptionAlgorithm) { // Callers should have validated the encryption algorithm and the encryption key - Debug.Assert(encryptionKey != null); - Debug.Assert(string.Equals(encryptionAlgorithm, SqlAeadAes256CbcHmac256Algorithm.AlgorithmName, StringComparison.OrdinalIgnoreCase) == true); + Debug.Assert(string.Equals(encryptionAlgorithm, SqlAeadAes256CbcHmac256Algorithm.AlgorithmName, StringComparison.OrdinalIgnoreCase)); // Validate encryption type - if (!((encryptionType == SqlClientEncryptionType.Deterministic) || (encryptionType == SqlClientEncryptionType.Randomized))) + if (encryptionType is not SqlClientEncryptionType.Deterministic and not SqlClientEncryptionType.Randomized) { throw SQL.InvalidEncryptionType(SqlAeadAes256CbcHmac256Algorithm.AlgorithmName, encryptionType, @@ -45,33 +46,23 @@ internal override SqlClientEncryptionAlgorithm Create(SqlClientSymmetricKey encr SqlClientEncryptionType.Randomized); } - // Get the cached encryption algorithm if one exists or create a new one, add it to cache and use it - // - // For now, we only have one version. In future, we may need to parse the algorithm names to derive the version byte. - const byte algorithmVersion = 0x1; - - StringBuilder algorithmKeyBuilder = new StringBuilder(Convert.ToBase64String(encryptionKey.RootKey), SqlSecurityUtility.GetBase64LengthFromByteLength(encryptionKey.RootKey.Length) + 4/*separators, type and version*/); - -#if DEBUG - int capacity = algorithmKeyBuilder.Capacity; -#endif //DEBUG + // Get the cached cryptographic algorithm if one exists or create a new one, add it to cache and use it + int capacity = SqlSecurityUtility.GetBase64LengthFromByteLength(encryptionKey.RootKey.Length) + 4 /* Separators, type and version */; + StringBuilder algorithmKeyBuilder = new(Convert.ToBase64String(encryptionKey.RootKey), capacity); - algorithmKeyBuilder.Append(":"); + algorithmKeyBuilder.Append(':'); algorithmKeyBuilder.Append((int)encryptionType); - algorithmKeyBuilder.Append(":"); - algorithmKeyBuilder.Append(algorithmVersion); + algorithmKeyBuilder.Append(':'); + algorithmKeyBuilder.Append(SqlAeadAes256CbcHmac256Algorithm.CurrentVersion); string algorithmKey = algorithmKeyBuilder.ToString(); -#if DEBUG Debug.Assert(algorithmKey.Length <= capacity, "We needed to allocate a larger array"); -#endif //DEBUG - SqlAeadAes256CbcHmac256Algorithm aesAlgorithm; - if (!_encryptionAlgorithms.TryGetValue(algorithmKey, out aesAlgorithm)) + if (!_encryptionAlgorithms.TryGetValue(algorithmKey, out SqlAeadAes256CbcHmac256Algorithm? aesAlgorithm)) { - SqlAeadAes256CbcHmac256EncryptionKey encryptedKey = new SqlAeadAes256CbcHmac256EncryptionKey(encryptionKey.RootKey, SqlAeadAes256CbcHmac256Algorithm.AlgorithmName); - aesAlgorithm = new SqlAeadAes256CbcHmac256Algorithm(encryptedKey, encryptionType, algorithmVersion); + SqlAeadAes256CbcHmac256EncryptionKey encryptedKey = new(encryptionKey.RootKey, SqlAeadAes256CbcHmac256Algorithm.AlgorithmName); + aesAlgorithm = new SqlAeadAes256CbcHmac256Algorithm(encryptedKey, encryptionType, SqlAeadAes256CbcHmac256Algorithm.CurrentVersion); // In case multiple threads reach here at the same time, the first one adds the value // the second one will be a no-op, the allocated memory will be claimed by Garbage Collector. diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/Utility.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/Utility.cs index a8a76b9e7f..024bb86e26 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/Utility.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/Utility.cs @@ -21,9 +21,9 @@ public static class Utility // reflections public static Assembly systemData = Assembly.GetAssembly(typeof(SqlConnection)); public static Type sqlClientSymmetricKey = systemData.GetType("Microsoft.Data.SqlClient.SqlClientSymmetricKey"); - public static Type sqlAeadAes256CbcHmac256Factory = systemData.GetType("Microsoft.Data.SqlClient.SqlAeadAes256CbcHmac256Factory"); + public static Type aeadAes256CbcHmac256Factory = systemData.GetType("Microsoft.Data.SqlClient.AlwaysEncrypted.AeadAes256CbcHmac256Factory"); public static ConstructorInfo sqlColumnEncryptionKeyConstructor = sqlClientSymmetricKey.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(byte[]) }, null); - public static MethodInfo sqlAeadAes256CbcHmac256FactoryCreate = sqlAeadAes256CbcHmac256Factory.GetMethod("Create", BindingFlags.Instance | BindingFlags.NonPublic); + public static MethodInfo aeadAes256CbcHmac256FactoryCreate = aeadAes256CbcHmac256Factory.GetMethod("Create", BindingFlags.Instance | BindingFlags.NonPublic); public static Type sqlClientEncryptionAlgorithm = systemData.GetType("Microsoft.Data.SqlClient.SqlClientEncryptionAlgorithm"); public static MethodInfo sqlClientEncryptionAlgorithmEncryptData = sqlClientEncryptionAlgorithm.GetMethod("EncryptData", BindingFlags.Instance | BindingFlags.NonPublic); public static Type SqlCipherMetadata = systemData.GetType("Microsoft.Data.SqlClient.SqlCipherMetadata"); @@ -191,11 +191,11 @@ internal static byte[] EncryptDataUsingAED(byte[] plainTextData, byte[] key, CCo Object columnEncryptionKey = sqlColumnEncryptionKeyConstructor.Invoke(new object[] { key }); Assert.True(columnEncryptionKey != null); - Object aesFactory = Activator.CreateInstance(sqlAeadAes256CbcHmac256Factory); + Object aesFactory = Activator.CreateInstance(aeadAes256CbcHmac256Factory); Assert.True(aesFactory != null); object[] parameters = new object[] { columnEncryptionKey, encryptionType, ColumnEncryptionAlgorithmName }; - Object authenticatedAES = sqlAeadAes256CbcHmac256FactoryCreate.Invoke(aesFactory, parameters); + Object authenticatedAES = aeadAes256CbcHmac256FactoryCreate.Invoke(aesFactory, parameters); Assert.True(authenticatedAES != null); parameters = new object[] { plainTextData }; @@ -259,11 +259,11 @@ internal static byte[] DecryptDataUsingAED(byte[] encryptedCellBlob, byte[] key, Object columnEncryptionKey = sqlColumnEncryptionKeyConstructor.Invoke(new object[] { key }); Assert.True(columnEncryptionKey != null); - Object aesFactory = Activator.CreateInstance(sqlAeadAes256CbcHmac256Factory); + Object aesFactory = Activator.CreateInstance(aeadAes256CbcHmac256Factory); Assert.True(aesFactory != null); object[] parameters = new object[] { columnEncryptionKey, encryptionType, ColumnEncryptionAlgorithmName }; - Object authenticatedAES = sqlAeadAes256CbcHmac256FactoryCreate.Invoke(aesFactory, parameters); + Object authenticatedAES = aeadAes256CbcHmac256FactoryCreate.Invoke(aesFactory, parameters); Assert.True(authenticatedAES != null); parameters = new object[] { encryptedCellBlob }; diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/AlwaysEncrypted/NativeAeadBaseline.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/AlwaysEncrypted/NativeAeadBaseline.cs index 945351056f..04021a2cc0 100644 --- a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/AlwaysEncrypted/NativeAeadBaseline.cs +++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/AlwaysEncrypted/NativeAeadBaseline.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. @@ -111,7 +111,7 @@ public class NativeAeadBaseline public void Known_Plaintext_Encrypts_To_Known_FinalCell(byte[] plainText, byte[] rootKey, byte[] expectedFinalCell) { SqlClientSymmetricKey cek = new(rootKey); - SqlAeadAes256CbcHmac256Factory aeadFactory = new(); + AeadAes256CbcHmac256Factory aeadFactory = new(); SqlClientEncryptionAlgorithm aeadAlgorithm = aeadFactory.Create(cek, SqlClientEncryptionType.Deterministic, SqlAeadAes256CbcHmac256Algorithm.AlgorithmName); byte[] encryptedData = aeadAlgorithm.EncryptData(plainText); @@ -130,7 +130,7 @@ public void Known_Plaintext_Encrypts_To_Known_FinalCell(byte[] plainText, byte[] public void Known_FinalCell_Decrypts_To_Known_Plaintext(byte[] expectedPlaintext, byte[] rootKey, byte[] finalCell) { SqlClientSymmetricKey cek = new(rootKey); - SqlAeadAes256CbcHmac256Factory aeadFactory = new(); + AeadAes256CbcHmac256Factory aeadFactory = new(); SqlClientEncryptionAlgorithm aeadAlgorithm = aeadFactory.Create(cek, SqlClientEncryptionType.Deterministic, SqlAeadAes256CbcHmac256Algorithm.AlgorithmName); byte[] decryptedData = aeadAlgorithm.DecryptData(finalCell); From 1766891447ead79352ad1a7fa400ba72ab0b6ac3 Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Sun, 5 Apr 2026 22:42:27 +0100 Subject: [PATCH 4/4] Move AeadAes256CbcHmac256Factory --- .../netcore/src/Microsoft.Data.SqlClient.csproj | 6 +++--- .../netfx/src/Microsoft.Data.SqlClient.csproj | 6 +++--- .../AeadAes256CbcHmac256Factory.cs} | 5 ++--- .../src/Microsoft/Data/SqlClient/EnclaveDelegate.cs | 1 + .../Data/SqlClient/AlwaysEncrypted/NativeAeadBaseline.cs | 1 + 5 files changed, 10 insertions(+), 9 deletions(-) rename src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/{SqlAeadAes256CbcHmac256Factory.cs => AlwaysEncrypted/AeadAes256CbcHmac256Factory.cs} (96%) 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 fea48a69e7..d7cf669e16 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -219,6 +219,9 @@ Microsoft\Data\ProviderBase\TimeoutTimer.cs + + Microsoft\Data\SqlClient\AlwaysEncrypted\AeadAes256CbcHmac256Factory.cs + Microsoft\Data\SqlClient\AlwaysEncrypted\ColumnMasterKeyMetadata.cs @@ -645,9 +648,6 @@ Microsoft\Data\SqlClient\SqlAeadAes256CbcHmac256EncryptionKey.cs - - Microsoft\Data\SqlClient\SqlAeadAes256CbcHmac256Factory.cs - Microsoft\Data\SqlClient\SqlAppContextSwitchManager.netcore.cs 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 f04d8fc4c4..ba4c9ebd50 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -304,6 +304,9 @@ Microsoft\Data\SqlClient\ActiveDirectoryAuthenticationTimeoutRetryHelper.cs + + Microsoft\Data\SqlClient\AlwaysEncrypted\AeadAes256CbcHmac256Factory.cs + Microsoft\Data\SqlClient\AlwaysEncrypted\ColumnMasterKeyMetadata.cs @@ -640,9 +643,6 @@ Microsoft\Data\SqlClient\SqlAeadAes256CbcHmac256EncryptionKey.cs - - Microsoft\Data\SqlClient\SqlAeadAes256CbcHmac256Factory.cs - Microsoft\Data\SqlClient\SqlAuthenticationParametersBuilder.cs diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlAeadAes256CbcHmac256Factory.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AlwaysEncrypted/AeadAes256CbcHmac256Factory.cs similarity index 96% rename from src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlAeadAes256CbcHmac256Factory.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AlwaysEncrypted/AeadAes256CbcHmac256Factory.cs index 7a87a33e91..ee38adb351 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlAeadAes256CbcHmac256Factory.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AlwaysEncrypted/AeadAes256CbcHmac256Factory.cs @@ -2,7 +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 Microsoft.Data.SqlClient.AlwaysEncrypted; using System; using System.Collections.Concurrent; using System.Diagnostics; @@ -10,12 +9,12 @@ #nullable enable -namespace Microsoft.Data.SqlClient +namespace Microsoft.Data.SqlClient.AlwaysEncrypted { /// /// This is a factory class for AEAD_AES_256_CBC_HMAC_SHA256. /// - internal class AeadAes256CbcHmac256Factory : EncryptionAlgorithmFactory + internal sealed class AeadAes256CbcHmac256Factory : EncryptionAlgorithmFactory { /// /// Factory classes cache the objects to avoid recomputation of the derived keys. diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs index f0439725cf..4248e9285f 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Microsoft.Data.SqlClient.AlwaysEncrypted; using System; using System.Collections.Concurrent; using System.Collections.Generic; diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/AlwaysEncrypted/NativeAeadBaseline.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/AlwaysEncrypted/NativeAeadBaseline.cs index 04021a2cc0..cb8f02191a 100644 --- a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/AlwaysEncrypted/NativeAeadBaseline.cs +++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/AlwaysEncrypted/NativeAeadBaseline.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Microsoft.Data.SqlClient.AlwaysEncrypted; using System; using System.Collections.Generic; using System.Linq;