From 769b76efb76e19dfcdbc598a849f81841fa75ef6 Mon Sep 17 00:00:00 2001 From: DavoudEshtehari <61173489+DavoudEshtehari@users.noreply.github.com> Date: Thu, 27 Jan 2022 13:16:38 -0800 Subject: [PATCH] [4.1.0] | Add new Attestation Protocol "None" (#1489) --- .../SqlConnection.xml | 2 +- .../SqlConnectionAttestationProtocol.xml | 6 +- .../netcore/ref/Microsoft.Data.SqlClient.cs | 6 +- .../src/Microsoft.Data.SqlClient.csproj | 7 +- .../SimulatorEnclaveProvider.NetCoreApp.cs | 114 ------------------ .../Microsoft/Data/SqlClient/SqlCommand.cs | 14 ++- .../src/Microsoft/Data/SqlClient/TdsEnums.cs | 6 +- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 38 ++---- .../netfx/ref/Microsoft.Data.SqlClient.cs | 6 +- .../netfx/src/Microsoft.Data.SqlClient.csproj | 6 +- .../SqlClient/SimulatorEnclaveProvider.cs | 114 ------------------ .../Microsoft/Data/SqlClient/SqlCommand.cs | 14 ++- .../src/Microsoft/Data/SqlClient/TdsEnums.cs | 6 +- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 50 +++----- .../Data/Common/DbConnectionStringCommon.cs | 19 +-- .../AzureAttestationBasedEnclaveProvider.cs | 2 +- .../Data/SqlClient/EnclaveDelegate.Crypto.cs | 30 +---- .../NoneAttestationEnclaveProvider.cs | 106 ++++++++++++++++ .../VirtualSecureModeEnclaveProviderBase.cs | 4 +- .../ConnectionStringBuilderShould.cs | 36 +++++- .../SqlConnectionShould.cs | 11 ++ .../ManualTests/AlwaysEncrypted/ApiShould.cs | 18 +++ .../TestFixtures/SQLSetupStrategy.cs | 1 + .../ManualTests/DataCommon/DataTestUtility.cs | 10 ++ .../SQL/ExceptionTest/ExceptionTest.cs | 10 ++ .../Config.cs | 1 + .../config.default.json | 1 + 27 files changed, 258 insertions(+), 380 deletions(-) delete mode 100644 src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SimulatorEnclaveProvider.NetCoreApp.cs delete mode 100644 src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SimulatorEnclaveProvider.cs create mode 100644 src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/NoneAttestationEnclaveProvider.cs diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml index ae9ed935b5..96a78643c8 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml @@ -527,7 +527,7 @@ End Module |Application Intent

-or-

ApplicationIntent|ReadWrite|Declares the application workload type when connecting to a server. Possible values are `ReadOnly` and `ReadWrite`. For example:

`ApplicationIntent=ReadOnly`

For more information about SqlClient support for Always On Availability Groups, see [SqlClient Support for High Availability, Disaster Recovery](/sql/connect/ado-net/sql/sqlclient-support-high-availability-disaster-recovery).| |Application Name|N/A|The name of the application. If no application name is provided, 'Framework Microsoft SqlClient Data Provider' when running on .NET Framework and 'Core Microsoft SqlClient Data Provider' otherwise.

An application name can be 128 characters or less.| |AttachDBFilename

-or-

Extended Properties

-or-

Initial File Name|N/A|The name of the primary database file, including the full path name of an attachable database. AttachDBFilename is only supported for primary data files with an .mdf extension.

If the value of the AttachDBFileName key is specified in the connection string, the database is attached and becomes the default database for the connection.

If this key is not specified and if the database was previously attached, the database will not be reattached. The previously attached database will be used as the default database for the connection.

If this key is specified together with the AttachDBFileName key, the value of this key will be used as the alias. However, if the name is already used in another attached database, the connection will fail.

The path may be absolute or relative by using the DataDirectory substitution string. If DataDirectory is used, the database file must exist within a subdirectory of the directory pointed to by the substitution string. **Note:** Remote server, HTTP, and UNC path names are not supported.

The database name must be specified with the keyword 'database' (or one of its aliases) as in the following:

"AttachDbFileName=|DataDirectory|\data\YourDB.mdf;integrated security=true;database=YourDatabase"

An error will be generated if a log file exists in the same directory as the data file and the 'database' keyword is used when attaching the primary data file. In this case, remove the log file. Once the database is attached, a new log file will be automatically generated based on the physical path.| -|Attestation Protocol|N/A|Gets or sets the value of Attestation Protocol.

Valid values are:
`AAS`
`HGS`| +|Attestation Protocol|N/A|Gets or sets the value of Attestation Protocol.

Valid values are:
`AAS`
`HGS`
`None`| |Authentication|N/A|The authentication method used for [Connecting to SQL Database By Using Azure Active Directory Authentication](https://azure.microsoft.com/documentation/articles/sql-database-aad-authentication/#7-connect-to-your-database-by-using-azure-active-directory-identities).

Valid values are:

`Active Directory Integrated`, `Active Directory Interactive`, `Active Directory Password`, `Active Directory Service Principal`, `Active Directory Device Code Flow`, `Active Directory Managed Identity`, `Active Directory MSI`, `Active Directory Default`, `Sql Password`.| |Column Encryption Setting|disabled|Enables or disables [Always Encrypted](/sql/relational-databases/security/encryption/always-encrypted-database-engine) functionality for the connection. Supported values are: `enabled` and `disabled`| |Command Timeout|30|The default wait time (in seconds) before terminating the attempt to execute a command and generating an error.

Valid values are greater than or equal to 0 and less than or equal to 2147483647.| diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionAttestationProtocol.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionAttestationProtocol.xml index d5f32ef2d3..db98ad4fdf 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionAttestationProtocol.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionAttestationProtocol.xml @@ -13,10 +13,10 @@ Attestation portocol for Azure Attestation Service 1 - - Attestation protocol for Simulator + + Attestation protocol for no attestation. Only compatible with Virtualization-based security (VBS) enclaves. An Enclave Attestation Url is not required when using this protocol. 2 - + Attestation protocol for Host Guardian Service 3 diff --git a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs index 22cbb29f6e..3d63a3f9db 100644 --- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs @@ -472,10 +472,8 @@ public enum SqlConnectionAttestationProtocol /// AAS = 1, -#if ENCLAVE_SIMULATOR - /// - SIM = 2, -#endif + /// + None = 2, /// HGS = 3 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 5b808d9b78..d905ad16a9 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -488,6 +488,9 @@ Microsoft\Data\SqlClient\VirtualSecureModeEnclaveProvider.cs + + Microsoft\Data\SqlClient\NoneAttestationEnclaveProvider.cs + Microsoft\Data\SqlClient\VirtualSecureModeEnclaveProviderBase.cs @@ -507,9 +510,6 @@ - - - Resources\StringsHelper.NetCore.cs @@ -558,7 +558,6 @@ Microsoft\Data\SqlClient\SqlSequentialStream.cs - diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SimulatorEnclaveProvider.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SimulatorEnclaveProvider.NetCoreApp.cs deleted file mode 100644 index fdf6d67d54..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SimulatorEnclaveProvider.NetCoreApp.cs +++ /dev/null @@ -1,114 +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.Generic; -using System.Diagnostics; -using System.Linq; -using System.Runtime.Caching; -using System.Security.Cryptography; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using System.Collections.Concurrent; - -namespace Microsoft.Data.SqlClient -{ - internal class SimulatorEnclaveProvider : EnclaveProviderBase - { - private static readonly int EnclaveSessionHandleSize = 8; - - // When overridden in a derived class, looks up an existing enclave session information in the enclave session cache. - // If the enclave provider doesn't implement enclave session caching, this method is expected to return null in the sqlEnclaveSession parameter. - internal override void GetEnclaveSession(EnclaveSessionParameters enclaveSessionParameters, bool generateCustomData, out SqlEnclaveSession sqlEnclaveSession, out long counter, out byte[] customData, out int customDataLength) - { - GetEnclaveSessionHelper(enclaveSessionParameters, false, out sqlEnclaveSession, out counter, out customData, out customDataLength); - } - - // Gets the information that SqlClient subsequently uses to initiate the process of attesting the enclave and to establish a secure session with the enclave. - internal override SqlEnclaveAttestationParameters GetAttestationParameters(string attestationUrl, byte[] customData, int customDataLength) - { - // The key derivation function and hash algorithm name are specified when key derivation is performed - ECDiffieHellman clientDHKey = ECDiffieHellman.Create(); - clientDHKey.KeySize = 384; - - return new SqlEnclaveAttestationParameters(2, new byte[] { }, clientDHKey); - } - - // When overridden in a derived class, performs enclave attestation, generates a symmetric key for the session, creates a an enclave session and stores the session information in the cache. - internal override void CreateEnclaveSession(byte[] attestationInfo, ECDiffieHellman clientDHKey, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength, out SqlEnclaveSession sqlEnclaveSession, out long counter) - { - ////for simulator: enclave does not send public key, and sends an empty attestation info - //// The only non-trivial content it sends is the session setup info (DH pubkey of enclave) - - sqlEnclaveSession = null; - counter = 0; - try - { - ThreadRetryCache.Remove(Thread.CurrentThread.ManagedThreadId.ToString()); - sqlEnclaveSession = GetEnclaveSessionFromCache(enclaveSessionParameters, out counter); - - if (sqlEnclaveSession == null) - { - if (!string.IsNullOrEmpty(enclaveSessionParameters.AttestationUrl)) - { - ////Read AttestationInfo - int attestationInfoOffset = 0; - uint sizeOfTrustedModuleAttestationInfoBuffer = BitConverter.ToUInt32(attestationInfo, attestationInfoOffset); - attestationInfoOffset += sizeof(UInt32); - int sizeOfTrustedModuleAttestationInfoBufferInt = checked((int)sizeOfTrustedModuleAttestationInfoBuffer); - Debug.Assert(sizeOfTrustedModuleAttestationInfoBuffer == 0); - - ////read secure session info - uint sizeOfSecureSessionInfoResponse = BitConverter.ToUInt32(attestationInfo, attestationInfoOffset); - attestationInfoOffset += sizeof(UInt32); - - byte[] enclaveSessionHandle = new byte[EnclaveSessionHandleSize]; - Buffer.BlockCopy(attestationInfo, attestationInfoOffset, enclaveSessionHandle, 0, EnclaveSessionHandleSize); - attestationInfoOffset += EnclaveSessionHandleSize; - - uint sizeOfTrustedModuleDHPublicKeyBuffer = BitConverter.ToUInt32(attestationInfo, attestationInfoOffset); - attestationInfoOffset += sizeof(UInt32); - uint sizeOfTrustedModuleDHPublicKeySignatureBuffer = BitConverter.ToUInt32(attestationInfo, attestationInfoOffset); - attestationInfoOffset += sizeof(UInt32); - int sizeOfTrustedModuleDHPublicKeyBufferInt = checked((int)sizeOfTrustedModuleDHPublicKeyBuffer); - - byte[] trustedModuleDHPublicKey = new byte[sizeOfTrustedModuleDHPublicKeyBuffer]; - Buffer.BlockCopy(attestationInfo, attestationInfoOffset, trustedModuleDHPublicKey, 0, - sizeOfTrustedModuleDHPublicKeyBufferInt); - attestationInfoOffset += sizeOfTrustedModuleDHPublicKeyBufferInt; - - byte[] trustedModuleDHPublicKeySignature = new byte[sizeOfTrustedModuleDHPublicKeySignatureBuffer]; - Buffer.BlockCopy(attestationInfo, attestationInfoOffset, trustedModuleDHPublicKeySignature, 0, - checked((int)sizeOfTrustedModuleDHPublicKeySignatureBuffer)); - - byte[] sharedSecret; - using ECDiffieHellman ecdh = KeyConverter.CreateECDiffieHellmanFromPublicKeyBlob(trustedModuleDHPublicKey); - sharedSecret = KeyConverter.DeriveKey(clientDHKey, ecdh.PublicKey); - long sessionId = BitConverter.ToInt64(enclaveSessionHandle, 0); - sqlEnclaveSession = AddEnclaveSessionToCache(enclaveSessionParameters, sharedSecret, sessionId, out counter); - } - else - { - throw new AlwaysEncryptedAttestationException(Strings.FailToCreateEnclaveSession); - } - } - } - finally - { - UpdateEnclaveSessionLockStatus(sqlEnclaveSession); - } - } - - /// - /// When overridden in a derived class, looks up and evicts an enclave session from the enclave session cache, if the provider implements session caching. - /// - /// The set of parameters required for enclave session. - /// The session to be invalidated. - internal override void InvalidateEnclaveSession(EnclaveSessionParameters enclaveSessionParameters, SqlEnclaveSession enclaveSessionToInvalidate) - { - InvalidateEnclaveSessionHelper(enclaveSessionParameters, enclaveSessionToInvalidate); - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs index 85fe6156ab..6d14a45798 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -204,10 +204,9 @@ internal bool IsColumnEncryptionEnabled } } - internal bool ShouldUseEnclaveBasedWorkflow - { - get { return !string.IsNullOrWhiteSpace(_activeConnection.EnclaveAttestationUrl) && IsColumnEncryptionEnabled; } - } + internal bool ShouldUseEnclaveBasedWorkflow => + (!string.IsNullOrWhiteSpace(_activeConnection.EnclaveAttestationUrl) || Connection.AttestationProtocol == SqlConnectionAttestationProtocol.None) && + IsColumnEncryptionEnabled; /// /// Per-command custom providers. It can be provided by the user and can be set more than once. @@ -4211,7 +4210,7 @@ private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDi if (isRequestedByEnclave) { - if (string.IsNullOrWhiteSpace(this.Connection.EnclaveAttestationUrl)) + if (string.IsNullOrWhiteSpace(this.Connection.EnclaveAttestationUrl) && Connection.AttestationProtocol != SqlConnectionAttestationProtocol.None) { throw SQL.NoAttestationUrlSpecifiedForEnclaveBasedQuerySpDescribe(this._activeConnection.Parser.EnclaveType); } @@ -4636,8 +4635,11 @@ private void GenerateEnclavePackage() return; } - if (string.IsNullOrWhiteSpace(this._activeConnection.EnclaveAttestationUrl)) + if (string.IsNullOrWhiteSpace(this._activeConnection.EnclaveAttestationUrl) && + Connection.AttestationProtocol != SqlConnectionAttestationProtocol.None) + { throw SQL.NoAttestationUrlSpecifiedForEnclaveBasedQueryGeneratingEnclavePackage(this._activeConnection.Parser.EnclaveType); + } string enclaveType = this._activeConnection.Parser.EnclaveType; if (string.IsNullOrWhiteSpace(enclaveType)) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs index 728e521847..fd4f4313e6 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs @@ -1070,10 +1070,8 @@ public enum SqlConnectionAttestationProtocol /// AAS = 1, -#if ENCLAVE_SIMULATOR - /// - SIM = 2, -#endif + /// + None = 2, /// HGS = 3 diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs index 997b610458..f5077cb531 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -3176,7 +3176,7 @@ private bool TryProcessFeatureExtAck(TdsParserStateObject stateObj) if (TceVersionSupported < TdsEnums.MIN_TCE_VERSION_WITH_ENCLAVE_SUPPORT) { // Check if enclave attestation url was specified and server does not support enclave computations and we aren't going to be routed to another server. - if (!string.IsNullOrWhiteSpace(_connHandler.ConnectionOptions.EnclaveAttestationUrl) && SqlConnectionAttestationProtocol.NotSpecified != attestationProtocol) + if (!string.IsNullOrWhiteSpace(_connHandler.ConnectionOptions.EnclaveAttestationUrl) && attestationProtocol != SqlConnectionAttestationProtocol.NotSpecified) { throw SQL.EnclaveComputationsNotSupported(); } @@ -3184,14 +3184,14 @@ private bool TryProcessFeatureExtAck(TdsParserStateObject stateObj) { throw SQL.AttestationURLNotSupported(); } - else if (SqlConnectionAttestationProtocol.NotSpecified != _connHandler.ConnectionOptions.AttestationProtocol) + else if (_connHandler.ConnectionOptions.AttestationProtocol != SqlConnectionAttestationProtocol.NotSpecified) { throw SQL.AttestationProtocolNotSupported(); } } // Check if enclave attestation url was specified and server does not return an enclave type and we aren't going to be routed to another server. - if (!string.IsNullOrWhiteSpace(_connHandler.ConnectionOptions.EnclaveAttestationUrl)) + if (!string.IsNullOrWhiteSpace(_connHandler.ConnectionOptions.EnclaveAttestationUrl) || attestationProtocol == SqlConnectionAttestationProtocol.None) { if (string.IsNullOrWhiteSpace(EnclaveType)) { @@ -3202,7 +3202,7 @@ private bool TryProcessFeatureExtAck(TdsParserStateObject stateObj) // Check if the attestation protocol is specified and supports the enclave type. if (SqlConnectionAttestationProtocol.NotSpecified != attestationProtocol && !IsValidAttestationProtocol(attestationProtocol, EnclaveType)) { - throw SQL.AttestationProtocolNotSupportEnclaveType(ConvertAttestationProtocolToString(attestationProtocol), EnclaveType); + throw SQL.AttestationProtocolNotSupportEnclaveType(attestationProtocol.ToString(), EnclaveType); } } } @@ -3217,10 +3217,8 @@ private bool IsValidAttestationProtocol(SqlConnectionAttestationProtocol attesta { case TdsEnums.ENCLAVE_TYPE_VBS: if (attestationProtocol != SqlConnectionAttestationProtocol.AAS -#if ENCLAVE_SIMULATOR - && attestationProtocol != SqlConnectionAttestationProtocol.SIM -#endif - && attestationProtocol != SqlConnectionAttestationProtocol.HGS) + && attestationProtocol != SqlConnectionAttestationProtocol.HGS + && attestationProtocol != SqlConnectionAttestationProtocol.None) { return false; } @@ -3229,7 +3227,7 @@ private bool IsValidAttestationProtocol(SqlConnectionAttestationProtocol attesta case TdsEnums.ENCLAVE_TYPE_SGX: #if ENCLAVE_SIMULATOR if (attestationProtocol != SqlConnectionAttestationProtocol.AAS - && attestationProtocol != SqlConnectionAttestationProtocol.SIM) + && attestationProtocol != SqlConnectionAttestationProtocol.None) #else if (attestationProtocol != SqlConnectionAttestationProtocol.AAS) #endif @@ -3240,7 +3238,7 @@ private bool IsValidAttestationProtocol(SqlConnectionAttestationProtocol attesta #if ENCLAVE_SIMULATOR case TdsEnums.ENCLAVE_TYPE_SIMULATOR: - if (attestationProtocol != SqlConnectionAttestationProtocol.SIM) + if (attestationProtocol != SqlConnectionAttestationProtocol.None) { return false; } @@ -3254,26 +3252,6 @@ private bool IsValidAttestationProtocol(SqlConnectionAttestationProtocol attesta return true; } - private string ConvertAttestationProtocolToString(SqlConnectionAttestationProtocol attestationProtocol) - { - switch (attestationProtocol) - { - case SqlConnectionAttestationProtocol.AAS: - return "AAS"; - - case SqlConnectionAttestationProtocol.HGS: - return "HGS"; - -#if ENCLAVE_SIMULATOR - case SqlConnectionAttestationProtocol.SIM: - return "SIM"; -#endif - - default: - return "NotSpecified"; - } - } - private bool TryReadByteString(TdsParserStateObject stateObj, out string value) { value = string.Empty; diff --git a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs index eca9091a58..6a6ae668e7 100644 --- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs @@ -889,10 +889,8 @@ public enum SqlConnectionAttestationProtocol /// AAS = 1, -#if ENCLAVE_SIMULATOR - /// - SIM = 2, -#endif + /// + None = 2, /// HGS = 3 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 df8e73b2d3..44fa3e0f15 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -165,6 +165,9 @@ Microsoft\Data\SqlClient\AzureAttestationBasedEnclaveProvider.cs + + Microsoft\Data\SqlClient\NoneAttestationEnclaveProvider.cs + Microsoft\Data\SqlClient\EnclaveDelegate.cs @@ -629,9 +632,6 @@ - - - diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SimulatorEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SimulatorEnclaveProvider.cs deleted file mode 100644 index 9a8550934c..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SimulatorEnclaveProvider.cs +++ /dev/null @@ -1,114 +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.Generic; -using System.Diagnostics; -using System.Linq; -using System.Runtime.Caching; -using System.Security.Cryptography; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using System.Collections.Concurrent; - -namespace Microsoft.Data.SqlClient -{ - internal class SimulatorEnclaveProvider : EnclaveProviderBase - { - private static readonly int EnclaveSessionHandleSize = 8; - - // When overridden in a derived class, looks up an existing enclave session information in the enclave session cache. - // If the enclave provider doesn't implement enclave session caching, this method is expected to return null in the sqlEnclaveSession parameter. - internal override void GetEnclaveSession(EnclaveSessionParameters enclaveSessionParameters, bool generateCustomData, out SqlEnclaveSession sqlEnclaveSession, out long counter, out byte[] customData, out int customDataLength) - { - GetEnclaveSessionHelper(enclaveSessionParameters, false, out sqlEnclaveSession, out counter, out customData, out customDataLength); - } - - // Gets the information that SqlClient subsequently uses to initiate the process of attesting the enclave and to establish a secure session with the enclave. - internal override SqlEnclaveAttestationParameters GetAttestationParameters(string attestationUrl, byte[] customData, int customDataLength) - { - ECDiffieHellmanCng clientDHKey = new ECDiffieHellmanCng(384); - clientDHKey.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash; - clientDHKey.HashAlgorithm = CngAlgorithm.Sha256; - - return new SqlEnclaveAttestationParameters(2, new byte[] { }, clientDHKey); - } - - // When overridden in a derived class, performs enclave attestation, generates a symmetric key for the session, creates a an enclave session and stores the session information in the cache. - internal override void CreateEnclaveSession(byte[] attestationInfo, ECDiffieHellman clientDHKey, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength, out SqlEnclaveSession sqlEnclaveSession, out long counter) - { - ////for simulator: enclave does not send public key, and sends an empty attestation info - //// The only non-trivial content it sends is the session setup info (DH pubkey of enclave) - - sqlEnclaveSession = null; - counter = 0; - try - { - ThreadRetryCache.Remove(Thread.CurrentThread.ManagedThreadId.ToString()); - sqlEnclaveSession = GetEnclaveSessionFromCache(enclaveSessionParameters, out counter); - - if (sqlEnclaveSession == null) - { - if (!string.IsNullOrEmpty(enclaveSessionParameters.AttestationUrl)) - { - ////Read AttestationInfo - int attestationInfoOffset = 0; - uint sizeOfTrustedModuleAttestationInfoBuffer = BitConverter.ToUInt32(attestationInfo, attestationInfoOffset); - attestationInfoOffset += sizeof(UInt32); - int sizeOfTrustedModuleAttestationInfoBufferInt = checked((int)sizeOfTrustedModuleAttestationInfoBuffer); - Debug.Assert(sizeOfTrustedModuleAttestationInfoBuffer == 0); - - ////read secure session info - uint sizeOfSecureSessionInfoResponse = BitConverter.ToUInt32(attestationInfo, attestationInfoOffset); - attestationInfoOffset += sizeof(UInt32); - - byte[] enclaveSessionHandle = new byte[EnclaveSessionHandleSize]; - Buffer.BlockCopy(attestationInfo, attestationInfoOffset, enclaveSessionHandle, 0, EnclaveSessionHandleSize); - attestationInfoOffset += EnclaveSessionHandleSize; - - uint sizeOfTrustedModuleDHPublicKeyBuffer = BitConverter.ToUInt32(attestationInfo, attestationInfoOffset); - attestationInfoOffset += sizeof(UInt32); - uint sizeOfTrustedModuleDHPublicKeySignatureBuffer = BitConverter.ToUInt32(attestationInfo, attestationInfoOffset); - attestationInfoOffset += sizeof(UInt32); - int sizeOfTrustedModuleDHPublicKeyBufferInt = checked((int)sizeOfTrustedModuleDHPublicKeyBuffer); - - byte[] trustedModuleDHPublicKey = new byte[sizeOfTrustedModuleDHPublicKeyBuffer]; - Buffer.BlockCopy(attestationInfo, attestationInfoOffset, trustedModuleDHPublicKey, 0, - sizeOfTrustedModuleDHPublicKeyBufferInt); - attestationInfoOffset += sizeOfTrustedModuleDHPublicKeyBufferInt; - - byte[] trustedModuleDHPublicKeySignature = new byte[sizeOfTrustedModuleDHPublicKeySignatureBuffer]; - Buffer.BlockCopy(attestationInfo, attestationInfoOffset, trustedModuleDHPublicKeySignature, 0, - checked((int)sizeOfTrustedModuleDHPublicKeySignatureBuffer)); - - byte[] sharedSecret; - using ECDiffieHellman ecdh = KeyConverter.CreateECDiffieHellmanFromPublicKeyBlob(trustedModuleDHPublicKey); - sharedSecret = KeyConverter.DeriveKey(clientDHKey, ecdh.PublicKey); - long sessionId = BitConverter.ToInt64(enclaveSessionHandle, 0); - sqlEnclaveSession = AddEnclaveSessionToCache(enclaveSessionParameters, sharedSecret, sessionId, out counter); - } - else - { - throw new AlwaysEncryptedAttestationException(Strings.FailToCreateEnclaveSession); - } - } - } - finally - { - UpdateEnclaveSessionLockStatus(sqlEnclaveSession); - } - } - - /// - /// When overridden in a derived class, looks up and evicts an enclave session from the enclave session cache, if the provider implements session caching. - /// - /// The set of parameters required for enclave session. - /// The session to be invalidated. - internal override void InvalidateEnclaveSession(EnclaveSessionParameters enclaveSessionParameters, SqlEnclaveSession enclaveSessionToInvalidate) - { - InvalidateEnclaveSessionHelper(enclaveSessionParameters, enclaveSessionToInvalidate); - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs index 35f5353f89..183a812a7a 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -160,10 +160,9 @@ internal bool IsColumnEncryptionEnabled } } - internal bool ShouldUseEnclaveBasedWorkflow - { - get { return !string.IsNullOrWhiteSpace(_activeConnection.EnclaveAttestationUrl) && IsColumnEncryptionEnabled; } - } + internal bool ShouldUseEnclaveBasedWorkflow => + (!string.IsNullOrWhiteSpace(_activeConnection.EnclaveAttestationUrl) || Connection.AttestationProtocol == SqlConnectionAttestationProtocol.None) && + IsColumnEncryptionEnabled; internal ConcurrentDictionary keysToBeSentToEnclave; internal bool requiresEnclaveComputations = false; @@ -4780,7 +4779,7 @@ private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDi if (isRequestedByEnclave) { - if (string.IsNullOrWhiteSpace(this.Connection.EnclaveAttestationUrl)) + if (string.IsNullOrWhiteSpace(this.Connection.EnclaveAttestationUrl) && Connection.AttestationProtocol != SqlConnectionAttestationProtocol.None) { throw SQL.NoAttestationUrlSpecifiedForEnclaveBasedQuerySpDescribe(this._activeConnection.Parser.EnclaveType); } @@ -5244,8 +5243,11 @@ private void GenerateEnclavePackage() return; } - if (string.IsNullOrWhiteSpace(this._activeConnection.EnclaveAttestationUrl)) + if (string.IsNullOrWhiteSpace(this._activeConnection.EnclaveAttestationUrl) && + Connection.AttestationProtocol != SqlConnectionAttestationProtocol.None) + { throw SQL.NoAttestationUrlSpecifiedForEnclaveBasedQueryGeneratingEnclavePackage(this._activeConnection.Parser.EnclaveType); + } string enclaveType = this._activeConnection.Parser.EnclaveType; if (string.IsNullOrWhiteSpace(enclaveType)) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs index 20a7fad79e..9ec2f3b4b7 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs @@ -1069,10 +1069,8 @@ public enum SqlConnectionAttestationProtocol /// AAS = 1, -#if ENCLAVE_SIMULATOR - /// - SIM = 2, -#endif + /// + None = 2, /// HGS = 3 diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index c22d0e5907..1cda7741bc 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -657,7 +657,7 @@ internal void Connect(ServerInfo serverInfo, // for DNS Caching phase 1 AssignPendingDNSInfo(serverInfo.UserProtocol, FQDNforDNSCahce); - if(!ClientOSEncryptionSupport) + if (!ClientOSEncryptionSupport) { //If encryption is required, an error will be thrown. if (encrypt) @@ -689,7 +689,7 @@ internal void Connect(ServerInfo serverInfo, // On Instance failure re-connect and flush SNI named instance cache. _physicalStateObj.SniContext = SniContext.Snix_Connect; - _physicalStateObj.CreatePhysicalSNIHandle(serverInfo.ExtendedServerName, ignoreSniOpenTimeout, timerExpire, + _physicalStateObj.CreatePhysicalSNIHandle(serverInfo.ExtendedServerName, ignoreSniOpenTimeout, timerExpire, out instanceName, _sniSpnBuffer, true, true, fParallel, transparentNetworkResolutionState, totalTimeout, _connHandler.ConnectionOptions.IPAddressPreference, serverInfo.ResolvedServerName); if (TdsEnums.SNI_SUCCESS != _physicalStateObj.Status) @@ -708,7 +708,7 @@ internal void Connect(ServerInfo serverInfo, AssignPendingDNSInfo(serverInfo.UserProtocol, FQDNforDNSCahce); SendPreLoginHandshake(instanceName, encrypt, !string.IsNullOrEmpty(certificate), useOriginalAddressInfo); - status = ConsumePreLoginHandshake(authType, encrypt, trustServerCert, integratedSecurity, serverCallback, clientCallback, + status = ConsumePreLoginHandshake(authType, encrypt, trustServerCert, integratedSecurity, serverCallback, clientCallback, out marsCapable, out _connHandler._fedAuthRequired); // Don't need to check for Sphinx failure, since we've already consumed @@ -3632,7 +3632,7 @@ private bool TryProcessFeatureExtAck(TdsParserStateObject stateObj) if (TceVersionSupported < TdsEnums.MIN_TCE_VERSION_WITH_ENCLAVE_SUPPORT) { // Check if enclave attestation url was specified and server does not support enclave computations and we aren't going to be routed to another server. - if (!string.IsNullOrWhiteSpace(_connHandler.ConnectionOptions.EnclaveAttestationUrl) && SqlConnectionAttestationProtocol.NotSpecified != attestationProtocol) + if (!string.IsNullOrWhiteSpace(_connHandler.ConnectionOptions.EnclaveAttestationUrl) && attestationProtocol != SqlConnectionAttestationProtocol.NotSpecified) { throw SQL.EnclaveComputationsNotSupported(); } @@ -3640,13 +3640,13 @@ private bool TryProcessFeatureExtAck(TdsParserStateObject stateObj) { throw SQL.AttestationURLNotSupported(); } - else if (SqlConnectionAttestationProtocol.NotSpecified != _connHandler.ConnectionOptions.AttestationProtocol) + else if (_connHandler.ConnectionOptions.AttestationProtocol != SqlConnectionAttestationProtocol.NotSpecified) { throw SQL.AttestationProtocolNotSupported(); } } // Check if enclave attestation url was specified and server does not return an enclave type and we aren't going to be routed to another server. - if (!string.IsNullOrWhiteSpace(_connHandler.ConnectionOptions.EnclaveAttestationUrl)) + if (!string.IsNullOrWhiteSpace(_connHandler.ConnectionOptions.EnclaveAttestationUrl) || attestationProtocol == SqlConnectionAttestationProtocol.None) { if (string.IsNullOrWhiteSpace(EnclaveType)) { @@ -3655,9 +3655,9 @@ private bool TryProcessFeatureExtAck(TdsParserStateObject stateObj) else { // Check if the attestation protocol is specified and supports the enclave type. - if (SqlConnectionAttestationProtocol.NotSpecified != attestationProtocol && !IsValidAttestationProtocol(attestationProtocol, EnclaveType)) + if (attestationProtocol != SqlConnectionAttestationProtocol.NotSpecified && !IsValidAttestationProtocol(attestationProtocol, EnclaveType)) { - throw SQL.AttestationProtocolNotSupportEnclaveType(ConvertAttestationProtocolToString(attestationProtocol), EnclaveType); + throw SQL.AttestationProtocolNotSupportEnclaveType(attestationProtocol.ToString(), EnclaveType); } } } @@ -3672,10 +3672,8 @@ private bool IsValidAttestationProtocol(SqlConnectionAttestationProtocol attesta { case TdsEnums.ENCLAVE_TYPE_VBS: if (attestationProtocol != SqlConnectionAttestationProtocol.AAS -#if ENCLAVE_SIMULATOR - && attestationProtocol != SqlConnectionAttestationProtocol.SIM -#endif - && attestationProtocol != SqlConnectionAttestationProtocol.HGS) + && attestationProtocol != SqlConnectionAttestationProtocol.HGS + && attestationProtocol != SqlConnectionAttestationProtocol.None) { return false; } @@ -3684,7 +3682,7 @@ private bool IsValidAttestationProtocol(SqlConnectionAttestationProtocol attesta case TdsEnums.ENCLAVE_TYPE_SGX: #if ENCLAVE_SIMULATOR if (attestationProtocol != SqlConnectionAttestationProtocol.AAS - && attestationProtocol != SqlConnectionAttestationProtocol.SIM) + && attestationProtocol != SqlConnectionAttestationProtocol.None) #else if (attestationProtocol != SqlConnectionAttestationProtocol.AAS) #endif @@ -3695,7 +3693,7 @@ private bool IsValidAttestationProtocol(SqlConnectionAttestationProtocol attesta #if ENCLAVE_SIMULATOR case TdsEnums.ENCLAVE_TYPE_SIMULATOR: - if (attestationProtocol != SqlConnectionAttestationProtocol.SIM) + if (attestationProtocol != SqlConnectionAttestationProtocol.None) { return false; } @@ -3709,26 +3707,6 @@ private bool IsValidAttestationProtocol(SqlConnectionAttestationProtocol attesta return true; } - private string ConvertAttestationProtocolToString(SqlConnectionAttestationProtocol attestationProtocol) - { - switch (attestationProtocol) - { - case SqlConnectionAttestationProtocol.AAS: - return "AAS"; - - case SqlConnectionAttestationProtocol.HGS: - return "HGS"; - -#if ENCLAVE_SIMULATOR - case SqlConnectionAttestationProtocol.SIM: - return "SIM"; -#endif - - default: - return "NotSpecified"; - } - } - private bool TryReadByteString(TdsParserStateObject stateObj, out string value) { value = string.Empty; @@ -6720,7 +6698,7 @@ internal bool TryReadSqlValue(SqlBuffer value, int length, TdsParserStateObject stateObj, SqlCommandColumnEncryptionSetting columnEncryptionOverride, - string columnName, + string columnName, SqlCommand command = null) { bool isPlp = md.metaType.IsPlp; @@ -10456,7 +10434,7 @@ internal Task TdsExecuteRPC(SqlCommand cmd, _SqlRPC[] rpcArray, int timeout, boo if (releaseConnectionLock) { task.ContinueWith( - static (Task _, object state) => ((TdsParser)state)._connHandler._parserLock.Release(), + static (Task _, object state) => ((TdsParser)state)._connHandler._parserLock.Release(), state: this, scheduler: TaskScheduler.Default ); diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/DbConnectionStringCommon.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/DbConnectionStringCommon.cs index aad002c116..0557ebaa75 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/DbConnectionStringCommon.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/DbConnectionStringCommon.cs @@ -715,13 +715,11 @@ internal static bool TryConvertToAttestationProtocol(string value, out SqlConnec result = SqlConnectionAttestationProtocol.AAS; return true; } -#if ENCLAVE_SIMULATOR - else if (StringComparer.InvariantCultureIgnoreCase.Equals(value, nameof(SqlConnectionAttestationProtocol.SIM))) + else if (StringComparer.InvariantCultureIgnoreCase.Equals(value, nameof(SqlConnectionAttestationProtocol.None))) { - result = SqlConnectionAttestationProtocol.SIM; + result = SqlConnectionAttestationProtocol.None; return true; } -#endif else { result = DbConnectionStringDefaults.AttestationProtocol; @@ -731,18 +729,11 @@ internal static bool TryConvertToAttestationProtocol(string value, out SqlConnec internal static bool IsValidAttestationProtocol(SqlConnectionAttestationProtocol value) { -#if ENCLAVE_SIMULATOR Debug.Assert(Enum.GetNames(typeof(SqlConnectionAttestationProtocol)).Length == 4, "SqlConnectionAttestationProtocol enum has changed, update needed"); return value == SqlConnectionAttestationProtocol.NotSpecified || value == SqlConnectionAttestationProtocol.HGS || value == SqlConnectionAttestationProtocol.AAS - || value == SqlConnectionAttestationProtocol.SIM; -#else - Debug.Assert(Enum.GetNames(typeof(SqlConnectionAttestationProtocol)).Length == 3, "SqlConnectionAttestationProtocol enum has changed, update needed"); - return value == SqlConnectionAttestationProtocol.NotSpecified - || value == SqlConnectionAttestationProtocol.HGS - || value == SqlConnectionAttestationProtocol.AAS; -#endif + || value == SqlConnectionAttestationProtocol.None; } internal static string AttestationProtocolToString(SqlConnectionAttestationProtocol value) @@ -753,9 +744,7 @@ internal static string AttestationProtocolToString(SqlConnectionAttestationProto { SqlConnectionAttestationProtocol.AAS => nameof(SqlConnectionAttestationProtocol.AAS), SqlConnectionAttestationProtocol.HGS => nameof(SqlConnectionAttestationProtocol.HGS), -#if ENCLAVE_SIMULATOR - SqlConnectionAttestationProtocol.SIM => nameof(SqlConnectionAttestationProtocol.SIM), -#endif + SqlConnectionAttestationProtocol.None => nameof(SqlConnectionAttestationProtocol.None), _ => null }; } 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 d08db25036..0e891924fb 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs @@ -49,7 +49,7 @@ internal class AzureAttestationEnclaveProvider : EnclaveProviderBase { #region Constants private const int DiffieHellmanKeySize = 384; - private const int AzureBasedAttestationProtocolId = 1; + private const int AzureBasedAttestationProtocolId = (int)SqlConnectionAttestationProtocol.AAS; private const int SigningKeyRetryInSec = 3; #endregion diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.Crypto.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.Crypto.cs index bf8786ef7d..d1e6c71527 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.Crypto.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.Crypto.cs @@ -101,13 +101,11 @@ private SqlColumnEncryptionEnclaveProvider GetEnclaveProvider(SqlConnectionAttes sqlColumnEncryptionEnclaveProvider = s_enclaveProviders[attestationProtocol]; break; -#if ENCLAVE_SIMULATOR - case SqlConnectionAttestationProtocol.SIM: - SimulatorEnclaveProvider simulatorEnclaveProvider = new SimulatorEnclaveProvider(); - s_enclaveProviders[attestationProtocol] = (SqlColumnEncryptionEnclaveProvider)simulatorEnclaveProvider; + case SqlConnectionAttestationProtocol.None: + NoneAttestationEnclaveProvider noneAttestationEnclaveProvider = new NoneAttestationEnclaveProvider(); + s_enclaveProviders[attestationProtocol] = noneAttestationEnclaveProvider; sqlColumnEncryptionEnclaveProvider = s_enclaveProviders[attestationProtocol]; break; -#endif default: break; @@ -116,7 +114,7 @@ private SqlColumnEncryptionEnclaveProvider GetEnclaveProvider(SqlConnectionAttes if (sqlColumnEncryptionEnclaveProvider == null) { - throw SQL.EnclaveProviderNotFound(enclaveType, ConvertAttestationProtocolToString(attestationProtocol)); + throw SQL.EnclaveProviderNotFound(enclaveType, attestationProtocol.ToString()); } return sqlColumnEncryptionEnclaveProvider; @@ -208,25 +206,5 @@ internal byte[] GetSerializedAttestationParameters(SqlEnclaveAttestationParamete return CombineByteArrays(attestationProtocolBytes, attestationProtocolInputLengthBytes, attestationProtocolInputBytes, clientDHPublicKeyLengthBytes, clientDHPublicKey); } - - private string ConvertAttestationProtocolToString(SqlConnectionAttestationProtocol attestationProtocol) - { - switch (attestationProtocol) - { - case SqlConnectionAttestationProtocol.AAS: - return "AAS"; - - case SqlConnectionAttestationProtocol.HGS: - return "HGS"; - -#if ENCLAVE_SIMULATOR - case SqlConnectionAttestationProtocol.SIM: - return "SIM"; -#endif - - default: - return "NotSpecified"; - } - } } } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/NoneAttestationEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/NoneAttestationEnclaveProvider.cs new file mode 100644 index 0000000000..599f8b5aa3 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/NoneAttestationEnclaveProvider.cs @@ -0,0 +1,106 @@ +// 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.Diagnostics; +using System.Security.Cryptography; +using System.Threading; + +namespace Microsoft.Data.SqlClient +{ + internal class NoneAttestationEnclaveProvider : EnclaveProviderBase + { + private static readonly int EnclaveSessionHandleSize = 8; + private const int DiffieHellmanKeySize = 384; + private const int NoneAttestationProtocolId = (int)SqlConnectionAttestationProtocol.None; + + + // When overridden in a derived class, looks up an existing enclave session information in the enclave session cache. + // If the enclave provider doesn't implement enclave session caching, this method is expected to return null in the sqlEnclaveSession parameter. + internal override void GetEnclaveSession(EnclaveSessionParameters enclaveSessionParameters, bool generateCustomData, out SqlEnclaveSession sqlEnclaveSession, out long counter, out byte[] customData, out int customDataLength) + { + GetEnclaveSessionHelper(enclaveSessionParameters, false, out sqlEnclaveSession, out counter, out customData, out customDataLength); + } + + // Gets the information that SqlClient subsequently uses to initiate the process of attesting the enclave and to establish a secure session with the enclave. + internal override SqlEnclaveAttestationParameters GetAttestationParameters(string attestationUrl, byte[] customData, int customDataLength) + { + ECDiffieHellman clientDHKey = KeyConverter.CreateECDiffieHellman(DiffieHellmanKeySize); + return new SqlEnclaveAttestationParameters(NoneAttestationProtocolId, Array.Empty(), clientDHKey); + } + + // When overridden in a derived class, performs enclave attestation, generates a symmetric key for the session, creates an enclave session and stores the session information in the cache. + internal override void CreateEnclaveSession(byte[] attestationInfo, ECDiffieHellman clientDHKey, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength, out SqlEnclaveSession sqlEnclaveSession, out long counter) + { + // for None attestation: enclave does not send public key, and sends an empty attestation info + // The only non-trivial content it sends is the session setup info (DH pubkey of enclave) + + sqlEnclaveSession = null; + counter = 0; + try + { + ThreadRetryCache.Remove(Thread.CurrentThread.ManagedThreadId.ToString()); + sqlEnclaveSession = GetEnclaveSessionFromCache(enclaveSessionParameters, out counter); + + if (sqlEnclaveSession == null) + { + // Read AttestationInfo + int attestationInfoOffset = 0; + uint sizeOfTrustedModuleAttestationInfoBuffer = BitConverter.ToUInt32(attestationInfo, attestationInfoOffset); + attestationInfoOffset += sizeof(UInt32); + int sizeOfTrustedModuleAttestationInfoBufferInt = checked((int)sizeOfTrustedModuleAttestationInfoBuffer); + Debug.Assert(sizeOfTrustedModuleAttestationInfoBuffer == 0); + + // read secure session info + uint sizeOfSecureSessionInfoResponse = BitConverter.ToUInt32(attestationInfo, attestationInfoOffset); + attestationInfoOffset += sizeof(UInt32); + + byte[] enclaveSessionHandle = new byte[EnclaveSessionHandleSize]; + Buffer.BlockCopy(attestationInfo, attestationInfoOffset, enclaveSessionHandle, 0, EnclaveSessionHandleSize); + attestationInfoOffset += EnclaveSessionHandleSize; + + uint sizeOfTrustedModuleDHPublicKeyBuffer = BitConverter.ToUInt32(attestationInfo, attestationInfoOffset); + attestationInfoOffset += sizeof(UInt32); + uint sizeOfTrustedModuleDHPublicKeySignatureBuffer = BitConverter.ToUInt32(attestationInfo, attestationInfoOffset); + attestationInfoOffset += sizeof(UInt32); + int sizeOfTrustedModuleDHPublicKeyBufferInt = checked((int)sizeOfTrustedModuleDHPublicKeyBuffer); + + byte[] trustedModuleDHPublicKey = new byte[sizeOfTrustedModuleDHPublicKeyBuffer]; + Buffer.BlockCopy(attestationInfo, attestationInfoOffset, trustedModuleDHPublicKey, 0, + sizeOfTrustedModuleDHPublicKeyBufferInt); + attestationInfoOffset += sizeOfTrustedModuleDHPublicKeyBufferInt; + + byte[] trustedModuleDHPublicKeySignature = new byte[sizeOfTrustedModuleDHPublicKeySignatureBuffer]; + Buffer.BlockCopy(attestationInfo, attestationInfoOffset, trustedModuleDHPublicKeySignature, 0, + checked((int)sizeOfTrustedModuleDHPublicKeySignatureBuffer)); + + byte[] sharedSecret; + using ECDiffieHellman ecdh = KeyConverter.CreateECDiffieHellmanFromPublicKeyBlob(trustedModuleDHPublicKey); + sharedSecret = KeyConverter.DeriveKey(clientDHKey, ecdh.PublicKey); + long sessionId = BitConverter.ToInt64(enclaveSessionHandle, 0); + sqlEnclaveSession = AddEnclaveSessionToCache(enclaveSessionParameters, sharedSecret, sessionId, out counter); + + if (sqlEnclaveSession is null) + { + throw new AlwaysEncryptedAttestationException(Strings.FailToCreateEnclaveSession); + } + } + } + finally + { + UpdateEnclaveSessionLockStatus(sqlEnclaveSession); + } + } + + /// + /// When overridden in a derived class, looks up and evicts an enclave session from the enclave session cache, if the provider implements session caching. + /// + /// The set of parameters required for enclave session. + /// The session to be invalidated. + internal override void InvalidateEnclaveSession(EnclaveSessionParameters enclaveSessionParameters, SqlEnclaveSession enclaveSessionToInvalidate) + { + InvalidateEnclaveSessionHelper(enclaveSessionParameters, enclaveSessionToInvalidate); + } + } +} diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs index f71047d965..6c940e3749 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs @@ -22,7 +22,7 @@ internal abstract class VirtualizationBasedSecurityEnclaveProviderBase : Enclave #region Constants private const int DiffieHellmanKeySize = 384; - private const int VsmHGSProtocolId = 3; + private const int VsmHGSProtocolId = (int)SqlConnectionAttestationProtocol.HGS; // ENCLAVE_IDENTITY related constants private static readonly EnclaveIdentity ExpectedPolicy = new EnclaveIdentity() @@ -95,7 +95,7 @@ internal override void GetEnclaveSession(EnclaveSessionParameters enclaveSession internal override SqlEnclaveAttestationParameters GetAttestationParameters(string attestationUrl, byte[] customData, int customDataLength) { ECDiffieHellman clientDHKey = KeyConverter.CreateECDiffieHellman(DiffieHellmanKeySize); - return new SqlEnclaveAttestationParameters(VsmHGSProtocolId, new byte[] { }, clientDHKey); + return new SqlEnclaveAttestationParameters(VsmHGSProtocolId, Array.Empty(), clientDHKey); } // When overridden in a derived class, performs enclave attestation, generates a symmetric key for the session, creates a an enclave session and stores the session information in the cache. diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs index 5c9c124763..aa042ffb65 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs @@ -88,6 +88,17 @@ public void TestSqlConnectionStringAttestationProtocol() Assert.Equal(SqlConnectionAttestationProtocol.NotSpecified, connectionStringBuilder3.AttestationProtocol); Assert.True(string.IsNullOrEmpty(connectionStringBuilder3.DataSource)); + + SqlConnectionStringBuilder connectionStringBuilder4 = new SqlConnectionStringBuilder(); + connectionStringBuilder4.AttestationProtocol = SqlConnectionAttestationProtocol.None; + Assert.Equal(SqlConnectionAttestationProtocol.None, connectionStringBuilder4.AttestationProtocol); + connectionStringBuilder4.DataSource = @"localhost"; + + VerifyAttestationProtocol(connectionStringBuilder4, SqlConnectionAttestationProtocol.None); + + connectionStringBuilder4.Clear(); + Assert.Equal(SqlConnectionAttestationProtocol.NotSpecified, connectionStringBuilder4.AttestationProtocol); + Assert.Empty(connectionStringBuilder4.DataSource); } [Fact] @@ -117,20 +128,32 @@ public void TestSqlConnectionStringBuilderEquivilantTo_AttestationProtocol() { SqlConnectionAttestationProtocol protocol1 = SqlConnectionAttestationProtocol.AAS; SqlConnectionAttestationProtocol protocol2 = SqlConnectionAttestationProtocol.HGS; + SqlConnectionAttestationProtocol protocol3 = SqlConnectionAttestationProtocol.None; SqlConnectionStringBuilder connectionStringBuilder1 = new SqlConnectionStringBuilder(); SqlConnectionStringBuilder connectionStringBuilder2 = new SqlConnectionStringBuilder(); + SqlConnectionStringBuilder connectionStringBuilder3 = new SqlConnectionStringBuilder(); // Modify the default value and set the same value on the both the builder objects above. connectionStringBuilder1.AttestationProtocol = protocol1; connectionStringBuilder2.AttestationProtocol = protocol1; + connectionStringBuilder3.AttestationProtocol = protocol1; // Use the EquivalentTo function to compare both the builder objects and make sure the result is expected. Assert.True(connectionStringBuilder1.EquivalentTo(connectionStringBuilder2)); + Assert.True(connectionStringBuilder1.EquivalentTo(connectionStringBuilder3)); + Assert.Equal(connectionStringBuilder1.AttestationProtocol, connectionStringBuilder2.AttestationProtocol); + Assert.Equal(connectionStringBuilder1.AttestationProtocol, connectionStringBuilder3.AttestationProtocol); connectionStringBuilder2.AttestationProtocol = protocol2; - Assert.True(!connectionStringBuilder1.EquivalentTo(connectionStringBuilder2)); + Assert.True(!connectionStringBuilder3.EquivalentTo(connectionStringBuilder2)); + Assert.Equal(protocol2, connectionStringBuilder2.AttestationProtocol); + + connectionStringBuilder3.AttestationProtocol = protocol3; + Assert.True(!connectionStringBuilder1.EquivalentTo(connectionStringBuilder3)); + Assert.True(!connectionStringBuilder2.EquivalentTo(connectionStringBuilder3)); + Assert.Equal(protocol3, connectionStringBuilder3.AttestationProtocol); } @@ -151,14 +174,15 @@ public void TestSqlConnectionStringBuilderColumnEncryptionSetting(SqlConnectionC [InlineData(SqlConnectionAttestationProtocol.AAS)] [InlineData(SqlConnectionAttestationProtocol.HGS)] [InlineData(SqlConnectionAttestationProtocol.NotSpecified)] + [InlineData(SqlConnectionAttestationProtocol.None)] public void TestSqlConnectionStringBuilderAttestationProtocol(SqlConnectionAttestationProtocol protocol) { SqlConnectionStringBuilder connectionStringBuilder = new SqlConnectionStringBuilder(); connectionStringBuilder.DataSource = @"localhost"; // Modify value. - connectionStringBuilder.AttestationProtocol = protocol; - + connectionStringBuilder.AttestationProtocol = protocol; + //Create a connection object with the above builder and verify the expected value. VerifyAttestationProtocol(connectionStringBuilder, protocol); } @@ -313,6 +337,12 @@ public void TestSqlConnectionStringBuilderTryGetValue(SqlConnectionColumnEncrypt tryGetValueResult = connectionStringBuilder.TryGetValue(@"Attestation Protocol", out outputValue); Assert.True(tryGetValueResult); Assert.Equal(SqlConnectionAttestationProtocol.AAS, outputValue); + + connectionStringBuilder.AttestationProtocol = SqlConnectionAttestationProtocol.None; + + Assert.True(connectionStringBuilder.TryGetValue(@"Attestation Protocol", out outputValue), + "'Attestation Protocol'' key not found in SqlConnectionStringBuilder"); + Assert.Equal(SqlConnectionAttestationProtocol.None, outputValue); } [Theory] diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/SqlConnectionShould.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/SqlConnectionShould.cs index faa48c7763..1669b228f7 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/SqlConnectionShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/SqlConnectionShould.cs @@ -151,5 +151,16 @@ SqlCommandColumnEncryptionSetting sqlCommandColumnEncryptionSetting_2 } } } + + [Fact] + public void TestInvalidAttestationProtocolInConnectionString() + { + string connectionString = "Data Source=localhost; Initial Catalog = testdb; Column Encryption Setting=Enabled;"; + ArgumentException ex = Assert.Throws(() => new SqlConnection(connectionString + "Attestation protocol=invalid")); + Assert.Null(ex.InnerException); + Assert.NotNull(ex.Message); + Assert.Contains("Invalid value for key 'Attestation Protocol'", ex.Message); + Assert.Null(ex.ParamName); + } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs index 2bec916210..5f60faddb5 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs @@ -2122,6 +2122,24 @@ public void TestSqlCommandCancellationToken(string connection, int initalValue, } } + + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsSGXEnclaveConnStringSetup))] + public void TestNoneAttestationProtocolWithSGXEnclave() + { + SqlConnectionStringBuilder builder = new(DataTestUtility.TCPConnectionStringAASSGX); + builder.AttestationProtocol = SqlConnectionAttestationProtocol.None; + builder.EnclaveAttestationUrl = string.Empty; + + using (SqlConnection connection = new(builder.ConnectionString)) + { + InvalidOperationException ex = Assert.Throws(() => connection.Open()); + string expectedErrorMessage = string.Format( + SystemDataResourceManager.Instance.TCE_AttestationProtocolNotSupportEnclaveType, + SqlConnectionAttestationProtocol.None.ToString(), "SGX"); + Assert.Contains(expectedErrorMessage, ex.Message); + } + } + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] [ClassData(typeof(AEConnectionStringProvider))] public void TestConnectionCustomKeyStoreProviderDuringAeQuery(string connectionString) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategy.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategy.cs index 3a995dd9f8..0cd8436cb4 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategy.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategy.cs @@ -137,6 +137,7 @@ protected List CreateTables(IList columnEncryptionKe SqlNullValuesTable = new SqlNullValuesTable(GenerateUniqueName("SqlNullValuesTable"), columnEncryptionKeys[0]); tables.Add(SqlNullValuesTable); + // columnEncryptionKeys[2] is encrypted with DummyCMK. use this encrypted column to test custom key store providers CustomKeyStoreProviderTestTable = new ApiTestTable(GenerateUniqueName("CustomKeyStoreProviderTestTable"), columnEncryptionKeys[2], columnEncryptionKeys[0], useDeterministicEncryption: true); tables.Add(CustomKeyStoreProviderTestTable); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs index 27acea362d..6c31280c0a 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs @@ -27,6 +27,7 @@ public static class DataTestUtility public static readonly string TCPConnectionString = null; public static readonly string TCPConnectionStringHGSVBS = null; public static readonly string TCPConnectionStringAASVBS = null; + public static readonly string TCPConnectionStringNoneVBS = null; public static readonly string TCPConnectionStringAASSGX = null; public static readonly string AADAuthorityURL = null; public static readonly string AADPasswordConnectionString = null; @@ -86,6 +87,7 @@ static DataTestUtility() TCPConnectionString = c.TCPConnectionString; TCPConnectionStringHGSVBS = c.TCPConnectionStringHGSVBS; TCPConnectionStringAASVBS = c.TCPConnectionStringAASVBS; + TCPConnectionStringNoneVBS = c.TCPConnectionStringNoneVBS; TCPConnectionStringAASSGX = c.TCPConnectionStringAASSGX; AADAuthorityURL = c.AADAuthorityURL; AADPasswordConnectionString = c.AADPasswordConnectionString; @@ -148,6 +150,12 @@ static DataTestUtility() AEConnStrings.Add(TCPConnectionStringAASVBS); } + if (!string.IsNullOrEmpty(TCPConnectionStringNoneVBS)) + { + AEConnStrings.Add(TCPConnectionStringNoneVBS); + AEConnStringsSetup.Add(TCPConnectionStringNoneVBS); + } + if (!string.IsNullOrEmpty(TCPConnectionStringAASSGX)) { AEConnStrings.Add(TCPConnectionStringAASSGX); @@ -296,6 +304,8 @@ public static bool AreConnStringSetupForAE() return AEConnStrings.Count > 0 && IsNotAzureSynapse(); } + public static bool IsSGXEnclaveConnStringSetup() => !string.IsNullOrEmpty(TCPConnectionStringAASSGX); + public static bool IsAADPasswordConnStrSetup() { return !string.IsNullOrEmpty(AADPasswordConnectionString); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ExceptionTest/ExceptionTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ExceptionTest/ExceptionTest.cs index a52dee1ad4..c9dfe33bda 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ExceptionTest/ExceptionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ExceptionTest/ExceptionTest.cs @@ -5,6 +5,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Data; using System.Globalization; using System.Threading.Tasks; using Xunit; @@ -251,6 +252,7 @@ public static void EnclavesConnectionExceptionTest() string connectionStringWithAttestationProtocol = DataTestUtility.TCPConnectionString + ";Attestation Protocol = HGS;"; string connectionStringWithAttestationURL = DataTestUtility.TCPConnectionString + ";Enclave Attestation URL = https://dummyURL;"; string connectionStringWithEnclave = connectionStringWithAttestationURL + ";Attestation Protocol = HGS;"; + string connectionStringWithNoneAttestationProtocol = DataTestUtility.TCPConnectionString + ";Attestation Protocol = None;"; InvalidOperationException e1 = Assert.Throws(() => new SqlConnection(connectionStringWithAttestationURL).Open()); Assert.Contains("You have specified the enclave attestation URL in the connection string", e1.Message); @@ -260,6 +262,14 @@ public static void EnclavesConnectionExceptionTest() InvalidOperationException e3 = Assert.Throws(() => new SqlConnection(connectionStringWithEnclave).Open()); Assert.Contains("You have specified the enclave attestation URL and attestation protocol in the connection string", e3.Message); + + if (DataTestUtility.EnclaveEnabled) + { + // connection should work if attestation protocol is None but no attestation url is provided + SqlConnection sqlConnection = new(connectionStringWithNoneAttestationProtocol); + sqlConnection.Open(); + Assert.Equal(ConnectionState.Open, sqlConnection.State); + } } [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Config.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Config.cs index dec84eabd1..101b0c0606 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Config.cs +++ b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Config.cs @@ -14,6 +14,7 @@ public class Config public string NPConnectionString = null; public string TCPConnectionStringHGSVBS = null; public string TCPConnectionStringAASVBS = null; + public string TCPConnectionStringNoneVBS = null; public string TCPConnectionStringAASSGX = null; public string AADAuthorityURL = null; public string AADPasswordConnectionString = null; diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.json b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.json index b49cf6a9ec..5b3628430b 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.json +++ b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.json @@ -3,6 +3,7 @@ "NPConnectionString": "Data Source=np:localhost;Database=Northwind;Integrated Security=true;", "TCPConnectionStringHGSVBS": "", "TCPConnectionStringAASVBS": "", + "TCPConnectionStringNoneVBS": "", "TCPConnectionStringAASSGX": "", "EnclaveEnabled": false, "TracingEnabled": false,