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;