From fa3575d54cd96b37ca17ed34270bcad182121556 Mon Sep 17 00:00:00 2001 From: Karina Zhou Date: Mon, 7 Oct 2019 15:21:04 -0700 Subject: [PATCH 01/63] Add Attestation Protocol in connection string --- .../netfx/ref/Microsoft.Data.SqlClient.cs | 6 + .../Data/Common/DbConnectionStringCommon.cs | 133 ++++++++++++++++++ .../Microsoft/Data/SqlClient/SqlConnection.cs | 14 ++ .../Data/SqlClient/SqlConnectionString.cs | 33 ++++- .../SqlClient/SqlConnectionStringBuilder.cs | 51 ++++++- .../src/Microsoft/Data/SqlClient/SqlUtil.cs | 10 ++ .../src/Microsoft/Data/SqlClient/TdsEnums.cs | 22 ++- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 36 +++++ .../netfx/src/Resources/Strings.Designer.cs | 30 +++- .../netfx/src/Resources/Strings.resx | 9 ++ 10 files changed, 340 insertions(+), 4 deletions(-) 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 89302df067..d9b3f3c1f8 100644 --- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs @@ -467,6 +467,12 @@ public enum SqlConnectionColumnEncryptionSetting Disabled = 0, Enabled = 1, } + public enum SqlConnectionAttestationProtocol + { + None = 0, + AAS = 1, + HGS = 2 + } [System.ComponentModel.DefaultPropertyAttribute("DataSource")] public sealed partial class SqlConnectionStringBuilder : System.Data.Common.DbConnectionStringBuilder { diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionStringCommon.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionStringCommon.cs index 0c0ce39dcc..c3563b720a 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionStringCommon.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionStringCommon.cs @@ -815,6 +815,137 @@ internal static SqlConnectionColumnEncryptionSetting ConvertToColumnEncryptionSe } } + #region <> + + /// + /// Attestation Protocol. + /// + const string AttestationProtocolHGS = "HGS"; + const string AttestationProtocolAAS = "AAS"; + + /// + /// Convert a string value to the corresponding SqlConnectionAttestationProtocol + /// + /// + /// + /// + internal static bool TryConvertToAttestationProtocol(string value, out SqlConnectionAttestationProtocol result) + { + bool isSuccess = false; + + if (StringComparer.InvariantCultureIgnoreCase.Equals(value, AttestationProtocolHGS)) + { + result = SqlConnectionAttestationProtocol.HGS; + isSuccess = true; + } + else if (StringComparer.InvariantCultureIgnoreCase.Equals(value, AttestationProtocolAAS)) + { + result = SqlConnectionAttestationProtocol.AAS; + isSuccess = true; + } + else + { + result = DbConnectionStringDefaults.AttestationProtocol; + } + + return isSuccess; + } + + internal static bool IsValidAttestationProtocol(SqlConnectionAttestationProtocol value) + { + Debug.Assert(Enum.GetNames(typeof(SqlConnectionAttestationProtocol)).Length == 3, "SqlConnectionAttestationProtocol enum has changed, update needed"); + return value == SqlConnectionAttestationProtocol.None + || value == SqlConnectionAttestationProtocol.HGS + || value == SqlConnectionAttestationProtocol.AAS; + + } + + internal static string AttestationProtocolToString(SqlConnectionAttestationProtocol value) + { + Debug.Assert(IsValidAttestationProtocol(value), "value is not a valid attestation protocol"); + + switch (value) + { + case SqlConnectionAttestationProtocol.HGS: + return AttestationProtocolHGS; + case SqlConnectionAttestationProtocol.AAS: + return AttestationProtocolAAS; + default: + return null; + } + } + + internal static SqlConnectionAttestationProtocol ConvertToAttestationProtocol(string keyword, object value) + { + if (null == value) + { + return DbConnectionStringDefaults.AttestationProtocol; + } + + string sValue = (value as string); + SqlConnectionAttestationProtocol result; + + if (null != sValue) + { + if (TryConvertToAttestationProtocol(sValue, out result)) + { + return result; + } + + // try again after remove leading & trailing whitespaces. + sValue = sValue.Trim(); + if (TryConvertToAttestationProtocol(sValue, out result)) + { + return result; + } + + // string values must be valid + throw ADP.InvalidConnectionOptionValue(keyword); + } + else + { + // the value is not string, try other options + SqlConnectionAttestationProtocol eValue; + + if (value is SqlConnectionAttestationProtocol) + { + eValue = (SqlConnectionAttestationProtocol)value; + } + else if (value.GetType().IsEnum) + { + // explicitly block scenarios in which user tries to use wrong enum types, like: + // builder["SqlConnectionAttestationProtocol"] = EnvironmentVariableTarget.Process; + // workaround: explicitly cast non-SqlConnectionAttestationProtocol enums to int + throw ADP.ConvertFailed(value.GetType(), typeof(SqlConnectionAttestationProtocol), null); + } + else + { + try + { + // Enum.ToObject allows only integral and enum values (enums are blocked above), rasing ArgumentException for the rest + eValue = (SqlConnectionAttestationProtocol)Enum.ToObject(typeof(SqlConnectionAttestationProtocol), value); + } + catch (ArgumentException e) + { + // to be consistent with the messages we send in case of wrong type usage, replace + // the error with our exception, and keep the original one as inner one for troubleshooting + throw ADP.ConvertFailed(value.GetType(), typeof(SqlConnectionAttestationProtocol), e); + } + } + + if (IsValidAttestationProtocol(eValue)) + { + return eValue; + } + else + { + throw ADP.InvalidEnumerationValue(typeof(SqlConnectionAttestationProtocol), (int)eValue); + } + } + } + + #endregion + internal static bool IsValidCertificateValue(string value) { return string.IsNullOrEmpty(value) @@ -880,6 +1011,7 @@ internal static class DbConnectionStringDefaults internal static readonly SqlAuthenticationMethod Authentication = SqlAuthenticationMethod.NotSpecified; internal static readonly SqlConnectionColumnEncryptionSetting ColumnEncryptionSetting = SqlConnectionColumnEncryptionSetting.Disabled; internal const string EnclaveAttestationUrl = ""; + internal static readonly SqlConnectionAttestationProtocol AttestationProtocol = SqlConnectionAttestationProtocol.None; internal const string Certificate = ""; internal const PoolBlockingPeriod PoolBlockingPeriod = SqlClient.PoolBlockingPeriod.Auto; } @@ -952,6 +1084,7 @@ internal static class DbConnectionStringKeywords internal const string Certificate = "Certificate"; internal const string ColumnEncryptionSetting = "Column Encryption Setting"; internal const string EnclaveAttestationUrl = "Enclave Attestation Url"; + internal const string AttestationProtocol = "Attestation Protocol"; internal const string PoolBlockingPeriod = "PoolBlockingPeriod"; // common keywords (OleDb, OracleClient, SqlClient) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs index 7e91a4bd4d..537622697c 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -505,6 +505,20 @@ internal string EnclaveAttestationUrl } } + // kz + /// + /// Get attestation protocol + /// + /// + internal SqlConnectionAttestationProtocol AttestationProtocol + { + get + { + SqlConnectionString opt = (SqlConnectionString)ConnectionOptions; + return opt.AttestationProtocol; + } + } + // Is this connection is a Context Connection? private bool UsesContextConnection(SqlConnectionString opt) { diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionString.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionString.cs index 672d9fcafd..7779c61cf9 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionString.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionString.cs @@ -55,6 +55,7 @@ internal static class DEFAULT internal static readonly SqlAuthenticationMethod Authentication = SqlAuthenticationMethod.NotSpecified; internal static readonly SqlConnectionColumnEncryptionSetting ColumnEncryptionSetting = SqlConnectionColumnEncryptionSetting.Disabled; internal const string EnclaveAttestationUrl = ""; + internal static readonly SqlConnectionAttestationProtocol AttestationProtocol = SqlConnectionAttestationProtocol.None; #if ADONET_CERT_AUTH internal const string Certificate = ""; #endif @@ -71,6 +72,7 @@ internal static class KEY internal const string PoolBlockingPeriod = "poolblockingperiod"; internal const string ColumnEncryptionSetting = "column encryption setting"; internal const string EnclaveAttestationUrl = "enclave attestation url"; + internal const string AttestationProtocol = "attestation protocol"; internal const string Connect_Timeout = "connect timeout"; internal const string Connection_Reset = "connection reset"; internal const string Context_Connection = "context connection"; @@ -215,6 +217,7 @@ internal static class TRANSACIONBINDING private readonly SqlAuthenticationMethod _authType; private readonly SqlConnectionColumnEncryptionSetting _columnEncryptionSetting; private readonly string _enclaveAttestationUrl; + private readonly SqlConnectionAttestationProtocol _attestationProtocol; private readonly int _connectTimeout; private readonly int _loadBalanceTimeout; @@ -298,6 +301,7 @@ internal SqlConnectionString(string connectionString) : base(connectionString, G _authType = ConvertValueToAuthenticationType(); _columnEncryptionSetting = ConvertValueToColumnEncryptionSetting(); _enclaveAttestationUrl = ConvertValueToString(KEY.EnclaveAttestationUrl, DEFAULT.EnclaveAttestationUrl); + _attestationProtocol = ConvertValueToAttestationProtocol(); #if ADONET_CERT_AUTH _certificate = ConvertValueToString(KEY.Certificate, DEFAULT.Certificate); @@ -605,6 +609,7 @@ internal SqlConnectionString(SqlConnectionString connectionOptions, string dataS _authType = connectionOptions._authType; _columnEncryptionSetting = connectionOptions._columnEncryptionSetting; _enclaveAttestationUrl = connectionOptions._enclaveAttestationUrl; + _attestationProtocol = connectionOptions._attestationProtocol; #if ADONET_CERT_AUTH _certificate = connectionOptions._certificate; #endif @@ -631,8 +636,8 @@ internal SqlConnectionString(SqlConnectionString connectionOptions, string dataS internal bool TransparentNetworkIPResolution { get { return _transparentNetworkIPResolution; } } internal SqlAuthenticationMethod Authentication { get { return _authType; } } internal SqlConnectionColumnEncryptionSetting ColumnEncryptionSetting { get { return _columnEncryptionSetting; } } - internal string EnclaveAttestationUrl { get { return _enclaveAttestationUrl; } } + internal SqlConnectionAttestationProtocol AttestationProtocol { get { return _attestationProtocol; } } #if ADONET_CERT_AUTH internal string Certificate { get { return _certificate; } } internal bool UsesCertificate { get { return _authType == SqlClient.SqlAuthenticationMethod.SqlCertificate; } } @@ -764,6 +769,7 @@ internal static Hashtable GetParseSynonyms() hash.Add(KEY.Type_System_Version, KEY.Type_System_Version); hash.Add(KEY.ColumnEncryptionSetting, KEY.ColumnEncryptionSetting); hash.Add(KEY.EnclaveAttestationUrl, KEY.EnclaveAttestationUrl); + hash.Add(KEY.AttestationProtocol, KEY.AttestationProtocol); hash.Add(KEY.User_ID, KEY.User_ID); hash.Add(KEY.User_Instance, KEY.User_Instance); hash.Add(KEY.Workstation_Id, KEY.Workstation_Id); @@ -993,6 +999,31 @@ internal SqlConnectionColumnEncryptionSetting ConvertValueToColumnEncryptionSett } } + // kz + internal SqlConnectionAttestationProtocol ConvertValueToAttestationProtocol() + { + object value = base.Parsetable[KEY.AttestationProtocol]; + + string valStr = value as string; + if (valStr == null) + { + return DEFAULT.AttestationProtocol; + } + + try + { + return DbConnectionStringBuilderUtil.ConvertToAttestationProtocol(KEY.AttestationProtocol, valStr); + } + catch (FormatException e) + { + throw ADP.InvalidConnectionOptionValue(KEY.AttestationProtocol, e); + } + catch (OverflowException e) + { + throw ADP.InvalidConnectionOptionValue(KEY.AttestationProtocol, e); + } + } + internal bool ConvertValueToEncrypt() { // If the Authentication keyword is provided, default to Encrypt=true; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs index d38f9b851a..1ee5cd1e6c 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs @@ -77,8 +77,9 @@ private enum Keywords ConnectRetryInterval, ColumnEncryptionSetting, - EnclaveAttestationUrl, + EnclaveAttestationUrl, + AttestationProtocol, #if ADONET_CERT_AUTH Certificate, @@ -132,6 +133,7 @@ private enum Keywords private SqlAuthenticationMethod _authentication = DbConnectionStringDefaults.Authentication; private SqlConnectionColumnEncryptionSetting _columnEncryptionSetting = DbConnectionStringDefaults.ColumnEncryptionSetting; private string _enclaveAttestationUrl = DbConnectionStringDefaults.EnclaveAttestationUrl; + private SqlConnectionAttestationProtocol _attestationProtocol = DbConnectionStringDefaults.AttestationProtocol; private PoolBlockingPeriod _poolBlockingPeriod = DbConnectionStringDefaults.PoolBlockingPeriod; #if ADONET_CERT_AUTH @@ -180,6 +182,7 @@ static SqlConnectionStringBuilder() validKeywords[(int)Keywords.Authentication] = DbConnectionStringKeywords.Authentication; validKeywords[(int)Keywords.ColumnEncryptionSetting] = DbConnectionStringKeywords.ColumnEncryptionSetting; validKeywords[(int)Keywords.EnclaveAttestationUrl] = DbConnectionStringKeywords.EnclaveAttestationUrl; + validKeywords[(int)Keywords.AttestationProtocol] = DbConnectionStringKeywords.AttestationProtocol; #if ADONET_CERT_AUTH validKeywords[(int)Keywords.Certificate] = DbConnectionStringKeywords.Certificate; #endif @@ -225,6 +228,7 @@ static SqlConnectionStringBuilder() hash.Add(DbConnectionStringKeywords.Authentication, Keywords.Authentication); hash.Add(DbConnectionStringKeywords.ColumnEncryptionSetting, Keywords.ColumnEncryptionSetting); hash.Add(DbConnectionStringKeywords.EnclaveAttestationUrl, Keywords.EnclaveAttestationUrl); + hash.Add(DbConnectionStringKeywords.AttestationProtocol, Keywords.AttestationProtocol); #if ADONET_CERT_AUTH hash.Add(DbConnectionStringKeywords.Certificate, Keywords.Certificate); #endif @@ -350,6 +354,9 @@ public override object this[string keyword] case Keywords.EnclaveAttestationUrl: EnclaveAttestationUrl = ConvertToString(value); break; + case Keywords.AttestationProtocol: + AttestationProtocol = ConvertToAttestationProtocol(keyword, value); + break; #if ADONET_CERT_AUTH case Keywords.Certificate: Certificate = ConvertToString(value); break; #endif @@ -622,6 +629,26 @@ public string EnclaveAttestationUrl } } + // kz + [DisplayName(DbConnectionStringKeywords.AttestationProtocol)] + [ResCategoryAttribute(StringsHelper.ResourceNames.DataCategory_Security)] + [ResDescriptionAttribute(StringsHelper.ResourceNames.TCE_DbConnectionString_AttestationProtocol)] + [RefreshPropertiesAttribute(RefreshProperties.All)] + public SqlConnectionAttestationProtocol AttestationProtocol + { + get { return _attestationProtocol; } + set + { + if (!DbConnectionStringBuilderUtil.IsValidAttestationProtocol(value)) + { + throw ADP.InvalidEnumerationValue(typeof(SqlConnectionAttestationProtocol), (int)value); + } + + SetAttestationProtocolValue(value); + _attestationProtocol = value; + } + } + [DisplayName(DbConnectionStringKeywords.TrustServerCertificate)] [ResCategoryAttribute(StringsHelper.ResourceNames.DataCategory_Security)] [ResDescriptionAttribute(StringsHelper.ResourceNames.DbConnectionString_TrustServerCertificate)] @@ -1162,6 +1189,17 @@ private static SqlConnectionColumnEncryptionSetting ConvertToColumnEncryptionSet return DbConnectionStringBuilderUtil.ConvertToColumnEncryptionSetting(keyword, value); } + /// + /// Convert to SqlConnectionAttestationProtocol + /// + /// + /// + /// + private static SqlConnectionAttestationProtocol ConvertToAttestationProtocol(string keyword, object value) + { + return DbConnectionStringBuilderUtil.ConvertToAttestationProtocol(keyword, value); + } + private object GetAt(Keywords index) { switch (index) @@ -1245,6 +1283,8 @@ private object GetAt(Keywords index) return ColumnEncryptionSetting; case Keywords.EnclaveAttestationUrl: return EnclaveAttestationUrl; + case Keywords.AttestationProtocol: + return AttestationProtocol; #if ADONET_CERT_AUTH case Keywords.Certificate: return Certificate; #endif @@ -1433,6 +1473,9 @@ private void Reset(Keywords index) case Keywords.EnclaveAttestationUrl: _enclaveAttestationUrl = DbConnectionStringDefaults.EnclaveAttestationUrl; break; + case Keywords.AttestationProtocol: + _attestationProtocol = DbConnectionStringDefaults.AttestationProtocol; + break; default: Debug.Fail("unexpected keyword"); throw ADP.KeywordNotSupported(_validKeywords[(int)index]); @@ -1473,6 +1516,12 @@ private void SetColumnEncryptionSettingValue(SqlConnectionColumnEncryptionSettin base[DbConnectionStringKeywords.ColumnEncryptionSetting] = DbConnectionStringBuilderUtil.ColumnEncryptionSettingToString(value); } + private void SetAttestationProtocolValue(SqlConnectionAttestationProtocol value) + { + Debug.Assert(DbConnectionStringBuilderUtil.IsValidAttestationProtocol(value), "Invalid value for SqlConnectionAttestationProtocol"); + base[DbConnectionStringKeywords.AttestationProtocol] = DbConnectionStringBuilderUtil.AttestationProtocolToString(value); + } + public override bool ShouldSerialize(string keyword) { ADP.CheckArgumentNull(keyword, "keyword"); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs index a43050396e..1a6eede208 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs @@ -1730,6 +1730,16 @@ static internal Exception EnclaveTypeNotReturned() return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_EnclaveTypeNotReturned)); } + static internal Exception EnclaveTypeNotSupported(string enclaveType) + { + return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_SequentialAccessNotSupportedOnEncryptedColumn, enclaveType)); + } + + static internal Exception AttestationProtocolNotSupportEnclaveType(string enclaveType) + { + return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_AttestationProtocolNotSupportEnclaveType, enclaveType)); + } + // // TCE- Extensibility related error messages // 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 7914784d72..cb67a430d2 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 @@ -945,6 +945,8 @@ internal enum FedAuthInfoId : byte internal const int AES_256_CBC = 1; internal const int AEAD_AES_256_CBC_HMAC_SHA256 = 2; + internal const string ENCLAVE_TYPE_VBS = "VBS"; + internal const string ENCLAVE_TYPE_SGX = "SGX"; // TCE Param names for exec handling internal const string TCE_PARAM_CIPHERTEXT = "cipherText"; @@ -1031,7 +1033,7 @@ public enum SqlConnectionColumnEncryptionSetting public enum SqlCommandColumnEncryptionSetting { /// - /// if “Column Encryption Setting=Enabled” in the connection string, use Enabled. Otherwise, maps to Disabled. + /// if �Column Encryption Setting=Enabled� in the connection string, use Enabled. Otherwise, maps to Disabled. /// UseConnectionSetting = 0, @@ -1052,6 +1054,24 @@ public enum SqlCommandColumnEncryptionSetting Disabled, } + public enum SqlConnectionAttestationProtocol + { + /// + /// If the attestation protocol is not specified. Use this as default value. + /// + None = 0, + + /// + /// Attestation portocol for Azure Attestation Service + /// + AAS = 1, + + /// + /// Attestation protocol for Host Guardian Service + /// + HGS = 2 + } + public enum SqlAuthenticationMethod { NotSpecified = 0, 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 4c65f61235..eb69f1f556 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 @@ -3470,6 +3470,42 @@ private bool TryProcessFeatureExtAck(TdsParserStateObject stateObj) throw SQL.EnclaveTypeNotReturned(); } + // Check if enclave attestation url was specified and the attestation protocol supports the enclave type. + if (this.Connection.RoutingInfo == null + && (!string.IsNullOrWhiteSpace(_connHandler.ConnectionOptions.EnclaveAttestationUrl)) + && (!string.IsNullOrWhiteSpace(EnclaveType)) + && (!IsValidAttestationProtocol(EnclaveType))) + { + throw SQL.AttestationProtocolNotSupportEnclaveType(EnclaveType); + } + + return true; + } + + private bool IsValidAttestationProtocol(string enclaveType) + { + switch (enclaveType.ToUpper()) + { + case TdsEnums.ENCLAVE_TYPE_VBS: + if (_connHandler.ConnectionOptions.AttestationProtocol != SqlConnectionAttestationProtocol.AAS + && _connHandler.ConnectionOptions.AttestationProtocol != SqlConnectionAttestationProtocol.HGS) + { + return false; + } + break; + + case TdsEnums.ENCLAVE_TYPE_SGX: + if (_connHandler.ConnectionOptions.AttestationProtocol != SqlConnectionAttestationProtocol.AAS) + { + return false; + } + break; + + default: + // if we reach here, the enclave type is not supported + throw SQL.EnclaveTypeNotSupported(enclaveType); + } + return true; } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs index 5bf47a3c97..b786bdaa03 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs @@ -11646,6 +11646,16 @@ internal static string TCE_DbConnectionString_EnclaveAttestationUrl { return ResourceManager.GetString("TCE_DbConnectionString_EnclaveAttestationUrl", resourceCulture); } } + + /// + /// Looks up a localized string similar to Specifies an attestation protocol for its corresponding enclave attestation service. + /// + /// + internal static string TCE_DbConnectionString_AttestationProtocol { + get { + return ResourceManager.GetString("TCE_DbConnectionString_AttestationProtocol", resourceCulture); + } + } /// /// Looks up a localized string similar to Decryption failed. The last 10 bytes of the encrypted column encryption key are: '{0}'. The first 10 bytes of ciphertext are: '{1}'.. @@ -11826,7 +11836,25 @@ internal static string TCE_EnclaveTypeNotReturned { return ResourceManager.GetString("TCE_EnclaveTypeNotReturned", resourceCulture); } } - + + /// + /// Looks up a localized string similar to The enclave type '{0}' returned from the server is not supported. + /// + internal static string TCE_EnclaveTypeNotSupported { + get { + return ResourceManager.GetString("TCE_EnclaveTypeNotSupported", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to initialize connection. The attestation protocol does not support the enclave type '{0}'. + /// + internal static string TCE_AttestationProtocolNotSupportEnclaveType { + get { + return ResourceManager.GetString("TCE_AttestationProtocolNotSupportEnclaveType", resourceCulture); + } + } + /// /// Looks up a localized string similar to Internal Error. Enclave type received from SQL Server is null or empty when executing a query requiring enclave computations.. /// diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx index 88e750c707..e71eda5734 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx @@ -4443,4 +4443,13 @@ Execution Timeout Expired. The timeout period elapsed prior to completion of the operation or the server is not responding. + + Specifies an attestation protocol for its corresponding enclave attestation service. + + + The enclave type '{0}' returned from the server is not supported. + + + Failed to initialize connection. The attestation protocol does not support the enclave type '{0}'. + \ No newline at end of file From a3b20c729ba33636af65a6c75e7678d31fdf27af Mon Sep 17 00:00:00 2001 From: Javad Rahnama Date: Mon, 7 Oct 2019 16:21:25 -0700 Subject: [PATCH 02/63] Files for Enclave Provider --- .../AlwaysEncryptedAttestationException.xml | 25 + .../EnclavePublicKey.xml | 14 + .../netfx/src/Microsoft.Data.SqlClient.csproj | 12 + .../AlwaysEncryptedAttestationException.cs | 21 + .../AlwaysEncryptedEnclaveProviderUtils.cs | 92 +++ .../AzureAttestationBasedEnclaveProvider.cs | 571 ++++++++++++++++++ .../Data/SqlClient/EnclaveProviderBase.cs | 258 ++++++++ .../Data/SqlClient/EnclaveSessionCache.cs | 103 ++++ ...tionEnclaveProviderConfigurationManager.cs | 1 - .../VirtualSecureModeEnclaveProvider.cs | 544 +++++++++++++++++ .../VirtualSecureModeEnclaveProviderBase.cs | 445 ++++++++++++++ .../netfx/src/Resources/Strings.Designer.cs | 196 +++++- .../netfx/src/Resources/Strings.resx | 63 ++ 13 files changed, 2340 insertions(+), 5 deletions(-) create mode 100644 doc/snippets/Microsoft.Data.SqlClient/AlwaysEncryptedAttestationException.xml create mode 100644 doc/snippets/Microsoft.Data.SqlClient/EnclavePublicKey.xml create mode 100644 src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.cs create mode 100644 src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs create mode 100644 src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs create mode 100644 src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs create mode 100644 src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveSessionCache.cs create mode 100644 src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.cs create mode 100644 src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs diff --git a/doc/snippets/Microsoft.Data.SqlClient/AlwaysEncryptedAttestationException.xml b/doc/snippets/Microsoft.Data.SqlClient/AlwaysEncryptedAttestationException.xml new file mode 100644 index 0000000000..7b3391f86a --- /dev/null +++ b/doc/snippets/Microsoft.Data.SqlClient/AlwaysEncryptedAttestationException.xml @@ -0,0 +1,25 @@ + + + + + Represents errors occuring during an Always Encrypted secure enclave operation + + To be added. + + + + Default Constructor. + + To be added. + + + Constructor + + + + Constructor with Parameters. + + + + + diff --git a/doc/snippets/Microsoft.Data.SqlClient/EnclavePublicKey.xml b/doc/snippets/Microsoft.Data.SqlClient/EnclavePublicKey.xml new file mode 100644 index 0000000000..41b0d371b9 --- /dev/null +++ b/doc/snippets/Microsoft.Data.SqlClient/EnclavePublicKey.xml @@ -0,0 +1,14 @@ + + + + Class to hold the enclave's RSA public key + + + To be added. + + + Instantiates the Class with assigning received payload value to PublicKey + + + + 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 2cb40a0c1d..e9a58ca14a 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -74,6 +74,7 @@ + @@ -88,12 +89,17 @@ + + + + + @@ -342,6 +348,12 @@ 3.0.8 + + 5.5.0 + + + 5.5.0 + 4.5.0 diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.cs new file mode 100644 index 0000000000..7716a91539 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.cs @@ -0,0 +1,21 @@ +// 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; + +namespace Microsoft.Data.SqlClient +{ + /// + public class AlwaysEncryptedAttestationException : Exception + { + /// + public AlwaysEncryptedAttestationException(string message, Exception innerException) : base(message, innerException) { } + + /// + public AlwaysEncryptedAttestationException(string message) : base(message) { } + + /// + public AlwaysEncryptedAttestationException() : base() { } + } +} diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs new file mode 100644 index 0000000000..ebd70821fe --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs @@ -0,0 +1,92 @@ +// 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.Linq; + +namespace Microsoft.Data.SqlClient +{ + /// + /// Class to hold the enclave's RSA public key + /// + /// + public class EnclavePublicKey + { + /// + /// + /// + /// + public byte[] PublicKey { get; set; } + + /// + public EnclavePublicKey(byte[] payload) + { + PublicKey = payload; + } + } + + /// + /// Class to hold the Enclave's Diffie-Hellman public key and signature + /// + public class EnclaveDiffieHellmanInfo + { + /// + /// + /// + public int Size { get; private set; } + + /// + /// + /// + public byte[] PublicKey { get; private set; } + + /// + /// + /// + public byte[] PublicKeySignature { get; private set; } + + /// + /// + /// + /// + public EnclaveDiffieHellmanInfo(byte[] payload) + { + Size = payload.Length; + + int offset = 0; + int publicKeySize = BitConverter.ToInt32(payload, offset); + offset += sizeof(int); + + int publicKeySignatureSize = BitConverter.ToInt32(payload, offset); + offset += sizeof(int); + + PublicKey = payload.Skip(offset).Take(publicKeySize).ToArray(); + offset += publicKeySize; + + PublicKeySignature = payload.Skip(offset).Take(publicKeySignatureSize).ToArray(); + offset += publicKeySignatureSize; + } + } + + /// + /// + /// + public enum EnclaveType + { + /// + /// + /// + None = 0, + + /// + /// + /// + Vbs = 1, + + /// + /// + /// + Sgx = 2 + } +} diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs new file mode 100644 index 0000000000..020f0bcb20 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs @@ -0,0 +1,571 @@ +// 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.IdentityModel.Tokens.Jwt; +using System.Linq; +using System.Runtime.Caching; +using System.Security.Claims; +using System.Security.Cryptography; +using System.Text; +using System.Threading; +using Microsoft.IdentityModel.JsonWebTokens; +using Microsoft.IdentityModel.Logging; +using Microsoft.IdentityModel.Protocols; +using Microsoft.IdentityModel.Protocols.OpenIdConnect; +using Microsoft.IdentityModel.Tokens; + +// Azure Attestation Protocol Flow +// To start the attestation process, Sql Client sends the Protocol Id (i.e. 1), Nonce, Attestation Url and ECDH Public Key +// Sql Server uses attestation Url to attest the enclave and send the JWT to Sql client. +// Along with JWT, Sql server also sends enclave RSA public key, enclave Type, Enclave ECDH Public key. + +// To verify the chain of trust here is how it works +// JWT is signed by well-known signing keys which Sql client can download over https (via OpenIdConnect protocol). +// JWT contains the Enclave public key to safeguard against spoofing enclave RSA public key. +// Enclave ECDH public key signed by enclave RSA key + +// JWT validation +// To get the signing key for the JWT, we use OpenIdConnect API's. It download the signing keys from the well-known endpoint. +// We validate that JWT is signed, valid (i.e. not expired) and check the Issuer. + +// Claim validation: +// Validate the RSA public key send by Sql server matches the value specified in JWT. + +// Enclave Specific checks +// VSM +// Validate the nonce send by Sql client during start of attestation is same as that of specified in the JWT + +// SGX +// JWT for SGX enclave does not contain nonce claim. To workaround this limitation Sql Server sends the RSA public key XOR with the Nonce. +// In Sql server tempered with the nonce value then both Sql Server and client will not able to compute the same shared secret. + +namespace Microsoft.Data.SqlClient +{ + /// + /// Implementation of an Enclave provider (both for Sgx and Vsm) with Azure Attestation + /// + public class AzureAttestationEnclaveProvider : EnclaveProviderBase + { + #region Constants + private const int DiffieHellmanKeySize = 384; + private const int AzureBasedAttestationProtocolId = 1; + private const int SigningKeyRetryInSec = 3; + #endregion + + #region Members + // this is meta data endpoint for AAS provided by Windows team + // i.e. https:///.well-known/openid-configuration + // such as https://sql.azure.attest.com/.well-known/openid-configuration + private const string AttestationUrlSuffix = @"/.well-known/openid-configuration"; + + private static readonly MemoryCache OpenIdConnectConfigurationCache = new MemoryCache("OpenIdConnectConfigurationCache"); + #endregion + + #region Public methods + /// + /// 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. + /// + /// The name of the SQL Server instance containing the enclave. + /// The endpoint of an attestation service, SqlClient contacts to attest the enclave. + /// When this method returns, the requested enclave session or null if the provider doesn't implement session caching. This parameter is treated as uninitialized. + /// A counter that the enclave provider is expected to increment each time SqlClient retrieves the session from the cache. The purpose of this field is to prevent replay attacks. + public override void GetEnclaveSession(string servername, string attestationUrl, out SqlEnclaveSession sqlEnclaveSession, out long counter) + { + GetEnclaveSessionHelper(servername, attestationUrl, true, out sqlEnclaveSession, out counter); + } + + /// + /// Gets the information that SqlClient subsequently uses to initiate the process of attesting the enclave and to establish a secure session with the enclave. + /// + /// The information SqlClient subsequently uses to initiate the process of attesting the enclave and to establish a secure session with the enclave. + public override SqlEnclaveAttestationParameters GetAttestationParameters() + { + ECDiffieHellmanCng clientDHKey = new ECDiffieHellmanCng(DiffieHellmanKeySize); + clientDHKey.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash; + clientDHKey.HashAlgorithm = CngAlgorithm.Sha256; + byte[] attestationParam = PrepareAttestationParameters(); + return new SqlEnclaveAttestationParameters(AzureBasedAttestationProtocolId, attestationParam, 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. + /// + /// The information the provider uses to attest the enclave and generate a symmetric key for the session. The format of this information is specific to the enclave attestation protocol. + /// A Diffie-Hellman algorithm object that encapsulates a client-side key pair. + /// The endpoint of an attestation service for attesting the enclave. + /// The name of the SQL Server instance containing the enclave. + /// The requested enclave session or null if the provider doesn't implement session caching. + /// A counter that the enclave provider is expected to increment each time SqlClient retrieves the session from the cache. The purpose of this field is to prevent replay attacks. + public override void CreateEnclaveSession(byte[] attestationInfo, ECDiffieHellmanCng clientDHKey, string attestationUrl, string servername, out SqlEnclaveSession sqlEnclaveSession, out long counter) + { + sqlEnclaveSession = null; + counter = 0; + try + { + AttestationInfoCacheItem attestationInfoCacheItem = AttestationInfoCache.Remove(Thread.CurrentThread.ManagedThreadId.ToString()) as AttestationInfoCacheItem; + sqlEnclaveSession = GetEnclaveSessionFromCache(servername, attestationUrl, out counter); + if (sqlEnclaveSession == null) + { + if (attestationInfoCacheItem != null) + { + byte[] nonce = attestationInfoCacheItem.AttestNonce; + + IdentityModelEventSource.ShowPII = true; + + // Deserialize the payload + AzureAttestationInfo attestInfo = new AzureAttestationInfo(attestationInfo); + + // Validate the attestation info + VerifyAzureAttestationInfo(attestationUrl, attestInfo.EnclaveType, attestInfo.AttestationToken.AttestationToken, attestInfo.Identity, nonce); + + // Set up shared secret and validate signature + byte[] sharedSecret = GetSharedSecret(attestInfo.Identity, nonce, attestInfo.EnclaveType, attestInfo.EnclaveDHInfo, clientDHKey); + + // add session to cache + sqlEnclaveSession = AddEnclaveSessionToCache(attestationUrl, servername, sharedSecret, attestInfo.SessionId, out counter); + } + else + { + throw new AlwaysEncryptedAttestationException(Strings.FailToCreateEnclaveSession); + } + } + } + finally + { + // As per current design, we want to minimize the number of create session calls. To acheive this we block all the GetEnclaveSession calls until the first call to + // GetEnclaveSession -> GetAttestationParameters -> CreateEnclaveSession completes or the event timeout happen. + // Case 1: When the first request successfully creates the session, then all outstanding GetEnclaveSession will use the current session. + // Case 2: When the first request unable to create the encalve session (may be due to some error or the first request doesn't require enclave computation) then in those case we set the event timeout to 0. + UpdateEnclaveSessionLockStatus(sqlEnclaveSession); + } + } + + /// + /// When overridden in a derived class, looks up and evicts an enclave session from the enclave session cache, if the provider implements session caching. + /// + /// The name of the SQL Server instance containing the enclave. + /// The endpoint of an attestation service, SqlClient contacts to attest the enclave. + /// The session to be invalidated. + public override void InvalidateEnclaveSession(string serverName, string enclaveAttestationUrl, SqlEnclaveSession enclaveSessionToInvalidate) + { + InvalidateEnclaveSessionHelper(serverName, enclaveAttestationUrl, enclaveSessionToInvalidate); + } + #endregion + + #region Internal Class + /// + /// A model class respresenting the deserialization of the byte payload the client + /// receives from SQL Server while setting up a session. + /// Protocol format: + /// 1. Total Size of the attestation blob as UINT + /// 2. Size of Enclave RSA public key as UINT + /// 3. Size of Attestation token as UINT + /// 4. Enclave Type as UINT + /// 5. Enclave RSA public key (raw key, of length #2) + /// 6. Attestation token (of length #3) + /// 7. Size of Session Id was UINT + /// 8. Session id value + /// 9. Size of enclave ECDH public key + /// 10. Enclave ECDH public key (of length #9) + /// + internal class AzureAttestationInfo + { + public uint TotalSize { get; set; } + + /// + /// The enclave's RSA Public Key. + /// Needed to establish trust of the enclave. + /// Used to verify the enclave's DiffieHellman info. + /// + public EnclavePublicKey Identity { get; set; } + + /// + /// The enclave report from the SQL Server host's enclave. + /// + public AzureAttestationToken AttestationToken { get; set; } + + /// + /// The id of the current session. + /// Needed to set up a secure session between the client and enclave. + /// + public long SessionId { get; set; } + + public EnclaveType EnclaveType { get; set; } + + /// + /// The DiffieHellman public key and signature of SQL Server host's enclave. + /// Needed to set up a secure session between the client and enclave. + /// + public EnclaveDiffieHellmanInfo EnclaveDHInfo { get; set; } + + public AzureAttestationInfo(byte[] attestationInfo) + { + try + { + int offset = 0; + + // Total size of the attestation info buffer + TotalSize = BitConverter.ToUInt32(attestationInfo, offset); + offset += sizeof(uint); + + // Size of the Enclave public key + int identitySize = BitConverter.ToInt32(attestationInfo, offset); + offset += sizeof(uint); + + // Size of the Azure attestation token + int attestationTokenSize = BitConverter.ToInt32(attestationInfo, offset); + offset += sizeof(uint); + + // Enclave type + int enclaveType = BitConverter.ToInt32(attestationInfo, offset); + EnclaveType = (EnclaveType)enclaveType; + offset += sizeof(uint); + + // Get the enclave public key + byte[] identityBuffer = attestationInfo.Skip(offset).Take(identitySize).ToArray(); + Identity = new EnclavePublicKey(identityBuffer); + offset += identitySize; + + // Get Azure attestation token + byte[] attestationTokenBuffer = attestationInfo.Skip(offset).Take(attestationTokenSize).ToArray(); + AttestationToken = new AzureAttestationToken(attestationTokenBuffer); + offset += attestationTokenSize; + + uint secureSessionInfoResponseSize = BitConverter.ToUInt32(attestationInfo, offset); + offset += sizeof(uint); + + SessionId = BitConverter.ToInt64(attestationInfo, offset); + offset += sizeof(long); + + int secureSessionBufferSize = Convert.ToInt32(secureSessionInfoResponseSize) - sizeof(uint); + byte[] secureSessionBuffer = attestationInfo.Skip(offset).Take(secureSessionBufferSize).ToArray(); + EnclaveDHInfo = new EnclaveDiffieHellmanInfo(secureSessionBuffer); + offset += Convert.ToInt32(EnclaveDHInfo.Size); + } + catch (Exception exception) + { + throw new AlwaysEncryptedAttestationException(String.Format(Strings.FailToParseAttestationInfo, exception.Message)); + } + } + } + + /// + /// A managed model representing the output of EnclaveGetAttestationReport + /// https://msdn.microsoft.com/en-us/library/windows/desktop/mt844233(v=vs.85).aspx + /// + internal class AzureAttestationToken + { + public string AttestationToken { get; set; } + + public AzureAttestationToken(byte[] payload) + { + string jwt = System.Text.Encoding.Default.GetString(payload); + AttestationToken = jwt.Trim().Trim('"'); + } + } + #endregion Internal Class + + #region Private helpers + /// + /// Prepare the attestation data in following format + /// Attestation Url length + /// Attestation Url + /// Size of nonce + /// Nonce value + /// + internal byte[] PrepareAttestationParameters() + { + AttestationInfoCacheItem attestationInfoCacheItem = AttestationInfoCache[Thread.CurrentThread.ManagedThreadId.ToString()] as AttestationInfoCacheItem; + if (attestationInfoCacheItem != null) + { + // In c# strings are not null terminated, so adding the null termination before serializing it + string attestationUrlLocal = attestationInfoCacheItem.AttestationUrl + char.MinValue; + byte[] serializedAttestationUrl = Encoding.Unicode.GetBytes(attestationUrlLocal); + byte[] serializedAttestationUrlLength = BitConverter.GetBytes(serializedAttestationUrl.Length); + + // serializing nonce + byte[] serializedNonce = attestationInfoCacheItem.AttestNonce; + byte[] serializedNonceLength = BitConverter.GetBytes(attestationInfoCacheItem.AttestNonce.Length); + + // Computing the total length of the data + int totalDataSize = serializedAttestationUrl.Length + serializedAttestationUrlLength.Length + serializedNonce.Length + serializedNonceLength.Length; + + int dataCopied = 0; + byte[] attestationParam = new byte[totalDataSize]; + + // copy the attestation url and url length + Buffer.BlockCopy(serializedAttestationUrlLength, 0, attestationParam, dataCopied, serializedAttestationUrlLength.Length); + dataCopied += serializedAttestationUrlLength.Length; + + Buffer.BlockCopy(serializedAttestationUrl, 0, attestationParam, dataCopied, serializedAttestationUrl.Length); + dataCopied += serializedAttestationUrl.Length; + + // copy the nonce and nonce length + Buffer.BlockCopy(serializedNonceLength, 0, attestationParam, dataCopied, serializedNonceLength.Length); + dataCopied += serializedNonceLength.Length; + + Buffer.BlockCopy(serializedNonce, 0, attestationParam, dataCopied, serializedNonce.Length); + dataCopied += serializedNonce.Length; + + return attestationParam; + } + else + { + throw new AlwaysEncryptedAttestationException(Strings.FailToCreateEnclaveSession); + } + } + + private void VerifyAzureAttestationInfo(string attestationUrl, EnclaveType enclaveType, string attestationToken, EnclavePublicKey enclavePublicKey, byte[] nonce) + { + bool shouldForceUpdateSigningKeys = false; + string attestationInstanceUrl = GetAttestationInstanceUrl(attestationUrl); + + bool shouldRetryValidation; + bool isSignatureValid; + string exceptionMessage = string.Empty; + do + { + shouldRetryValidation = false; + + // Get the OpenId config object for the signing keys + OpenIdConnectConfiguration openIdConfig = GetOpenIdConfigForSigningKeys(attestationInstanceUrl, shouldForceUpdateSigningKeys); + + // Verify the token signature against the signing keys downloaded from meta data end point + bool isKeySigningExpired; + isSignatureValid = VerifyTokenSignature(attestationToken, attestationInstanceUrl, openIdConfig.SigningKeys, out isKeySigningExpired, out exceptionMessage); + + // In cases if we fail to validate the token, since we are using the old signing keys + // let's re-download the signing keys again and re-validate the token signature + if (!isSignatureValid && isKeySigningExpired && !shouldForceUpdateSigningKeys) + { + shouldForceUpdateSigningKeys = true; + shouldRetryValidation = true; + } + } + while (shouldRetryValidation); + + if (!isSignatureValid) + { + throw new AlwaysEncryptedAttestationException(String.Format(Strings.AttestationTokenSignatureValidationFailed, exceptionMessage)); + } + + // Validate claims in the token + ValidateAttestationClaims(enclaveType, attestationToken, enclavePublicKey, nonce); + } + + private static string GetInnerMostExceptionMessage(Exception exception) + { + Exception exLocal = exception; + while (exLocal.InnerException != null) + { + exLocal = exLocal.InnerException; + } + + return exLocal.Message; + } + + private OpenIdConnectConfiguration GetOpenIdConfigForSigningKeys(string url, bool forceUpdate) + { + OpenIdConnectConfiguration openIdConnectConfig = OpenIdConnectConfigurationCache[url] as OpenIdConnectConfiguration; + if (forceUpdate || openIdConnectConfig == null) + { + // Compute the meta data endpoint + string openIdMetadataEndpoint = url + AttestationUrlSuffix; + + try + { + IConfigurationManager configurationManager = new ConfigurationManager(openIdMetadataEndpoint, new OpenIdConnectConfigurationRetriever()); + openIdConnectConfig = configurationManager.GetConfigurationAsync(CancellationToken.None).Result; + } + catch (Exception exception) + { + throw new AlwaysEncryptedAttestationException(String.Format(Strings.GetAttestationTokenSigningKeysFailed, GetInnerMostExceptionMessage(exception)), exception); + } + + OpenIdConnectConfigurationCache.Add(url, openIdConnectConfig, DateTime.UtcNow.AddDays(1)); + } + + return openIdConnectConfig; + } + + private string GetAttestationInstanceUrl(string attestationUrl) + { + Uri attestationUri = new Uri(attestationUrl); + return attestationUri.GetLeftPart(UriPartial.Authority); + } + + private static ICollection GenerateListOfIssuers(string tokenIssuerUrl) + { + List issuerUrls = new List(); + + Uri tokenIssuerUri = new Uri(tokenIssuerUrl); + int port = tokenIssuerUri.Port; + bool isDefaultPort = tokenIssuerUri.IsDefaultPort; + + string issuerUrl = tokenIssuerUri.GetLeftPart(UriPartial.Authority); + issuerUrls.Add(issuerUrl); + + if (isDefaultPort) + { + issuerUrls.Add(String.Concat(issuerUrl, ":", port.ToString())); + } + + return issuerUrls; + } + + private bool VerifyTokenSignature(string attestationToken, string tokenIssuerUrl, ICollection issuerSigningKeys, out bool isKeySigningExpired, out string exceptionMessage) + { + exceptionMessage = string.Empty; + bool isSignatureValid = false; + isKeySigningExpired = false; + + // Configure the TokenValidationParameters + TokenValidationParameters validationParameters = + new TokenValidationParameters + { + RequireExpirationTime = true, + ValidateLifetime = true, + ValidateIssuer = true, + ValidateAudience = false, + RequireSignedTokens = true, + ValidIssuers = GenerateListOfIssuers(tokenIssuerUrl), + IssuerSigningKeys = issuerSigningKeys + }; + + try + { + SecurityToken validatedToken; + JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler(); + var token = handler.ValidateToken(attestationToken, validationParameters, out validatedToken); + isSignatureValid = true; + } + catch (SecurityTokenExpiredException securityException) + { + throw new AlwaysEncryptedAttestationException(Strings.ExpiredAttestationToken, securityException); + } + catch (SecurityTokenValidationException securityTokenException) + { + isKeySigningExpired = true; + + // Sleep for SigningKeyRetryInSec sec before retrying to download the signing keys again. + Thread.Sleep(SigningKeyRetryInSec * 1000); + exceptionMessage = GetInnerMostExceptionMessage(securityTokenException); + } + catch (Exception exception) + { + throw new AlwaysEncryptedAttestationException(String.Format(Strings.InvalidAttestationToken, GetInnerMostExceptionMessage(exception))); + } + + return isSignatureValid; + } + + private byte[] ComputeSHA256(byte[] data) + { + byte[] result = null; + try + { + using (SHA256 sha256 = SHA256.Create()) + { + result = sha256.ComputeHash(data); + } + } + catch (Exception argumentException) + { + throw new AlwaysEncryptedAttestationException(Strings.InvalidArgumentToSHA256, argumentException); + } + return result; + } + + private void ValidateAttestationClaims(EnclaveType enclaveType, string attestationToken, EnclavePublicKey enclavePublicKey, byte[] nonce) + { + // Read the json token + JsonWebToken token = null; + try + { + JsonWebTokenHandler tokenHandler = new JsonWebTokenHandler(); + token = tokenHandler.ReadJsonWebToken(attestationToken); + } + catch (ArgumentException argumentException) + { + throw new AlwaysEncryptedAttestationException(String.Format(Strings.FailToParseAttestationToken, argumentException.Message)); + } + + // Get all the claims from the token + Dictionary claims = new Dictionary(); + foreach (Claim claim in token.Claims.ToList()) + { + claims.Add(claim.Type, claim.Value); + } + + // Get Enclave held data claim and validate it with the Base64UrlEncode(enclave public key) + ValidateClaim(claims, "aas-ehd", enclavePublicKey.PublicKey); + + if (enclaveType == EnclaveType.Vbs) + { + // Get rp_data claim and validate it with the Base64UrlEncode(nonce) + ValidateClaim(claims, "rp_data", nonce); + } + } + + private void ValidateClaim(Dictionary claims, string claimName, byte[] actualData) + { + // Get required claim data + string claimData; + bool hasClaim = claims.TryGetValue(claimName, out claimData); + if (!hasClaim) + { + throw new AlwaysEncryptedAttestationException(String.Format(Strings.MissingClaimInAttestationToken, claimName)); + } + + // Get the Base64Url of the actual data and compare it with claim + string encodedActualData = string.Empty; + try + { + encodedActualData = Base64UrlEncoder.Encode(actualData); + } + catch (Exception) + { + throw new AlwaysEncryptedAttestationException(Strings.InvalidArgumentToBase64UrlDecoder); + } + + bool hasValidClaim = String.Equals(encodedActualData, claimData, StringComparison.InvariantCultureIgnoreCase); + if (!hasValidClaim) + { + throw new AlwaysEncryptedAttestationException(String.Format(Strings.InvalidClaimInAttestationToken, claimName, claimData)); + } + } + + private byte[] GetSharedSecret(EnclavePublicKey enclavePublicKey, byte[] nonce, EnclaveType enclaveType, EnclaveDiffieHellmanInfo enclaveDHInfo, ECDiffieHellmanCng clientDHKey) + { + byte[] enclaveRsaPublicKey = enclavePublicKey.PublicKey; + + // For SGX enclave we Sql server sends the enclave public key XOR'ed with Nonce. + // In case if Sql server replayed old JWT then shared secret will not match and hence client will not able to determine the updated enclave keys. + if (enclaveType == EnclaveType.Sgx) + { + for (int iterator = 0; iterator < enclaveRsaPublicKey.Length; iterator++) + { + enclaveRsaPublicKey[iterator] = (byte)(enclaveRsaPublicKey[iterator] ^ nonce[iterator % nonce.Length]); + } + } + + // Perform signature verification. The enclave's DiffieHellman public key was signed by the enclave's RSA public key. + CngKey cngkey = CngKey.Import(enclaveRsaPublicKey, CngKeyBlobFormat.GenericPublicBlob); + using (RSACng rsacng = new RSACng(cngkey)) + { + if (!rsacng.VerifyData(enclaveDHInfo.PublicKey, enclaveDHInfo.PublicKeySignature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)) + { + throw new ArgumentException(Strings.GetSharedSecretFailed); + } + } + + CngKey key = CngKey.Import(enclaveDHInfo.PublicKey, CngKeyBlobFormat.GenericPublicBlob); + return clientDHKey.DeriveKeyMaterial(key); + } + #endregion + } +} diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs new file mode 100644 index 0000000000..917bd90af4 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs @@ -0,0 +1,258 @@ +// 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.Runtime.Caching; +using System.Security.Cryptography; +using System.Threading; + + +// Enclave session locking model +// 1. For doing the enclave attestation, driver makes either 1, 2 or 3 API calls(in order) +// - GetEnclaveSession +// - GetAttestationParameters +// - CreateEnclaveSession + +// First API call when an enclave session is cached. +// First 2 API calls when we are running a non-enclave query with no session cached. +// All 3 API calls in order when you run an enclave query with no session cached. + +// In case if the enclave session is cached and validate(not expired), GetEnclaveSession API returns the SqlEnclaveSession. +// In case if the enclave session is not cached then driver end up calling GetAttestationParameters and CreateEnclaveSession. +// Note: When we have non-enclave query, then in those cases we never call CreateEnclaveSession. This is one of main pivot point for designing the below locking model. +// As per current API design driver passes attestation url and the server name during GetEnclaveSession but not in GetAttestationParameters. +// In order to create the attestation parameter enclave provider needs to know the attestation url. To overcome this limitation we added a AttestationInfoCache memory cache +// which save the attestation url and nonce with current thread as the lookup key. +// Later we use the AttestationInfoCache object to retrieve the attestation url in GetAttestationParameters which we saved during CreateEnclaveSession call. + +// 2. In case during application start, if app spins of multiple threads at the same time (during stress test or benchmarking) where DB connection is Always encrypted enabled, +// then with the existing design we end up creating multiple enclave session. Each enclave session adds an extra memory overhead to the system and also it generates multiple calls to attestation +// service, which customer may be paying for. +// Current design try to collapse multiple GetEnclaveSession calls into a single call to CreateEnclaveSession.We achieve this goal by introducing a lock in GetEnclaveSession +// such that when we have some outstanding call doing the attestation, then all the other call to GetEnclaveSession wait for the ongoing attestation loop to complete. +// To avoid infinite thread starvation, we also added a lock timeout. +// If the ongoing attestation request completes successfully, then it creates the enclave session and release the lock so that all the subsequent request reads uses the cached value. +// In cases if the network is extremely slow and the lock timeout expires before the current ongoing attestation complete, in those cases we end up triggering the enclave attestation +// on the current thread. + +// Scenario (1) +// we have 2 threads, where the both threads require enclave computation. +// When thread one invokes GetEnclaveSession then it successfully sets the event (as its the first request in the system). +// Later when the 2nd thread comes along then it gets blocked on sessionLockEvent.WaitOne till attestation on thread 1 completes or event timeout. + +// case 1: Attestation on thread 1 completes before event timeout happens +// In this case thread 1 signal the event after completing the attestation and save the enclave session value. +// Thread 2 gets the event signaled and read the cache enclave session value and return. + +// case 2: Attestation on thread 1 does not complete before lock time happens +// In this case thread 1 is unable to signal on time. Hence thread 2 starts its own attestation process and reduces the timeout to 0 so that any +// further request doesn't get block on timeout. +// If the attestation request on either thread completes it will signal the event and reset the time out to default value. +// In this case if we have multiple threads (say n threads) waiting for the attestation request to complete and it never completes on the first thread then we end up making n attestation requests. + +// Scenario (2) +// We have 2 threads, where first query on thread 1 does not require enclave computation but subsequent query on thread 1 does, whereas on thread 2 all query needs enclave computation. +// When thread one invokes GetEnclaveSession then it successfully sets the event (as it the first request in the system). +// Later when the 2nd thread comes along then it gets blocked on sessionLockEvent.WaitOne till attestation on thread 1 completes or event timeout. + +// Running first query on thread 1 while thread 2 waiting (no timeout) +// In this case thread 1 never signal the event (while running 1st query) as it does not require enclave computation. +// So thread 2 keeps waiting either for timeout to happen or thread 1 again comes in setup the enclave session to signal it. + +// Running second query on thread 1 while thread 2 waiting (no timeout) +// In this case thread 1 don't have to wait for event as it’s already did it while running 1st query. +// Now thread 2 keeps waiting either for timeout to happen or thread 1 finish up setting the session. + +namespace Microsoft.Data.SqlClient +{ + /// + /// Base class for Enclave provider + /// + public abstract class EnclaveProviderBase : SqlColumnEncryptionEnclaveProvider + { + #region Constants + private const int NonceSize = 256; + private const int AttestationInfoCacheTimeoutInMinutes = 10; + private const int LockTimeoutMaxInMilliseconds = 15 * 1000; // 15 seconds + #endregion + + #region Members + private static readonly EnclaveSessionCache SessionCache = new EnclaveSessionCache(); + + private static AutoResetEvent sessionLockEvent = new AutoResetEvent(true); + + private static int lockTimeoutInMilliseconds = LockTimeoutMaxInMilliseconds; + + private static bool isSessionLockAcquired = false; + + private static readonly Object lockUpdateSessionLock = new Object(); + + internal class AttestationInfoCacheItem + { + public string AttestationUrl { get; private set; } + + public byte[] AttestNonce { get; private set; } + public AttestationInfoCacheItem(string attestationUri, byte[] nonce) + { + AttestationUrl = attestationUri; + AttestNonce = nonce; + } + } + + /// + /// It is used to save the attestation url and nonce value across API calls + /// + protected static readonly MemoryCache AttestationInfoCache = new MemoryCache("AttestationInfoCache"); + #endregion + + #region Public methods + /// + /// Helper method to get the enclave session from the cache if present + /// + /// The name of the SQL Server instance containing the enclave. + /// The endpoint of an attestation service, SqlClient contacts to attest the enclave. + /// True, if we want to create the nonce value. true for AAS + /// When this method returns, the requested enclave session or null if the provider doesn't implement session caching. This parameter is treated as uninitialized. + /// A counter that the enclave provider is expected to increment each time SqlClient retrieves the session from the cache. The purpose of this field is to prevent replay attacks. + protected void GetEnclaveSessionHelper(string servername, string attestationUrl, bool shouldGenerateNonce, out SqlEnclaveSession sqlEnclaveSession, out long counter) + { + sqlEnclaveSession = SessionCache.GetEnclaveSession(servername, attestationUrl, out counter); + if (sqlEnclaveSession == null) + { + bool sessionCacheLockTaken = false; + bool sameThreadRetry = false; + + // In case if on some thread we are running SQL workload which don't require attestation, then in those cases we don't want same thread to wait for event to be signaled. + // hence skipping it + AttestationInfoCacheItem attestationInfoCacheItem = AttestationInfoCache[Thread.CurrentThread.ManagedThreadId.ToString()] as AttestationInfoCacheItem; + if (attestationInfoCacheItem != null) + { + sameThreadRetry = true; + } + else + { + // We are explicitly not signalling the event here, as we want to hold the event till driver calls CreateEnclaveSession + // If we signal the event now, then multiple thread end up calling GetAttestationParameters which triggers the attestation workflow. + sessionCacheLockTaken = sessionLockEvent.WaitOne(lockTimeoutInMilliseconds); + + if (sessionCacheLockTaken) + { + lock (lockUpdateSessionLock) + { + isSessionLockAcquired = true; + } + } + } + + // In case of multi-threaded application, first thread will set the event and all the subsequent threads will wait here either until the enclave + // session is created or timeout happens. + if (sessionCacheLockTaken || sameThreadRetry) + { + // While the current thread is waiting for event to be signaled and in the meanwhile we already completed the attestation on different thread + // then we need to signal the event here + sqlEnclaveSession = SessionCache.GetEnclaveSession(servername, attestationUrl, out counter); + if (sqlEnclaveSession != null && !sameThreadRetry) + { + lock (lockUpdateSessionLock) + { + isSessionLockAcquired = false; + sessionLockEvent.Set(); + } + } + } + else + { + // In case if we are unable to signal the event, then it represents either + // 1. On other thread we have an ongoing attestation request which is taking more time may due to slow network or + // 2. Current workload doesn't require enclave computation due to which driver is not invoking the CreateEnclaveSession, hence sqlEnclaveSession is never set. + // In both cases we need to reduce the timeout to 0 so that subsequent request should not wait. + Interlocked.Exchange(ref lockTimeoutInMilliseconds, 0); + } + + if (sqlEnclaveSession == null) + { + if (!sameThreadRetry) + { + // Client decides to initiate the process of attesting the enclave and to establish a secure session with the enclave. + // To ensure that server send new attestation request instead of replaying / re-sending the old token, we will create a nonce for current attestation request. + byte[] nonce = new byte[NonceSize]; + if (shouldGenerateNonce) + { + using (RandomNumberGenerator rng = new RNGCryptoServiceProvider()) + { + rng.GetBytes(nonce); + } + } + + attestationInfoCacheItem = new AttestationInfoCacheItem(attestationUrl, nonce); + } + + AttestationInfoCache.Set(Thread.CurrentThread.ManagedThreadId.ToString(), attestationInfoCacheItem, DateTime.UtcNow.AddMinutes(AttestationInfoCacheTimeoutInMinutes)); + } + } + } + + /// + /// Reset the session lock status + /// + /// + protected void UpdateEnclaveSessionLockStatus(SqlEnclaveSession sqlEnclaveSession) + { + // As per current design, we want to minimize the number of create session calls. To acheive this we block all the GetEnclaveSession calls until the first call to + // GetEnclaveSession -> GetAttestationParameters -> CreateEnclaveSession completes or the event timeout happens. + // Case 1: When the first request successfully creates the session, then all outstanding GetEnclaveSession will use the current session. + // Case 2: When the first request unable to create the encalve session (may be due to some error or the first request doesn't require enclave computation) then in those case we set the event timeout to 0. + if (sqlEnclaveSession != null && isSessionLockAcquired) + { + lock (lockUpdateSessionLock) + { + if (isSessionLockAcquired) + { + isSessionLockAcquired = false; + Interlocked.Exchange(ref lockTimeoutInMilliseconds, LockTimeoutMaxInMilliseconds); + sessionLockEvent.Set(); + } + } + } + } + + /// + /// Helper method to remove the enclave session from the cache + /// + /// The name of the SQL Server instance containing the enclave. + /// The endpoint of an attestation service, SqlClient contacts to attest the enclave. + /// + protected void InvalidateEnclaveSessionHelper(string servername, string attestationUrl, SqlEnclaveSession enclaveSessionToInvalidate) + { + SessionCache.InvalidateSession(servername, attestationUrl, enclaveSessionToInvalidate); + } + + /// + /// Helper method for getting the enclave session from the session cache + /// + /// The endpoint of an attestation service, SqlClient contacts to attest the enclave. + /// The name of the SQL Server instance containing the enclave. + /// A counter that the enclave provider is expected to increment each time SqlClient retrieves the session from the cache. The purpose of this field is to prevent replay attacks. + /// + protected SqlEnclaveSession GetEnclaveSessionFromCache(string attestationUrl, string servername, out long counter) + { + return SessionCache.GetEnclaveSession(servername, attestationUrl, out counter); + } + + /// + /// Helper method for adding the enclave session to the session cache + /// + /// The endpoint of an attestation service, SqlClient contacts to attest the enclave. + /// The name of the SQL Server instance containing the enclave. + /// Shared secret computed using enclave ECDH public key + /// Session id for the current enclave session + /// A counter that the enclave provider is expected to increment each time SqlClient retrieves the session from the cache. The purpose of this field is to prevent replay attacks. + /// + protected SqlEnclaveSession AddEnclaveSessionToCache(string attestationUrl, string servername, byte[] sharedSecret, long sessionId, out long counter) + { + return SessionCache.CreateSession(attestationUrl, servername, sharedSecret, sessionId, out counter); + } + } + #endregion +} diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveSessionCache.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveSessionCache.cs new file mode 100644 index 0000000000..ee3e248a29 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveSessionCache.cs @@ -0,0 +1,103 @@ +// 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.Runtime.Caching; +using System.Threading; + +namespace Microsoft.Data.SqlClient +{ + /// + /// Maintains a cache of SqlEnclaveSession instances + /// + internal class EnclaveSessionCache + { + private readonly MemoryCache enclaveMemoryCache = new MemoryCache("EnclaveMemoryCache"); + private readonly Object enclaveCacheLock = new Object(); + + /// + /// Nonce for each message sent by the client to the server to prevent replay attacks by the server, + /// given that for Always Encrypted scenarios, the server is considered an "untrusted" man-in-the-middle. + /// + private long _counter; + + /// + /// Cache timeout of 8 hours to be consistent with jwt validity. + /// + private static int enclaveCacheTimeOutInHours = 8; + + /// + /// Retrieves a SqlEnclaveSession from the cache + /// + /// + /// + /// + public SqlEnclaveSession GetEnclaveSession(string servername, string attestationUrl, out long counter) + { + string cacheKey = GenerateCacheKey(servername, attestationUrl); + SqlEnclaveSession enclaveSession = enclaveMemoryCache[cacheKey] as SqlEnclaveSession; + counter = Interlocked.Increment(ref _counter); + return enclaveSession; + } + + /// + /// Invalidates a SqlEnclaveSession entry in the cache + /// + /// + /// + /// + public void InvalidateSession(string serverName, string enclaveAttestationUrl, SqlEnclaveSession enclaveSessionToInvalidate) + { + string cacheKey = GenerateCacheKey(serverName, enclaveAttestationUrl); + + lock (enclaveCacheLock) + { + long counter; + SqlEnclaveSession enclaveSession = GetEnclaveSession(serverName, enclaveAttestationUrl, out counter); + + if (enclaveSession != null && enclaveSession.SessionId == enclaveSessionToInvalidate.SessionId) + { + SqlEnclaveSession enclaveSessionRemoved = enclaveMemoryCache.Remove(cacheKey) as SqlEnclaveSession; + if (enclaveSessionRemoved == null) + { + throw new InvalidOperationException(Strings.EnclaveSessionInvalidationFailed); + } + } + } + } + + /// + /// Creates a new SqlEnclaveSession and adds it to the cache + /// + /// + /// + /// + /// + /// + public SqlEnclaveSession CreateSession(string attestationUrl, string serverName, byte[] sharedSecret, long sessionId, out long counter) + { + string cacheKey = GenerateCacheKey(serverName, attestationUrl); + SqlEnclaveSession enclaveSession = null; + lock (enclaveCacheLock) + { + enclaveSession = new SqlEnclaveSession(sharedSecret, sessionId); + enclaveMemoryCache.Add(cacheKey, enclaveSession, DateTime.UtcNow.AddHours(enclaveCacheTimeOutInHours)); + counter = Interlocked.Increment(ref _counter); + } + + return enclaveSession; + } + + /// + /// Generates the cache key for the enclave session cache + /// + /// + /// + /// + private string GenerateCacheKey(string serverName, string attestationUrl) + { + return (serverName + attestationUrl).ToLowerInvariant(); + } + } +} diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProviderConfigurationManager.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProviderConfigurationManager.cs index 27aedaf2f1..b5eacebfca 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProviderConfigurationManager.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProviderConfigurationManager.cs @@ -18,7 +18,6 @@ internal class SqlColumnEncryptionEnclaveProviderConfigurationSection : Configur /// [ConfigurationProperty("providers")] public ProviderSettingsCollection Providers => (ProviderSettingsCollection)base["providers"]; - } internal class SqlColumnEncryptionEnclaveProviderConfigurationManager diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.cs new file mode 100644 index 0000000000..7e1b75cd91 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.cs @@ -0,0 +1,544 @@ +// 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.IO; +using System.Linq; +using System.Net; +using System.Runtime.Serialization.Json; +using System.Security.Cryptography.X509Certificates; +using System.Threading; + +namespace Microsoft.Data.SqlClient +{ + /// + /// Implementation of an Enclave provider for Windows Virtual Secure Mode enclaves + /// + public class HostGuardianServiceEnclaveProvider : VirtualizationBasedSecurityEnclaveProviderBase + { + #region Members + + // this is endpoint given to us by HGS team from windows + private const string AttestationUrlSuffix = @"/v2.0/signingCertificates"; + + /// + /// To be added. + /// + public int MaxNumRetries; + + private int enclaveRetrySleepInSeconds = 3; + + /// + /// To be added. + /// + public int EnclaveRetrySleepInSeconds + { + get + { + return enclaveRetrySleepInSeconds; + } + set + { + if (value < 1) + { + throw new ArgumentException(Strings.EnclaveRetrySleepInSecondsValueException); + } + + enclaveRetrySleepInSeconds = value; + } + } + + #endregion + + #region Private helpers + + /// + /// Return the endpoint for given attestation url + /// + /// The url to alter for corresponding provider + /// altered url + protected override string GetAttestationUrl(string attestationUrl) + { + return attestationUrl.TrimEnd('/') + AttestationUrlSuffix; + } + + /// + /// Makes a web request to the provided url and returns the response as a byte[] + /// + /// The url to make the request to + /// The response as a byte[] + protected override byte[] MakeRequest(string url) + { + Exception exception = null; + + for (int n = 0; n < MaxNumRetries + 1 /* Initial attempt + numRetries */; n++) + { + try + { + if (n != 0) + { + Thread.Sleep(EnclaveRetrySleepInSeconds * 1000); + } + + WebRequest request = WebRequest.Create(url); + + using (WebResponse response = request.GetResponse()) + using (Stream stream = response.GetResponseStream()) + { + var deserializer = new DataContractJsonSerializer(typeof(byte[])); + return (byte[])deserializer.ReadObject(stream); + } + } + catch (Exception e) + { + exception = e; + } + } + + throw new AlwaysEncryptedAttestationException(String.Format(Strings.GetAttestationSigningCertificateRequestFailedFormat, url, exception.Message), exception); + } + + #endregion + } + + #region Models + + /// + /// A model class respresenting the deserialization of the byte payload the client + /// receives from SQL Server while setting up a session. + /// + internal class AttestationInfo + { + public uint TotalSize { get; set; } + + /// + /// The enclave's RSA Public Key. + /// Needed to establish trust of the enclave. + /// Used to verify the enclave's DiffieHellman info. + /// + public EnclavePublicKey Identity { get; set; } + + /// + /// The SQL Server host's health report the server received from the attestation service + /// and forwarded to the client. + /// Needed to establish trust of the enclave report received from SQL Server. + /// Used to verify the enclave report's signature. + /// + public HealthReport HealthReport { get; set; } + + /// + /// The enclave report from the SQL Server host's enclave. + /// + public EnclaveReportPackage EnclaveReportPackage { get; set; } + + /// + /// The id of the current session. + /// Needed to set up a secure session between the client and enclave. + /// + public long SessionId { get; set; } + + /// + /// The DiffieHellman public key and signature of SQL Server host's enclave. + /// Needed to set up a secure session between the client and enclave. + /// + public EnclaveDiffieHellmanInfo EnclaveDHInfo { get; set; } + + public AttestationInfo(byte[] attestationInfo) + { + int offset = 0; + + TotalSize = BitConverter.ToUInt32(attestationInfo, offset); + offset += sizeof(uint); + + int identitySize = BitConverter.ToInt32(attestationInfo, offset); + offset += sizeof(uint); + + int healthReportSize = BitConverter.ToInt32(attestationInfo, offset); + offset += sizeof(uint); + + int enclaveReportSize = BitConverter.ToInt32(attestationInfo, offset); + offset += sizeof(uint); + + byte[] identityBuffer = attestationInfo.Skip(offset).Take(identitySize).ToArray(); + Identity = new EnclavePublicKey(identityBuffer); + offset += identitySize; + + byte[] healthReportBuffer = attestationInfo.Skip(offset).Take(healthReportSize).ToArray(); + HealthReport = new HealthReport(healthReportBuffer); + offset += healthReportSize; + + byte[] enclaveReportBuffer = attestationInfo.Skip(offset).Take(enclaveReportSize).ToArray(); + EnclaveReportPackage = new EnclaveReportPackage(enclaveReportBuffer); + offset += EnclaveReportPackage.GetSizeInPayload(); + + uint secureSessionInfoResponseSize = BitConverter.ToUInt32(attestationInfo, offset); + offset += sizeof(uint); + + SessionId = BitConverter.ToInt64(attestationInfo, offset); + offset += sizeof(long); + + int secureSessionBufferSize = Convert.ToInt32(secureSessionInfoResponseSize) - sizeof(uint); + byte[] secureSessionBuffer = attestationInfo.Skip(offset).Take(secureSessionBufferSize).ToArray(); + EnclaveDHInfo = new EnclaveDiffieHellmanInfo(secureSessionBuffer); + offset += Convert.ToInt32(EnclaveDHInfo.Size); + } + } + + /// + /// A model class to hold the SQL Server's host health report in an X509Certificate2 + /// + internal class HealthReport + { + private int Size { get; set; } + + public X509Certificate2 Certificate { get; set; } + + public HealthReport(byte[] payload) + { + Size = payload.Length; + Certificate = new X509Certificate2(payload); + } + + public int GetSizeInPayload() + { + return Size; + } + } + + /// + /// A managed model representing the output of EnclaveGetAttestationReport + /// https://msdn.microsoft.com/en-us/library/windows/desktop/mt844233(v=vs.85).aspx + /// + internal class EnclaveReportPackage + { + private int Size { get; set; } + + public EnclaveReportPackageHeader PackageHeader { get; set; } + + public EnclaveReport Report { get; set; } + + public List Modules { get; set; } + + public byte[] ReportAsBytes { get; set; } + + public byte[] SignatureBlob { get; set; } + + public EnclaveReportPackage(byte[] payload) + { + Size = payload.Length; + + int offset = 0; + PackageHeader = new EnclaveReportPackageHeader(payload.Skip(offset).ToArray()); + offset += PackageHeader.GetSizeInPayload(); + + Report = new EnclaveReport(payload.Skip(offset).ToArray()); + offset += Report.GetSizeInPayload(); + + // Modules are not used for anything currently, ignore parsing for now + // + // Modules = new List(); + // int reportSizeRemaining = Convert.ToInt32(Report.ReportSize) - Report.GetSizeInPayload(); + // while (reportSizeRemaining > 0) + // { + // var module = new VSMEnclaveReportModule(payload.Skip(offset).ToArray()); + // Modules.Add(module); + // reportSizeRemaining -= module.GetSizeInPayload(); + // offset += module.GetSizeInPayload(); + // } + + // Moving the offset back to the start of the report, + // we need the report as a byte buffer for signature verification. + offset = PackageHeader.GetSizeInPayload(); + int dataToHashSize = Convert.ToInt32(PackageHeader.SignedStatementSize); + ReportAsBytes = payload.Skip(offset).Take(dataToHashSize).ToArray(); + offset += dataToHashSize; + + int signatureSize = Convert.ToInt32(PackageHeader.SignatureSize); + SignatureBlob = payload.Skip(offset).Take(signatureSize).ToArray(); + offset += signatureSize; + } + + public int GetSizeInPayload() + { + return Size; + } + } + + /// + /// A managed model of struct VBS_ENCLAVE_REPORT_PKG_HEADER + /// https://msdn.microsoft.com/en-us/library/windows/desktop/mt844257(v=vs.85).aspx + /// + internal class EnclaveReportPackageHeader + { + public uint PackageSize { get; set; } + + public uint Version { get; set; } + + public uint SignatureScheme { get; set; } + + public uint SignedStatementSize { get; set; } + + public uint SignatureSize { get; set; } + + public uint Reserved { get; set; } + + public EnclaveReportPackageHeader(byte[] payload) + { + int offset = 0; + PackageSize = BitConverter.ToUInt32(payload, offset); + offset += sizeof(uint); + + Version = BitConverter.ToUInt32(payload, offset); + offset += sizeof(uint); + + SignatureScheme = BitConverter.ToUInt32(payload, offset); + offset += sizeof(uint); + + SignedStatementSize = BitConverter.ToUInt32(payload, offset); + offset += sizeof(uint); + + SignatureSize = BitConverter.ToUInt32(payload, offset); + offset += sizeof(uint); + + Reserved = BitConverter.ToUInt32(payload, offset); + offset += sizeof(uint); + } + + public int GetSizeInPayload() + { + return 6 * sizeof(uint); + } + } + + /// + /// A managed model of struct VBS_ENCLAVE_REPORT + /// https://msdn.microsoft.com/en-us/library/windows/desktop/mt844255(v=vs.85).aspx + /// + internal class EnclaveReport + { + private int Size { get; set; } + + public uint ReportSize { get; set; } + + public uint ReportVersion { get; set; } + + public byte[] EnclaveData { get; set; } + + private const int EnclaveDataLength = 64; + + public EnclaveIdentity Identity { get; set; } + + public EnclaveReport(byte[] payload) + { + Size = payload.Length; + + int offset = 0; + + ReportSize = BitConverter.ToUInt32(payload, offset); + offset += sizeof(uint); + + ReportVersion = BitConverter.ToUInt32(payload, offset); + offset += sizeof(uint); + + EnclaveData = payload.Skip(offset).Take(EnclaveDataLength).ToArray(); + offset += EnclaveDataLength; + + Identity = new EnclaveIdentity(payload.Skip(offset).ToArray()); + offset += Identity.GetSizeInPayload(); + } + + public int GetSizeInPayload() + { + return sizeof(uint) * 2 + sizeof(byte) * 64 + Identity.GetSizeInPayload(); + } + } + + /// + /// A managed model of struct ENCLAVE_IDENTITY + /// https://msdn.microsoft.com/en-us/library/windows/desktop/mt844239(v=vs.85).aspx + /// + internal class EnclaveIdentity + { + private int Size { get; set; } + + private static readonly int ImageEnclaveLongIdLength = 32; + + private static readonly int ImageEnclaveShortIdLength = 16; + + public byte[] OwnerId = new byte[ImageEnclaveLongIdLength]; + + public byte[] UniqueId = new byte[ImageEnclaveLongIdLength]; + + public byte[] AuthorId = new byte[ImageEnclaveLongIdLength]; + + public byte[] FamilyId = new byte[ImageEnclaveShortIdLength]; + + public byte[] ImageId = new byte[ImageEnclaveShortIdLength]; + + public uint EnclaveSvn { get; set; } + + public uint SecureKernelSvn { get; set; } + + public uint PlatformSvn { get; set; } + + public uint Flags { get; set; } + + public uint SigningLevel { get; set; } + + public uint Reserved { get; set; } + + public EnclaveIdentity() { } + + public EnclaveIdentity(byte[] payload) + { + Size = payload.Length; + + int offset = 0; + + int ownerIdLength = ImageEnclaveLongIdLength; + OwnerId = payload.Skip(offset).Take(ownerIdLength).ToArray(); + offset += ownerIdLength; + + int uniqueIdLength = ImageEnclaveLongIdLength; + UniqueId = payload.Skip(offset).Take(uniqueIdLength).ToArray(); + offset += uniqueIdLength; + + int authorIdLength = ImageEnclaveLongIdLength; + AuthorId = payload.Skip(offset).Take(authorIdLength).ToArray(); + offset += authorIdLength; + + int familyIdLength = ImageEnclaveShortIdLength; + FamilyId = payload.Skip(offset).Take(familyIdLength).ToArray(); + offset += familyIdLength; + + int imageIdLength = ImageEnclaveShortIdLength; + ImageId = payload.Skip(offset).Take(imageIdLength).ToArray(); + offset += imageIdLength; + + EnclaveSvn = BitConverter.ToUInt32(payload, offset); + offset += sizeof(uint); + + SecureKernelSvn = BitConverter.ToUInt32(payload, offset); + offset += sizeof(uint); + + PlatformSvn = BitConverter.ToUInt32(payload, offset); + offset += sizeof(uint); + + Flags = BitConverter.ToUInt32(payload, offset); + offset += sizeof(uint); + + SigningLevel = BitConverter.ToUInt32(payload, offset); + offset += sizeof(uint); + + Reserved = BitConverter.ToUInt32(payload, offset); + offset += sizeof(uint); + } + + public int GetSizeInPayload() + { + return sizeof(byte) * ImageEnclaveLongIdLength * 3 + sizeof(byte) * ImageEnclaveShortIdLength * 2 + sizeof(uint) * 6; + } + } + + /// + /// A managed model of struct VBS_ENCLAVE_REPORT_VARDATA_HEADER + /// https://msdn.microsoft.com/en-us/library/windows/desktop/mt827065(v=vs.85).aspx + /// + internal class EnclaveReportModuleHeader + { + public uint DataType { get; set; } + + public uint ModuleSize { get; set; } + + public EnclaveReportModuleHeader(byte[] payload) + { + int offset = 0; + DataType = BitConverter.ToUInt32(payload, offset); + offset += sizeof(uint); + + ModuleSize = BitConverter.ToUInt32(payload, offset); + offset += sizeof(uint); + } + + public int GetSizeInPayload() + { + return 2 * sizeof(uint); + } + } + + /// + /// A managed model of struct VBS_ENCLAVE_REPORT_MODULE + /// https://msdn.microsoft.com/en-us/library/windows/desktop/mt844256(v=vs.85).aspx + /// + internal class EnclaveReportModule + { + private static readonly int ImageEnclaveLongIdLength = 32; + + private static readonly int ImageEnclaveShortIdLength = 16; + + public EnclaveReportModuleHeader Header { get; set; } + + public byte[] UniqueId = new byte[ImageEnclaveLongIdLength]; + + public byte[] AuthorId = new byte[ImageEnclaveLongIdLength]; + + public byte[] FamilyId = new byte[ImageEnclaveShortIdLength]; + + public byte[] ImageId = new byte[ImageEnclaveShortIdLength]; + + public uint Svn { get; set; } + + public string ModuleName { get; set; } + + public EnclaveReportModule(byte[] payload) + { + int offset = 0; + Header = new EnclaveReportModuleHeader(payload); + offset += Convert.ToInt32(Header.GetSizeInPayload()); + + int uniqueIdLength = ImageEnclaveLongIdLength; + UniqueId = payload.Skip(offset).Take(uniqueIdLength).ToArray(); + offset += uniqueIdLength; + + int authorIdLength = ImageEnclaveLongIdLength; + AuthorId = payload.Skip(offset).Take(authorIdLength).ToArray(); + offset += authorIdLength; + + int familyIdLength = ImageEnclaveShortIdLength; + FamilyId = payload.Skip(offset).Take(familyIdLength).ToArray(); + offset += familyIdLength; + + int imageIdLength = ImageEnclaveShortIdLength; + ImageId = payload.Skip(offset).Take(familyIdLength).ToArray(); + offset += imageIdLength; + + Svn = BitConverter.ToUInt32(payload, offset); + offset += sizeof(uint); + + int strLen = Convert.ToInt32(Header.ModuleSize) - offset; + ModuleName = BitConverter.ToString(payload, offset, 1); + offset += sizeof(char) * 1; + } + + public int GetSizeInPayload() + { + return Header.GetSizeInPayload() + Convert.ToInt32(Header.ModuleSize); + } + } + + /// + /// An enum representing the Flags property of ENCLAVE_IDENTITY + /// https://msdn.microsoft.com/en-us/library/windows/desktop/mt844239(v=vs.85).aspx + /// + internal enum EnclaveIdentityFlags + { + ENCLAVE_FLAG_NONE = 0x00000000, + ENCLAVE_FLAG_FULL_DEBUG_ENABLED = 0x00000001, + ENCLAVE_FLAG_DYNAMIC_DEBUG_ENABLED = 0x00000002, + ENCLAVE_FLAG_DYNAMIC_DEBUG_ACTIVE = 0x00000004 + } + + #endregion +} diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs new file mode 100644 index 0000000000..7a6be7ed54 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs @@ -0,0 +1,445 @@ +// 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.Linq; +using System.Runtime.Caching; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Threading; + +namespace Microsoft.Data.SqlClient +{ + /// + /// To be added. + /// + public abstract class VirtualizationBasedSecurityEnclaveProviderBase : EnclaveProviderBase + { + #region Members + + private static readonly MemoryCache rootSigningCertificateCache = new MemoryCache("RootSigningCertificateCache"); + + #endregion + + #region Constants + + private const int DiffieHellmanKeySize = 384; + private const int VsmHGSProtocolId = 3; + + /// + /// ENCLAVE_IDENTITY related constants + /// + private static readonly EnclaveIdentity ExpectedPolicy = new EnclaveIdentity() + { + OwnerId = new byte[] + { + 0x10, 0x20, 0x30, 0x40, 0x41, 0x31, 0x21, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, + + UniqueId = new byte[] { }, + + // This field is calculated as follows: + // "Fixed Microsoft GUID" = {845319A6-706C-47BC-A7E8-5137B0BC750D} + // CN of the certificate that signed the file + // Opus Info - Authenticated Attribute that contains a Name and a URL + // + // In our case the Opus Info is: + // Description: Microsoft SQL Server Always Encrypted VBS Enclave Library, + // Description URL: https://go.microsoft.com/fwlink/?linkid=2018716 + AuthorId = new byte[] + { + 0x04, 0x37, 0xCA, 0xE2, 0x53, 0x7D, 0x8B, 0x9B, + 0x07, 0x76, 0xB6, 0x1B, 0x11, 0xE6, 0xCE, 0xD3, + 0xD2, 0x32, 0xE9, 0x30, 0x8F, 0x60, 0xE2, 0x1A, + 0xDA, 0xB2, 0xFD, 0x91, 0xE3, 0xDA, 0x95, 0x98 + }, + + FamilyId = new byte[] + { + 0xFE, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, + + ImageId = new byte[] + { + // This value should be same as defined in sqlserver code Sql/Ntdbms/aetm/enclave/dllmain.cpp + 0x19, 0x17, 0x12, 0x00, 0x01, 0x05, 0x20, 0x13, + 0x00, 0x05, 0x14, 0x03, 0x12, 0x01, 0x22, 0x05 + }, + + EnclaveSvn = 0, + + SecureKernelSvn = 0, + + PlatformSvn = 1, + + // 0: ENCLAVE_VBS_FLAG_NO_DEBUG in ds_main; Flag does not permit debug enclaves + Flags = 0, + + SigningLevel = 0, + + Reserved = 0 + }; + + #endregion + + #region Public methods + + /// + /// 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. + /// + /// The name of the SQL Server instance containing the enclave. + /// The endpoint of an attestation service, SqlClient contacts to attest the enclave. + /// When this method returns, the requested enclave session or null if the provider doesn't implement session caching. This parameter is treated as uninitialized. + /// A counter that the enclave provider is expected to increment each time SqlClient retrieves the session from the cache. The purpose of this field is to prevent replay attacks. + public override void GetEnclaveSession(string servername, string attestationUrl, out SqlEnclaveSession sqlEnclaveSession, out long counter) + { + GetEnclaveSessionHelper(servername, attestationUrl, false, out sqlEnclaveSession, out counter); + } + + /// + /// Gets the information that SqlClient subsequently uses to initiate the process of attesting the enclave and to establish a secure session with the enclave. + /// + /// The information SqlClient subsequently uses to initiate the process of attesting the enclave and to establish a secure session with the enclave. + public override SqlEnclaveAttestationParameters GetAttestationParameters() + { + ECDiffieHellmanCng clientDHKey = new ECDiffieHellmanCng(DiffieHellmanKeySize); + clientDHKey.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash; + clientDHKey.HashAlgorithm = CngAlgorithm.Sha256; + return new SqlEnclaveAttestationParameters(VsmHGSProtocolId, 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. + /// + /// The information the provider uses to attest the enclave and generate a symmetric key for the session. The format of this information is specific to the enclave attestation protocol. + /// A Diffie-Hellman algorithm object that encapsulates a client-side key pair. + /// The endpoint of an attestation service for attesting the enclave. + /// The name of the SQL Server instance containing the enclave. + /// The requested enclave session or null if the provider doesn't implement session caching. + /// A counter that the enclave provider is expected to increment each time SqlClient retrieves the session from the cache. The purpose of this field is to prevent replay attacks. + public override void CreateEnclaveSession(byte[] attestationInfo, ECDiffieHellmanCng clientDHKey, string attestationUrl, string servername, out SqlEnclaveSession sqlEnclaveSession, out long counter) + { + sqlEnclaveSession = null; + counter = 0; + try + { + AttestationInfoCacheItem attestationInfoCacheItem = AttestationInfoCache.Remove(Thread.CurrentThread.ManagedThreadId.ToString()) as AttestationInfoCacheItem; + sqlEnclaveSession = GetEnclaveSessionFromCache(servername, attestationUrl, out counter); + if (sqlEnclaveSession == null) + { + if (attestationInfoCacheItem != null) + { + // Deserialize the payload + AttestationInfo info = new AttestationInfo(attestationInfo); + + // Verify enclave policy matches expected policy + VerifyEnclavePolicy(info.EnclaveReportPackage); + + // Perform Attestation per VSM protocol + VerifyAttestationInfo(attestationUrl, info.HealthReport, info.EnclaveReportPackage); + + // Set up shared secret and validate signature + byte[] sharedSecret = GetSharedSecret(info.Identity, info.EnclaveDHInfo, clientDHKey); + + // add session to cache + sqlEnclaveSession = AddEnclaveSessionToCache(attestationUrl, servername, sharedSecret, info.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 name of the SQL Server instance containing the enclave. + /// The endpoint of an attestation service, SqlClient contacts to attest the enclave. + /// The session to be invalidated. + public override void InvalidateEnclaveSession(string serverName, string enclaveAttestationUrl, SqlEnclaveSession enclaveSessionToInvalidate) + { + InvalidateEnclaveSessionHelper(serverName, enclaveAttestationUrl, enclaveSessionToInvalidate); + } + + #endregion + + #region Private helpers + + /// + /// Performs Attestation per the protocol used by Virtual Secure Modules. + /// + /// Url of the attestation service + /// The health report about the SQL Server host + /// The enclave report about the SQL Server host's enclave + private void VerifyAttestationInfo(string attestationUrl, HealthReport healthReport, EnclaveReportPackage enclaveReportPackage) + { + bool shouldRetryValidation; + bool shouldForceUpdateSigningKeys = false; + do + { + shouldRetryValidation = false; + + // Get HGS Root signing certs from HGS + X509Certificate2Collection signingCerts = GetSigningCertificate(attestationUrl, shouldForceUpdateSigningKeys); + + // Verify SQL Health report root chain of trust is the HGS root signing cert + X509ChainStatusFlags chainStatus = VerifyHealthReportAgainstRootCertificate(signingCerts, healthReport.Certificate); + if (chainStatus != X509ChainStatusFlags.NoError) + { + // In cases if we fail to validate the health report, it might be possible that we are using old signing keys + // let's re-download the signing keys again and re-validate the health report + if (!shouldForceUpdateSigningKeys) + { + shouldForceUpdateSigningKeys = true; + shouldRetryValidation = true; + } + else + { + throw new AlwaysEncryptedAttestationException(String.Format(Strings.VerifyHealthCertificateChainFormat, attestationUrl, chainStatus)); + } + } + } while (shouldRetryValidation); + + // Verify enclave report is signed by IDK_S from health report + VerifyEnclaveReportSignature(enclaveReportPackage, healthReport.Certificate); + } + + /// + /// Makes a web request to the provided url and returns the response as a byte[] + /// + /// The url to make the request to + /// The response as a byte[] + protected abstract byte[] MakeRequest(string url); + + /// + /// Gets the root signing certificate for the provided attestation service. + /// If the certificate does not exist in the cache, this will make a call to the + /// attestation service's "/signingCertificates" endpoint. This endpoint can + /// return multiple certificates if the attestation service consists + /// of multiple nodes. + /// + /// Url of attestation service + /// Re-download the signing certificate irrespective of caching + /// The root signing certificate(s) for the attestation service + private X509Certificate2Collection GetSigningCertificate(string attestationUrl, bool forceUpdate) + { + attestationUrl = GetAttestationUrl(attestationUrl); + X509Certificate2Collection signingCertificates = (X509Certificate2Collection)rootSigningCertificateCache[attestationUrl]; + if (forceUpdate || signingCertificates == null || AnyCertificatesExpired(signingCertificates)) + { + byte[] data = MakeRequest(attestationUrl); + var certificateCollection = new X509Certificate2Collection(); + + try + { + certificateCollection.Import(data); + } + catch (CryptographicException exception) + { + throw new AlwaysEncryptedAttestationException(String.Format(Strings.GetAttestationSigningCertificateFailedInvalidCertificate, attestationUrl), exception); + } + + rootSigningCertificateCache.Add(attestationUrl, certificateCollection, DateTime.Now.AddDays(1)); + } + + return (X509Certificate2Collection)rootSigningCertificateCache[attestationUrl]; + } + + /// + /// Return the endpoint for given attestation url + /// + /// The url to alter for corresponding provider + /// altered url + protected abstract string GetAttestationUrl(string attestationUrl); + + /// + /// Checks if any certificates in the collection are expired + /// + /// A collection of certificates + /// true if any certificates or expired, false otherwise + private bool AnyCertificatesExpired(X509Certificate2Collection certificates) + { + return certificates.OfType().Any(c => c.NotAfter < DateTime.Now); + } + + /// + /// Verifies that a chain of trust can be built from the health report provided + /// by SQL Server and the attestation service's root signing certificate(s). + /// + /// The root signing certificate(s) of the attestation service + /// The health report about the SQL Server host in the form of an X509Certificate2 + /// An X509ChainStatusFlags indicating why the chain failed to build + private X509ChainStatusFlags VerifyHealthReportAgainstRootCertificate(X509Certificate2Collection signingCerts, X509Certificate2 healthReportCert) + { + var chain = new X509Chain(); + + foreach (var cert in signingCerts) + { + chain.ChainPolicy.ExtraStore.Add(cert); + } + + chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; + + if (!chain.Build(healthReportCert)) + { + bool untrustedRoot = false; + + // iterate over the chain status to check why the build failed + foreach (X509ChainStatus status in chain.ChainStatus) + { + if (status.Status == X509ChainStatusFlags.UntrustedRoot) + { + untrustedRoot = true; + } + else + { + return status.Status; + } + } + + // if the chain failed with untrusted root, this could be because the client doesn't have the root cert + // installed. If the chain's untrusted root cert has the same thumbprint as the signing cert, then we + // do trust it. + if (untrustedRoot) + { + // iterate through the certificate chain, starting at the root since it's likely the + // signing certificate is the root + for (int i = 0; i < chain.ChainElements.Count; i++) + { + X509ChainElement element = chain.ChainElements[chain.ChainElements.Count - 1 - i]; + + foreach (X509Certificate2 cert in signingCerts) + { + if (element.Certificate.Thumbprint == cert.Thumbprint) + { + return X509ChainStatusFlags.NoError; + } + } + } + + // in the case where we didn't find matching thumbprint + return X509ChainStatusFlags.UntrustedRoot; + } + } + + return X509ChainStatusFlags.NoError; + } + + /// + /// Verifies the enclave report signature using the health report. + /// + /// The enclave report about the SQL Server host's enclave + /// The health report about the SQL Server host in the form of an X509Certificate2 + private void VerifyEnclaveReportSignature(EnclaveReportPackage enclaveReportPackage, X509Certificate2 healthReportCert) + { + // Check if report is formatted correctly + UInt32 calculatedSize = Convert.ToUInt32(enclaveReportPackage.PackageHeader.GetSizeInPayload()) + enclaveReportPackage.PackageHeader.SignedStatementSize + enclaveReportPackage.PackageHeader.SignatureSize; + + if (calculatedSize != enclaveReportPackage.PackageHeader.PackageSize) + { + throw new ArgumentException(Strings.VerifyEnclaveReportFormatFailed); + } + + // IDK_S is contained in healthReport cert public key + RSA rsacsp = healthReportCert.GetRSAPublicKey(); + RSAParameters rsaparams = rsacsp.ExportParameters(includePrivateParameters: false); + RSACng rsacng = new RSACng(); + rsacng.ImportParameters(rsaparams); + + if (!rsacng.VerifyData(enclaveReportPackage.ReportAsBytes, enclaveReportPackage.SignatureBlob, HashAlgorithmName.SHA256, RSASignaturePadding.Pss)) + { + throw new ArgumentException(Strings.VerifyEnclaveReportFailed); + } + } + + /// + /// Verifies the enclave policy matches expected policy. + /// + /// The enclave report about the SQL Server host's enclave + private void VerifyEnclavePolicy(EnclaveReportPackage enclaveReportPackage) + { + EnclaveIdentity identity = enclaveReportPackage.Report.Identity; + + VerifyEnclavePolicyProperty("OwnerId", identity.OwnerId, ExpectedPolicy.OwnerId); + VerifyEnclavePolicyProperty("AuthorId", identity.AuthorId, ExpectedPolicy.AuthorId); + VerifyEnclavePolicyProperty("FamilyId", identity.FamilyId, ExpectedPolicy.FamilyId); + VerifyEnclavePolicyProperty("ImageId", identity.ImageId, ExpectedPolicy.ImageId); + VerifyEnclavePolicyProperty("EnclaveSvn", identity.EnclaveSvn, ExpectedPolicy.EnclaveSvn); + VerifyEnclavePolicyProperty("SecureKernelSvn", identity.SecureKernelSvn, ExpectedPolicy.SecureKernelSvn); + VerifyEnclavePolicyProperty("PlatformSvn", identity.PlatformSvn, ExpectedPolicy.PlatformSvn); + + + // This is a check that the enclave is running without debug support or not. + // + if (identity.Flags != ExpectedPolicy.Flags) + { + throw new InvalidOperationException(Strings.VerifyEnclaveDebuggable); + } + } + + /// + /// Verifies a byte[] enclave policy property + /// + /// The enclave property name + /// The actual enclave property from the enclave report + /// The expected enclave property + private void VerifyEnclavePolicyProperty(string property, byte[] actual, byte[] expected) + { + if (!actual.SequenceEqual(expected)) + { + string exceptionMessage = String.Format(Strings.VerifyEnclavePolicyFailedFormat, property, BitConverter.ToString(actual), BitConverter.ToString(expected)); + throw new ArgumentException(exceptionMessage); + } + } + + /// + /// Verifies a uint enclave policy property + /// + /// The enclave property name + /// The actual enclave property from the enclave report + /// The expected enclave property + private void VerifyEnclavePolicyProperty(string property, uint actual, uint expected) + { + if (actual < expected) + { + string exceptionMessage = String.Format(Strings.VerifyEnclavePolicyFailedFormat, property, actual, expected); + throw new ArgumentException(exceptionMessage); + } + } + + /// + /// Derives the shared secret between the client and enclave. + /// + /// The enclave's RSA public key + /// The enclave's DiffieHellman key and signature info + /// The client's DiffieHellman key info + /// A byte buffer containing the shared secret + private byte[] GetSharedSecret(EnclavePublicKey enclavePublicKey, EnclaveDiffieHellmanInfo enclaveDHInfo, ECDiffieHellmanCng clientDHKey) + { + // Perform signature verification. The enclave's DiffieHellman public key was signed by the enclave's RSA public key. + CngKey cngkey = CngKey.Import(enclavePublicKey.PublicKey, CngKeyBlobFormat.GenericPublicBlob); + RSACng rsacng = new RSACng(cngkey); + if (!rsacng.VerifyData(enclaveDHInfo.PublicKey, enclaveDHInfo.PublicKeySignature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)) + { + throw new ArgumentException(Strings.GetSharedSecretFailed); + } + + CngKey key = CngKey.Import(enclaveDHInfo.PublicKey, CngKeyBlobFormat.GenericPublicBlob); + return clientDHKey.DeriveKeyMaterial(key); + } + + #endregion + } +} diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs index 5bf47a3c97..3e8e6b9705 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs @@ -19,7 +19,7 @@ namespace System { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Strings { @@ -1824,6 +1824,15 @@ internal static string ADP_VersionDoesNotSupportDataType { } } + /// + /// Looks up a localized string similar to The validation of an attestation token failed. The token signature does not match the signature omputed using a public key retrieved from the attestation public key endpoint at '{0}'. Verify the DNS apping for the endpoint. If correct, contact Customer Support Services.. + /// + internal static string AttestationTokenSignatureValidationFailed { + get { + return ResourceManager.GetString("AttestationTokenSignatureValidationFailed", resourceCulture); + } + } + /// /// Looks up a localized string similar to .database.chinacloudapi.cn. /// @@ -6126,6 +6135,33 @@ internal static string DbTable_UpdateCommand { } } + /// + /// Looks up a localized string similar to Internal error occurred when retrying the download of the HGS root certificate after the initial request failed. Contact Customer Support Services.. + /// + internal static string EnclaveRetrySleepInSecondsValueException { + get { + return ResourceManager.GetString("EnclaveRetrySleepInSecondsValueException", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Internal error. Unable to invalidate the requested enclave session, because it does not exist in the cache. Contact Customer Support Services.. + /// + internal static string EnclaveSessionInvalidationFailed { + get { + return ResourceManager.GetString("EnclaveSessionInvalidationFailed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The validation of an attestation token failed. The token received from SQL Server is expired. Contact Customer Support Services.. + /// + internal static string ExpiredAttestationToken { + get { + return ResourceManager.GetString("ExpiredAttestationToken", resourceCulture); + } + } + /// /// Looks up a localized string similar to Syntax error in aggregate argument: Expecting a single column argument with possible 'Child' qualifier.. /// @@ -6567,6 +6603,33 @@ internal static string ExtendedPropertiesDescr { } } + /// + /// Looks up a localized string similar to Failed to create enclave session as attestation server is busy.. + /// + internal static string FailToCreateEnclaveSession { + get { + return ResourceManager.GetString("FailToCreateEnclaveSession", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The validation of an attestation information failed. The attestation information has an invalid format. Contact Customer Support Services. Error details: '{0}'.. + /// + internal static string FailToParseAttestationInfo { + get { + return ResourceManager.GetString("FailToParseAttestationInfo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The validation of an attestation token failed. The token has an invalid format. Contact Customer Support Services. Error details: '{0}'.. + /// + internal static string FailToParseAttestationToken { + get { + return ResourceManager.GetString("FailToParseAttestationToken", resourceCulture); + } + } + /// /// Looks up a localized string similar to For accept and reject changes, indicates what kind of cascading should take place across this relation.. /// @@ -6621,6 +6684,42 @@ internal static string ForeignKeyRelatedTableDescr { } } + /// + /// Looks up a localized string similar to The attestation service returned an expired HGS root certificate for attestation URL '{0}'. Check the HGS root certificate configured for your HGS instance.. + /// + internal static string GetAttestationSigningCertificateFailedInvalidCertificate { + get { + return ResourceManager.GetString("GetAttestationSigningCertificateFailedInvalidCertificate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The obtained HGS root certificate for attestation URL '{0}' has an invalid format. Verify the attestation URL is correct and the HGS server is online and fully initialized. For more information contact Customer Support Services. Error details: '{1}'.. + /// + internal static string GetAttestationSigningCertificateRequestFailedFormat { + get { + return ResourceManager.GetString("GetAttestationSigningCertificateRequestFailedFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The validation of an attestation token failed. Cannot retrieve a public key from the attestation public key endpoint, or the retrieved key has an invalid format. Error details: '{0}'.. + /// + internal static string GetAttestationTokenSigningKeysFailed { + get { + return ResourceManager.GetString("GetAttestationTokenSigningKeysFailed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Signature verification of the enclave's Diffie-Hellman key failed. Contact Customer Support Services.. + /// + internal static string GetSharedSecretFailed { + get { + return ResourceManager.GetString("GetSharedSecretFailed", resourceCulture); + } + } + /// /// Looks up a localized string similar to Global Transactions are not enabled for this Azure SQL Database. Please contact Azure SQL Database support for assistance.. /// @@ -6648,6 +6747,42 @@ internal static string IEnumerableOfSqlDataRecordHasNoRows { } } + /// + /// Looks up a localized string similar to The validation of an attestation token failed due to an error while decoding the enclave public key obtained from SQL Server. Contact Customer Support Services.. + /// + internal static string InvalidArgumentToBase64UrlDecoder { + get { + return ResourceManager.GetString("InvalidArgumentToBase64UrlDecoder", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The validation of an attestation token failed due to an error while computing a hash of the enclave public key obtained from SQL Server. Contact Customer Support Services.. + /// + internal static string InvalidArgumentToSHA256 { + get { + return ResourceManager.GetString("InvalidArgumentToSHA256", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The validation of the attestation token has failed during signature validation. Exception: '{0}'.. + /// + internal static string InvalidAttestationToken { + get { + return ResourceManager.GetString("InvalidAttestationToken", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The validation of an attestation token failed. Claim '{0}' in the token has an invalid value of '{1}'. Verify the attestation policy. If the policy is correct, contact Customer Support Services.. + /// + internal static string InvalidClaimInAttestationToken { + get { + return ResourceManager.GetString("InvalidClaimInAttestationToken", resourceCulture); + } + } + /// /// Looks up a localized string similar to Invalid column ordinals in schema table. ColumnOrdinals, if present, must not have duplicates or gaps.. /// @@ -6909,6 +7044,15 @@ internal static string MetaType_SingleValuedStructNotSupported { } } + /// + /// Looks up a localized string similar to The validation of the attestation token failed. Claim '{0}' is missing in the token. Verify the attestation policy. If the policy is correct, contact Customer Support Services.. + /// + internal static string MissingClaimInAttestationToken { + get { + return ResourceManager.GetString("MissingClaimInAttestationToken", resourceCulture); + } + } + /// /// Looks up a localized string similar to Simple type '{0}' has already be declared with different '{1}'.. /// @@ -9224,12 +9368,11 @@ internal static string SQL_IntegratedWithUserIDAndPassword { return ResourceManager.GetString("SQL_IntegratedWithUserIDAndPassword", resourceCulture); } } - + /// /// Looks up a localized string similar to Cannot use 'Authentication=Active Directory Interactive' with 'User ID', 'UID', 'Password' or 'PWD' connection string keywords.. /// - internal static string SQL_InteractiveWithUserIDAndPassword - { + internal static string SQL_InteractiveWithUserIDAndPassword { get { return ResourceManager.GetString("SQL_InteractiveWithUserIDAndPassword", resourceCulture); } @@ -12709,6 +12852,51 @@ internal static string TCE_VeryLargeCiphertext { } } + /// + /// Looks up a localized string similar to Failed to check if the enclave is running in the production mode. Contact Customer Support Services.. + /// + internal static string VerifyEnclaveDebuggable { + get { + return ResourceManager.GetString("VerifyEnclaveDebuggable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Could not verify enclave policy due to a difference between the expected and actual values of the policy on property '{0}'. Actual: '{1}', Expected: '{2}'.. + /// + internal static string VerifyEnclavePolicyFailedFormat { + get { + return ResourceManager.GetString("VerifyEnclavePolicyFailedFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Signature verification of the enclave report failed. The report signature does not match the signature computed using the HGS root certificate. Verify the DNS mapping for the endpoint. If correct, contact Customer Support Services.. + /// + internal static string VerifyEnclaveReportFailed { + get { + return ResourceManager.GetString("VerifyEnclaveReportFailed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The enclave report received from SQL Server is not in the correct format. Contact Customer Support Services.. + /// + internal static string VerifyEnclaveReportFormatFailed { + get { + return ResourceManager.GetString("VerifyEnclaveReportFormatFailed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to build a chain of trust between the enclave host's health report and the HGS root certificate for attestation URL '{0}' with status: '{1}'. Verify the attestation URL matches the URL configured on the SQL Server machine. If both the client and SQL Server use the same attestation service, contact Customer Support Services.. + /// + internal static string VerifyHealthCertificateChainFormat { + get { + return ResourceManager.GetString("VerifyHealthCertificateChainFormat", resourceCulture); + } + } + /// /// Looks up a localized string similar to The value of attribute '{0}' should be '{1}' or '{2}'.. /// diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx index 88e750c707..4f8c7b7fce 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx @@ -4443,4 +4443,67 @@ Execution Timeout Expired. The timeout period elapsed prior to completion of the operation or the server is not responding. + + The validation of an attestation token failed. The token signature does not match the signature omputed using a public key retrieved from the attestation public key endpoint at '{0}'. Verify the DNS apping for the endpoint. If correct, contact Customer Support Services. + + + Internal error occurred when retrying the download of the HGS root certificate after the initial request failed. Contact Customer Support Services. + + + Internal error. Unable to invalidate the requested enclave session, because it does not exist in the cache. Contact Customer Support Services. + + + The validation of an attestation token failed. The token received from SQL Server is expired. Contact Customer Support Services. + + + Failed to create enclave session as attestation server is busy. + + + The validation of an attestation information failed. The attestation information has an invalid format. Contact Customer Support Services. Error details: '{0}'. + + + The validation of an attestation token failed. The token has an invalid format. Contact Customer Support Services. Error details: '{0}'. + + + The attestation service returned an expired HGS root certificate for attestation URL '{0}'. Check the HGS root certificate configured for your HGS instance. + + + The obtained HGS root certificate for attestation URL '{0}' has an invalid format. Verify the attestation URL is correct and the HGS server is online and fully initialized. For more information contact Customer Support Services. Error details: '{1}'. + + + The validation of an attestation token failed. Cannot retrieve a public key from the attestation public key endpoint, or the retrieved key has an invalid format. Error details: '{0}'. + + + Signature verification of the enclave's Diffie-Hellman key failed. Contact Customer Support Services. + + + The validation of an attestation token failed due to an error while decoding the enclave public key obtained from SQL Server. Contact Customer Support Services. + + + The validation of an attestation token failed due to an error while computing a hash of the enclave public key obtained from SQL Server. Contact Customer Support Services. + + + The validation of the attestation token has failed during signature validation. Exception: '{0}'. + + + The validation of an attestation token failed. Claim '{0}' in the token has an invalid value of '{1}'. Verify the attestation policy. If the policy is correct, contact Customer Support Services. + + + The validation of the attestation token failed. Claim '{0}' is missing in the token. Verify the attestation policy. If the policy is correct, contact Customer Support Services. + + + Failed to check if the enclave is running in the production mode. Contact Customer Support Services. + + + Could not verify enclave policy due to a difference between the expected and actual values of the policy on property '{0}'. Actual: '{1}', Expected: '{2}'. + + + Signature verification of the enclave report failed. The report signature does not match the signature computed using the HGS root certificate. Verify the DNS mapping for the endpoint. If correct, contact Customer Support Services. + + + The enclave report received from SQL Server is not in the correct format. Contact Customer Support Services. + + + Failed to build a chain of trust between the enclave host's health report and the HGS root certificate for attestation URL '{0}' with status: '{1}'. Verify the attestation URL matches the URL configured on the SQL Server machine. If both the client and SQL Server use the same attestation service, contact Customer Support Services. + \ No newline at end of file From 478d3d8e57544065de3b247b3bea5a55de1f938a Mon Sep 17 00:00:00 2001 From: Karina Zhou Date: Mon, 7 Oct 2019 18:06:00 -0700 Subject: [PATCH 03/63] resolve conflict --- .../netfx/ref/Microsoft.Data.SqlClient.cs | 4 ++++ .../netfx/ref/Microsoft.Data.SqlClient.csproj | 2 +- .../netfx/src/Microsoft.Data.SqlClient.csproj | 8 +++++--- .../Microsoft/Data/SqlClient/SqlConnection.cs | 2 -- .../Data/SqlClient/SqlConnectionString.cs | 1 - .../SqlClient/SqlConnectionStringBuilder.cs | 13 ++++-------- .../src/Microsoft/Data/SqlClient/TdsEnums.cs | 20 ++++++++++++++++++- .../netfx/src/Resources/Strings.Designer.cs | 1 - .../netfx/src/Resources/Strings.resx | 4 +--- 9 files changed, 34 insertions(+), 21 deletions(-) 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 f98ecc2e86..fa2cee38b0 100644 --- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs @@ -786,12 +786,16 @@ public enum SqlConnectionColumnEncryptionSetting /// Enabled = 1, } + + // To add include file for docs public enum SqlConnectionAttestationProtocol { None = 0, AAS = 1, HGS = 2 } + + /// [System.ComponentModel.DefaultPropertyAttribute("DataSource")] public sealed partial class SqlConnectionStringBuilder : System.Data.Common.DbConnectionStringBuilder { diff --git a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj index 1425d80c32..164a5fe4d2 100644 --- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj @@ -4,7 +4,7 @@ net46 $(ObjFolder)$(Configuration)\$(AssemblyName)\ref\ $(BinFolder)$(Configuration)\$(AssemblyName)\ref\ - $(OutputPath)\Microsoft.Data.SqlClient.xml + C:\NetworkShareKZ\VSOGit\DotnetSqlClient\src\Microsoft.Data.SqlClient.xml Framework $(BaseProduct) Debug;Release;net46-Release;net46-Debug 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 a020c5adb5..009e212716 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -57,7 +57,9 @@ - + + ..\..\..\..\bin\Windows_NT\Debug.AnyCPU\Microsoft.Data.SqlClient\netfx\Microsoft.Data.SqlClient.xml + @@ -322,10 +324,10 @@ - + True True - Strings.resx + $(ResxFileName).resx diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs index fa4ddf9098..cd3dcd6599 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -499,11 +499,9 @@ internal string EnclaveAttestationUrl } } - // kz /// /// Get attestation protocol /// - /// internal SqlConnectionAttestationProtocol AttestationProtocol { get diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionString.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionString.cs index 7779c61cf9..fa9800261e 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionString.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionString.cs @@ -999,7 +999,6 @@ internal SqlConnectionColumnEncryptionSetting ConvertValueToColumnEncryptionSett } } - // kz internal SqlConnectionAttestationProtocol ConvertValueToAttestationProtocol() { object value = base.Parsetable[KEY.AttestationProtocol]; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs index 3b52373737..8dbc11aa27 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs @@ -645,10 +645,7 @@ public string EnclaveAttestationUrl } } -<<<<<<< HEAD - /// -======= - // kz + // to add docs include file [DisplayName(DbConnectionStringKeywords.AttestationProtocol)] [ResCategoryAttribute(StringsHelper.ResourceNames.DataCategory_Security)] [ResDescriptionAttribute(StringsHelper.ResourceNames.TCE_DbConnectionString_AttestationProtocol)] @@ -668,7 +665,7 @@ public SqlConnectionAttestationProtocol AttestationProtocol } } ->>>>>>> fa3575d54cd96b37ca17ed34270bcad182121556 + /// [DisplayName(DbConnectionStringKeywords.TrustServerCertificate)] [ResCategoryAttribute(StringsHelper.ResourceNames.DataCategory_Security)] [ResDescriptionAttribute(StringsHelper.ResourceNames.DbConnectionString_TrustServerCertificate)] @@ -1564,16 +1561,14 @@ private void SetColumnEncryptionSettingValue(SqlConnectionColumnEncryptionSettin base[DbConnectionStringKeywords.ColumnEncryptionSetting] = DbConnectionStringBuilderUtil.ColumnEncryptionSettingToString(value); } -<<<<<<< HEAD - /// -======= private void SetAttestationProtocolValue(SqlConnectionAttestationProtocol value) { Debug.Assert(DbConnectionStringBuilderUtil.IsValidAttestationProtocol(value), "Invalid value for SqlConnectionAttestationProtocol"); base[DbConnectionStringKeywords.AttestationProtocol] = DbConnectionStringBuilderUtil.AttestationProtocolToString(value); } ->>>>>>> fa3575d54cd96b37ca17ed34270bcad182121556 + + /// public override bool ShouldSerialize(string keyword) { ADP.CheckArgumentNull(keyword, "keyword"); 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 644977a4b6..2096dc5737 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 @@ -1024,7 +1024,6 @@ public enum SqlConnectionColumnEncryptionSetting /// public enum SqlCommandColumnEncryptionSetting { - /// UseConnectionSetting = 0, @@ -1038,6 +1037,25 @@ public enum SqlCommandColumnEncryptionSetting Disabled, } + // To add include file for docs + public enum SqlConnectionAttestationProtocol + { + /// + /// If the attestation protocol is not specified. Use this as default value. + /// + None = 0, + + /// + /// Attestation portocol for Azure Attestation Service + /// + AAS = 1, + + /// + /// Attestation protocol for Host Guardian Service + /// + HGS = 2 + } + /// public enum SqlAuthenticationMethod { diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs index 017cecf0b2..fc0d6484d0 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs @@ -11793,7 +11793,6 @@ internal static string TCE_DbConnectionString_EnclaveAttestationUrl { /// /// Looks up a localized string similar to Specifies an attestation protocol for its corresponding enclave attestation service. /// - /// internal static string TCE_DbConnectionString_AttestationProtocol { get { return ResourceManager.GetString("TCE_DbConnectionString_AttestationProtocol", resourceCulture); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx index eb40a53909..b1cef25d4a 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx @@ -4443,7 +4443,6 @@ Execution Timeout Expired. The timeout period elapsed prior to completion of the operation or the server is not responding. -<<<<<<< HEAD The validation of an attestation token failed. The token signature does not match the signature omputed using a public key retrieved from the attestation public key endpoint at '{0}'. Verify the DNS apping for the endpoint. If correct, contact Customer Support Services. @@ -4506,7 +4505,7 @@ Failed to build a chain of trust between the enclave host's health report and the HGS root certificate for attestation URL '{0}' with status: '{1}'. Verify the attestation URL matches the URL configured on the SQL Server machine. If both the client and SQL Server use the same attestation service, contact Customer Support Services. -======= + Specifies an attestation protocol for its corresponding enclave attestation service. @@ -4515,6 +4514,5 @@ Failed to initialize connection. The attestation protocol does not support the enclave type '{0}'. ->>>>>>> fa3575d54cd96b37ca17ed34270bcad182121556 \ No newline at end of file From 6bd96a817d0b5546d5a928881d309efc696d520e Mon Sep 17 00:00:00 2001 From: Karina Zhou Date: Mon, 7 Oct 2019 18:12:21 -0700 Subject: [PATCH 04/63] Remove build info --- .../netfx/ref/Microsoft.Data.SqlClient.csproj | 2 +- .../netfx/src/Microsoft.Data.SqlClient.csproj | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj index 164a5fe4d2..9b3695035a 100644 --- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj @@ -4,7 +4,7 @@ net46 $(ObjFolder)$(Configuration)\$(AssemblyName)\ref\ $(BinFolder)$(Configuration)\$(AssemblyName)\ref\ - C:\NetworkShareKZ\VSOGit\DotnetSqlClient\src\Microsoft.Data.SqlClient.xml + $(OutputPath)\src\Microsoft.Data.SqlClient.xml Framework $(BaseProduct) Debug;Release;net46-Release;net46-Debug 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 009e212716..9933d3ac3a 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -57,9 +57,7 @@ - - ..\..\..\..\bin\Windows_NT\Debug.AnyCPU\Microsoft.Data.SqlClient\netfx\Microsoft.Data.SqlClient.xml - + From daab578c46141fb5b5fabd06af419c73c25f3f68 Mon Sep 17 00:00:00 2001 From: Javad Rahnama Date: Wed, 9 Oct 2019 13:08:06 -0700 Subject: [PATCH 05/63] Removing References to SqlColumnEncryptionManager --- .../netfx/ref/Microsoft.Data.SqlClient.cs | 14 ++++ .../netfx/src/Microsoft.Data.SqlClient.csproj | 21 ++---- ...tionEnclaveProviderConfigurationManager.cs | 72 ------------------- .../Microsoft/Data/SqlClient/SqlConnection.cs | 16 ----- .../SqlClient/SqlConnectionStringBuilder.cs | 3 + .../src/Microsoft/Data/SqlClient/TdsEnums.cs | 3 + 6 files changed, 25 insertions(+), 104 deletions(-) delete mode 100644 src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProviderConfigurationManager.cs 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 fa2cee38b0..564102a97b 100644 --- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs @@ -788,10 +788,24 @@ public enum SqlConnectionColumnEncryptionSetting } // To add include file for docs + /// + /// + /// public enum SqlConnectionAttestationProtocol { + /// + /// + /// None = 0, + + /// + /// + /// AAS = 1, + + /// + /// + /// HGS = 2 } 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 9933d3ac3a..63dd5e0abc 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -157,18 +157,11 @@ - - - Component - - - Component - + + - - Component - + @@ -177,9 +170,7 @@ - - Component - + @@ -261,9 +252,7 @@ - - Component - + diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProviderConfigurationManager.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProviderConfigurationManager.cs deleted file mode 100644 index b5eacebfca..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProviderConfigurationManager.cs +++ /dev/null @@ -1,72 +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.Configuration; - -namespace Microsoft.Data.SqlClient -{ - /// - /// The configuration section definition for reading app.config. - /// - internal class SqlColumnEncryptionEnclaveProviderConfigurationSection : ConfigurationSection - { - /// - /// User-defined SqlColumnEncryptionEnclaveProviders. - /// - [ConfigurationProperty("providers")] - public ProviderSettingsCollection Providers => (ProviderSettingsCollection)base["providers"]; - } - - internal class SqlColumnEncryptionEnclaveProviderConfigurationManager - { - private readonly Dictionary _enclaveProviders = new Dictionary(); - - /// - /// Constructor. - /// - public SqlColumnEncryptionEnclaveProviderConfigurationManager(SqlColumnEncryptionEnclaveProviderConfigurationSection configSection) - { - if (configSection != null && configSection.Providers != null && configSection.Providers.Count > 0) - { - foreach (ProviderSettings providerSettings in configSection.Providers) - { - var providerName = providerSettings.Name.ToLowerInvariant(); - SqlColumnEncryptionEnclaveProvider provider; - - try - { - var providerType = Type.GetType(providerSettings.Type, true); - provider = (SqlColumnEncryptionEnclaveProvider)Activator.CreateInstance(providerType); - } - catch (Exception e) - { - throw SQL.CannotCreateSqlColumnEncryptionEnclaveProvider(providerName, providerSettings.Type, e); - } - - _enclaveProviders[providerName] = provider; - } - } - } - - /// - /// Lookup SqlColumnEncryptionEnclaveProvider for a given SqlColumnEncryptionEnclaveProviderName - /// - /// - /// SqlColumnEncryptionEnclaveProvider for a give sqlColumnEncryptionEnclaveProviderName if found, else returns null - public SqlColumnEncryptionEnclaveProvider GetSqlColumnEncryptionEnclaveProvider(string SqlColumnEncryptionEnclaveProviderName) - { - if (string.IsNullOrEmpty(SqlColumnEncryptionEnclaveProviderName)) - throw SQL.SqlColumnEncryptionEnclaveProviderNameCannotBeEmpty(); - SqlColumnEncryptionEnclaveProviderName = SqlColumnEncryptionEnclaveProviderName.ToLowerInvariant(); - - SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = null; - _enclaveProviders.TryGetValue(SqlColumnEncryptionEnclaveProviderName, out sqlColumnEncryptionEnclaveProvider); - - return sqlColumnEncryptionEnclaveProvider; - } - - } -} diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs index cd3dcd6599..30e3feb101 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -49,23 +49,7 @@ internal bool ForceNewConnection internal bool _supressStateChangeForReconnection = false; // Do not use for anything else ! Value will be overwritten by CR process - static SqlConnection() - { - SqlColumnEncryptionEnclaveProviderConfigurationSection sqlColumnEncryptionEnclaveProviderConfigurationSection = null; - try - { - sqlColumnEncryptionEnclaveProviderConfigurationSection = (SqlColumnEncryptionEnclaveProviderConfigurationSection)ConfigurationManager.GetSection("SqlColumnEncryptionEnclaveProviders"); - } - catch (ConfigurationErrorsException e) - { - throw SQL.CannotGetSqlColumnEncryptionEnclaveProviderConfig(e); - } - - sqlColumnEncryptionEnclaveProviderConfigurationManager = new SqlColumnEncryptionEnclaveProviderConfigurationManager(sqlColumnEncryptionEnclaveProviderConfigurationSection); - } - static private readonly object EventInfoMessage = new object(); - static internal readonly SqlColumnEncryptionEnclaveProviderConfigurationManager sqlColumnEncryptionEnclaveProviderConfigurationManager; // System column encryption key store providers are added by default static private readonly Dictionary _SystemColumnEncryptionKeyStoreProviders diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs index 8dbc11aa27..cfa59f203e 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs @@ -646,6 +646,9 @@ public string EnclaveAttestationUrl } // to add docs include file + /// + /// + /// [DisplayName(DbConnectionStringKeywords.AttestationProtocol)] [ResCategoryAttribute(StringsHelper.ResourceNames.DataCategory_Security)] [ResDescriptionAttribute(StringsHelper.ResourceNames.TCE_DbConnectionString_AttestationProtocol)] 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 2096dc5737..8907702a1b 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 @@ -1038,6 +1038,9 @@ public enum SqlCommandColumnEncryptionSetting } // To add include file for docs + /// + /// + /// public enum SqlConnectionAttestationProtocol { /// From f5e5f6123fe3c2328fb33f54bd9cf23353014d39 Mon Sep 17 00:00:00 2001 From: Karina Zhou Date: Wed, 9 Oct 2019 13:38:57 -0700 Subject: [PATCH 06/63] Add attestation protocol to EnclaveDelegate --- .../netfx/ref/Microsoft.Data.SqlClient.cs | 17 ++++- .../Data/Common/DbConnectionStringCommon.cs | 4 +- .../Data/SqlClient/EnclaveDelegate.cs | 76 ++++++++++++++----- .../Microsoft/Data/SqlClient/SqlCommand.cs | 24 ++++-- .../Data/SqlClient/SqlConnectionString.cs | 2 +- .../SqlClient/SqlConnectionStringBuilder.cs | 4 +- .../src/Microsoft/Data/SqlClient/SqlUtil.cs | 9 ++- .../src/Microsoft/Data/SqlClient/TdsEnums.cs | 8 +- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 2 +- .../netfx/src/Resources/Strings.Designer.cs | 9 +++ .../netfx/src/Resources/Strings.resx | 5 +- 11 files changed, 120 insertions(+), 40 deletions(-) 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 fa2cee38b0..ff18606b42 100644 --- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs @@ -787,12 +787,23 @@ public enum SqlConnectionColumnEncryptionSetting Enabled = 1, } - // To add include file for docs + /// + /// To add include file for docs + /// public enum SqlConnectionAttestationProtocol { - None = 0, + /// + /// To add include file for docs + /// + NotSpecified = 0, + /// + /// To add include file for docs + /// AAS = 1, - HGS = 2 + /// + /// To add include file for docs + /// + HGS = 3 } /// diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionStringCommon.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionStringCommon.cs index c3563b720a..cb33b13cbd 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionStringCommon.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionStringCommon.cs @@ -854,7 +854,7 @@ internal static bool TryConvertToAttestationProtocol(string value, out SqlConnec internal static bool IsValidAttestationProtocol(SqlConnectionAttestationProtocol value) { Debug.Assert(Enum.GetNames(typeof(SqlConnectionAttestationProtocol)).Length == 3, "SqlConnectionAttestationProtocol enum has changed, update needed"); - return value == SqlConnectionAttestationProtocol.None + return value == SqlConnectionAttestationProtocol.NotSpecified || value == SqlConnectionAttestationProtocol.HGS || value == SqlConnectionAttestationProtocol.AAS; @@ -1011,7 +1011,7 @@ internal static class DbConnectionStringDefaults internal static readonly SqlAuthenticationMethod Authentication = SqlAuthenticationMethod.NotSpecified; internal static readonly SqlConnectionColumnEncryptionSetting ColumnEncryptionSetting = SqlConnectionColumnEncryptionSetting.Disabled; internal const string EnclaveAttestationUrl = ""; - internal static readonly SqlConnectionAttestationProtocol AttestationProtocol = SqlConnectionAttestationProtocol.None; + internal static readonly SqlConnectionAttestationProtocol AttestationProtocol = SqlConnectionAttestationProtocol.NotSpecified; internal const string Certificate = ""; internal const PoolBlockingPeriod PoolBlockingPeriod = SqlClient.PoolBlockingPeriod.Auto; } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs index aa6642084f..6e3d7024e8 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs @@ -26,6 +26,8 @@ internal class EnclaveDelegate private static readonly string GetSerializedAttestationParametersName = "GetSerializedAttestationParameters"; private static readonly string ComputeQueryStringHashName = "ComputeQueryStringHash"; + private static Dictionary EnclaveProviders = new Dictionary(); + private readonly Object _lock = new Object(); //singleton instance @@ -36,20 +38,21 @@ private EnclaveDelegate() { } /// /// Generate the byte package that needs to be sent to the enclave /// + /// attestation protocol /// Keys to be sent to enclave /// /// enclave type /// server name /// url for attestation endpoint /// - internal EnclavePackage GenerateEnclavePackage(Dictionary keysTobeSentToEnclave, string queryText, string enclaveType, string serverName, string enclaveAttestationUrl) + internal EnclavePackage GenerateEnclavePackage(SqlConnectionAttestationProtocol attestationProtocol, Dictionary keysTobeSentToEnclave, string queryText, string enclaveType, string serverName, string enclaveAttestationUrl) { SqlEnclaveSession sqlEnclaveSession = null; long counter; try { - GetEnclaveSession(enclaveType, serverName, enclaveAttestationUrl, out sqlEnclaveSession, out counter, throwIfNull: true); + GetEnclaveSession(attestationProtocol, enclaveType, serverName, enclaveAttestationUrl, out sqlEnclaveSession, out counter, throwIfNull: true); } catch (Exception e) { @@ -66,21 +69,21 @@ internal EnclavePackage GenerateEnclavePackage(Dictionary /// Create a new enclave session /// + /// attestation protocol /// enclave type /// servername /// attestation url for attestation service endpoint /// attestation info from SQL Server /// attestation parameters - internal void CreateEnclaveSession(string enclaveType, string serverName, string attestationUrl, + internal void CreateEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, string serverName, string attestationUrl, byte[] attestationInfo, SqlEnclaveAttestationParameters attestationParameters) { lock (_lock) { - SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = GetEnclaveProvider(enclaveType); + SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = GetEnclaveProvider(attestationProtocol, enclaveType); long counter; SqlEnclaveSession sqlEnclaveSession = null; sqlColumnEncryptionEnclaveProvider.GetEnclaveSession(serverName, attestationUrl, out sqlEnclaveSession, out counter); @@ -181,20 +185,54 @@ internal void CreateEnclaveSession(string enclaveType, string serverName, string } } - private SqlColumnEncryptionEnclaveProvider GetEnclaveProvider(string enclaveType) + private SqlColumnEncryptionEnclaveProvider GetEnclaveProvider(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType) { - if (SqlConnection.sqlColumnEncryptionEnclaveProviderConfigurationManager == null) - throw SQL.EnclaveProvidersNotConfiguredForEnclaveBasedQuery(); + SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = null; - var sqlColumnEncryptionEnclaveProvider = - SqlConnection.sqlColumnEncryptionEnclaveProviderConfigurationManager.GetSqlColumnEncryptionEnclaveProvider( - enclaveType); + if (!EnclaveProviders.TryGetValue(attestationProtocol, out sqlColumnEncryptionEnclaveProvider)) + { + switch (attestationProtocol) + { + case SqlConnectionAttestationProtocol.AAS: + AzureAttestationEnclaveProvider azureAttestationEnclaveProvider = new AzureAttestationEnclaveProvider(); + EnclaveProviders[attestationProtocol] = (SqlColumnEncryptionEnclaveProvider)azureAttestationEnclaveProvider; + sqlColumnEncryptionEnclaveProvider = EnclaveProviders[attestationProtocol]; + break; + + case SqlConnectionAttestationProtocol.HGS: + HostGuardianServiceEnclaveProvider hostGuardianServiceEnclaveProvider = new HostGuardianServiceEnclaveProvider(); + EnclaveProviders[attestationProtocol] = (SqlColumnEncryptionEnclaveProvider)hostGuardianServiceEnclaveProvider; + sqlColumnEncryptionEnclaveProvider = EnclaveProviders[attestationProtocol]; + break; + + default: + break; + } + } if (sqlColumnEncryptionEnclaveProvider == null) - throw SQL.EnclaveProviderNotFound(enclaveType); + { + throw SQL.EnclaveProviderNotFound(enclaveType, ConvertAttestationProtocolToString(attestationProtocol)); + } + return sqlColumnEncryptionEnclaveProvider; } + private string ConvertAttestationProtocolToString(SqlConnectionAttestationProtocol attestationProtocol) + { + switch (attestationProtocol) + { + case SqlConnectionAttestationProtocol.AAS: + return "AAS"; + + case SqlConnectionAttestationProtocol.HGS: + return "HGS"; + + default: + return "NotSpecified"; + } + } + /// /// Decrypt the keys that need to be sent to the enclave /// 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 6c38b54be9..e9927977af 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 @@ -2733,7 +2733,7 @@ private bool TriggerInternalEndAndRetryIfNecessary(CommandBehavior behavior, obj if (ShouldUseEnclaveBasedWorkflow && this.enclavePackage != null) { - EnclaveDelegate.Instance.InvalidateEnclaveSession(this._activeConnection.Parser.EnclaveType, this._activeConnection.DataSource, this._activeConnection.EnclaveAttestationUrl, this.enclavePackage.EnclaveSession); + EnclaveDelegate.Instance.InvalidateEnclaveSession(this._activeConnection.AttestationProtocol, this._activeConnection.Parser.EnclaveType, this._activeConnection.DataSource, this._activeConnection.EnclaveAttestationUrl, this.enclavePackage.EnclaveSession); } try @@ -4274,14 +4274,15 @@ private SqlDataReader TryFetchInputParameterEncryptionInfo(int timeout, if (ShouldUseEnclaveBasedWorkflow) { + SqlConnectionAttestationProtocol attestationProtocol = this._activeConnection.AttestationProtocol; string enclaveType = this._activeConnection.Parser.EnclaveType; string dataSource = this._activeConnection.DataSource; string enclaveAttestationUrl = this._activeConnection.EnclaveAttestationUrl; SqlEnclaveSession sqlEnclaveSession = null; - EnclaveDelegate.Instance.GetEnclaveSession(enclaveType, dataSource, enclaveAttestationUrl, out sqlEnclaveSession); + EnclaveDelegate.Instance.GetEnclaveSession(attestationProtocol, enclaveType, dataSource, enclaveAttestationUrl, out sqlEnclaveSession); if (sqlEnclaveSession == null) { - this.enclaveAttestationParameters = EnclaveDelegate.Instance.GetAttestationParameters(enclaveType, dataSource, enclaveAttestationUrl); + this.enclaveAttestationParameters = EnclaveDelegate.Instance.GetAttestationParameters(attestationProtocol, enclaveType, dataSource, enclaveAttestationUrl); serializedAttestatationParameters = EnclaveDelegate.Instance.GetSerializedAttestationParameters(this.enclaveAttestationParameters, enclaveType); } } @@ -4805,11 +4806,12 @@ private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDi byte[] attestationInfo = new byte[attestationInfoLength]; ds.GetBytes((int)DescribeParameterEncryptionResultSet3.AttestationInfo, 0, attestationInfo, 0, attestationInfoLength); + SqlConnectionAttestationProtocol attestationProtocol = this._activeConnection.AttestationProtocol; string enclaveType = this._activeConnection.Parser.EnclaveType; string dataSource = this._activeConnection.DataSource; string enclaveAttestationUrl = this._activeConnection.EnclaveAttestationUrl; - EnclaveDelegate.Instance.CreateEnclaveSession(enclaveType, dataSource, enclaveAttestationUrl, attestationInfo, enclaveAttestationParameters); + EnclaveDelegate.Instance.CreateEnclaveSession(attestationProtocol, enclaveType, dataSource, enclaveAttestationUrl, attestationInfo, enclaveAttestationParameters); enclaveAttestationParameters = null; attestationInfoRead = true; } @@ -4943,7 +4945,8 @@ internal SqlDataReader RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior if (ShouldUseEnclaveBasedWorkflow && this.enclavePackage != null) { - EnclaveDelegate.Instance.InvalidateEnclaveSession(this._activeConnection.Parser.EnclaveType, this._activeConnection.DataSource, this._activeConnection.EnclaveAttestationUrl, this.enclavePackage.EnclaveSession); + EnclaveDelegate.Instance.InvalidateEnclaveSession(this._activeConnection.AttestationProtocol, this._activeConnection.Parser.EnclaveType, + this._activeConnection.DataSource, this._activeConnection.EnclaveAttestationUrl, this.enclavePackage.EnclaveSession); } return RunExecuteReader(cmdBehavior, runBehavior, returnStream, method, null, TdsParserStaticMethods.GetRemainingTimeout(timeout, firstAttemptStart), out task, out usedCache, async, inRetry: true); @@ -4985,7 +4988,8 @@ internal SqlDataReader RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior if (ShouldUseEnclaveBasedWorkflow && this.enclavePackage != null) { - EnclaveDelegate.Instance.InvalidateEnclaveSession(this._activeConnection.Parser.EnclaveType, this._activeConnection.DataSource, this._activeConnection.EnclaveAttestationUrl, this.enclavePackage.EnclaveSession); + EnclaveDelegate.Instance.InvalidateEnclaveSession(this._activeConnection.AttestationProtocol, this._activeConnection.Parser.EnclaveType, + this._activeConnection.DataSource, this._activeConnection.EnclaveAttestationUrl, this.enclavePackage.EnclaveSession); } return RunExecuteReader(cmdBehavior, runBehavior, returnStream, method, null, TdsParserStaticMethods.GetRemainingTimeout(timeout, firstAttemptStart), out task, out usedCache, async, inRetry: true); @@ -5120,9 +5124,15 @@ private void GenerateEnclavePackage() if (string.IsNullOrWhiteSpace(enclaveType)) throw SQL.EnclaveTypeNullForEnclaveBasedQuery(); + SqlConnectionAttestationProtocol attestationProtocol = this._activeConnection.AttestationProtocol; + if (attestationProtocol == SqlConnectionAttestationProtocol.NotSpecified) + { + throw SQL.AttestationProtocolNotSpecifiedForGeneratingEnclavePackage(); + } + try { - this.enclavePackage = EnclaveDelegate.Instance.GenerateEnclavePackage(keysToBeSentToEnclave, + this.enclavePackage = EnclaveDelegate.Instance.GenerateEnclavePackage(attestationProtocol, keysToBeSentToEnclave, this.CommandText, enclaveType, this._activeConnection.DataSource, this._activeConnection.EnclaveAttestationUrl); } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionString.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionString.cs index fa9800261e..9249f01995 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionString.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionString.cs @@ -55,7 +55,7 @@ internal static class DEFAULT internal static readonly SqlAuthenticationMethod Authentication = SqlAuthenticationMethod.NotSpecified; internal static readonly SqlConnectionColumnEncryptionSetting ColumnEncryptionSetting = SqlConnectionColumnEncryptionSetting.Disabled; internal const string EnclaveAttestationUrl = ""; - internal static readonly SqlConnectionAttestationProtocol AttestationProtocol = SqlConnectionAttestationProtocol.None; + internal static readonly SqlConnectionAttestationProtocol AttestationProtocol = SqlConnectionAttestationProtocol.NotSpecified; #if ADONET_CERT_AUTH internal const string Certificate = ""; #endif diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs index 8dbc11aa27..6b6c2cadd2 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs @@ -645,7 +645,9 @@ public string EnclaveAttestationUrl } } - // to add docs include file + /// + /// To add include file for docs + /// [DisplayName(DbConnectionStringKeywords.AttestationProtocol)] [ResCategoryAttribute(StringsHelper.ResourceNames.DataCategory_Security)] [ResDescriptionAttribute(StringsHelper.ResourceNames.TCE_DbConnectionString_AttestationProtocol)] diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs index 1a6eede208..eaa8022be1 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs @@ -1592,9 +1592,14 @@ static internal Exception EnclaveProvidersNotConfiguredForEnclaveBasedQuery() return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_EnclaveProvidersNotConfiguredForEnclaveBasedQuery)); } - static internal Exception EnclaveProviderNotFound(string enclaveType) + static internal Exception EnclaveProviderNotFound(string enclaveType, string attestationProtocol) { - return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_EnclaveProviderNotFound, enclaveType)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_EnclaveProviderNotFound, enclaveType, attestationProtocol)); + } + + static internal Exception AttestationProtocolNotSpecifiedForGeneratingEnclavePackage() + { + return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_AttestationProtocolNotSpecifiedForGeneratingEnclavePackage)); } static internal Exception NullEnclaveSessionReturnedFromProvider(string enclaveType, string attestationUrl) 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 2096dc5737..0866911220 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 @@ -1037,13 +1037,15 @@ public enum SqlCommandColumnEncryptionSetting Disabled, } - // To add include file for docs + /// + /// To add include file for docs + /// public enum SqlConnectionAttestationProtocol { /// /// If the attestation protocol is not specified. Use this as default value. /// - None = 0, + NotSpecified = 0, /// /// Attestation portocol for Azure Attestation Service @@ -1053,7 +1055,7 @@ public enum SqlConnectionAttestationProtocol /// /// Attestation protocol for Host Guardian Service /// - HGS = 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 eb69f1f556..1c34e3f070 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 @@ -3484,7 +3484,7 @@ private bool TryProcessFeatureExtAck(TdsParserStateObject stateObj) private bool IsValidAttestationProtocol(string enclaveType) { - switch (enclaveType.ToUpper()) + switch (enclaveType) { case TdsEnums.ENCLAVE_TYPE_VBS: if (_connHandler.ConnectionOptions.AttestationProtocol != SqlConnectionAttestationProtocol.AAS diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs index fc0d6484d0..c4c14ec91c 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs @@ -11960,6 +11960,15 @@ internal static string TCE_EnclaveProviderNotFound { return ResourceManager.GetString("TCE_EnclaveProviderNotFound", resourceCulture); } } + + /// + /// Looks up a localized string similar to Error occured when generating enclave package. Attestation Protocol has not been specified in the connection string, but the query requires enclave computations. + /// + internal static string TCE_AttestationProtocolNotSpecifiedForGeneratingEnclavePackage { + get { + return ResourceManager.GetString("TCE_AttestationProtocolNotSpecifiedForGeneratingEnclavePackage", resourceCulture); + } + } /// /// Looks up a localized string similar to Executing a query requires enclave computations, but the application configuration is missing the enclave provider section.. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx index b1cef25d4a..28a3695d75 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx @@ -4321,7 +4321,7 @@ Executing a query requires enclave computations, but the application configuration is missing the enclave provider section. - No enclave provider found for enclave type '{0}'. Please specify the provider in the application configuration. + No enclave provider found for enclave type '{0}' and attestation protocol '{1}'. Please specify the correct attestation protocol in the connection string. Unable to communicate with the enclave. Null enclave session information received from the enclave provider. Enclave type is '{0}' and enclave attestation URL is '{1}'. @@ -4515,4 +4515,7 @@ Failed to initialize connection. The attestation protocol does not support the enclave type '{0}'. + + Error occured when generating enclave package. Attestation Protocol has not been specified in the connection string, but the query requires enclave computations. + \ No newline at end of file From fdf728f4fcd160cc45f589643145a56d0fe063e8 Mon Sep 17 00:00:00 2001 From: Karina Zhou Date: Wed, 9 Oct 2019 15:04:41 -0700 Subject: [PATCH 07/63] include attestation protocol info in exception --- .../src/Microsoft/Data/SqlClient/SqlUtil.cs | 4 +-- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 28 +++++++++++++++---- .../netfx/src/Resources/Strings.resx | 2 +- 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs index eaa8022be1..7bdf736858 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs @@ -1740,9 +1740,9 @@ static internal Exception EnclaveTypeNotSupported(string enclaveType) return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_SequentialAccessNotSupportedOnEncryptedColumn, enclaveType)); } - static internal Exception AttestationProtocolNotSupportEnclaveType(string enclaveType) + static internal Exception AttestationProtocolNotSupportEnclaveType(string attestationProtocolStr, string enclaveType) { - return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_AttestationProtocolNotSupportEnclaveType, enclaveType)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_AttestationProtocolNotSupportEnclaveType, attestationProtocolStr, enclaveType)); } // 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 bc0bfde7ff..1c906042f1 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 @@ -3471,31 +3471,32 @@ private bool TryProcessFeatureExtAck(TdsParserStateObject stateObj) } // Check if enclave attestation url was specified and the attestation protocol supports the enclave type. + SqlConnectionAttestationProtocol attestationProtocol = _connHandler.ConnectionOptions.AttestationProtocol; if (this.Connection.RoutingInfo == null && (!string.IsNullOrWhiteSpace(_connHandler.ConnectionOptions.EnclaveAttestationUrl)) && (!string.IsNullOrWhiteSpace(EnclaveType)) - && (!IsValidAttestationProtocol(EnclaveType))) + && (!IsValidAttestationProtocol(attestationProtocol, EnclaveType))) { - throw SQL.AttestationProtocolNotSupportEnclaveType(EnclaveType); + throw SQL.AttestationProtocolNotSupportEnclaveType(ConvertAttestationProtocolToString(attestationProtocol), EnclaveType); } return true; } - private bool IsValidAttestationProtocol(string enclaveType) + private bool IsValidAttestationProtocol(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType) { switch (enclaveType) { case TdsEnums.ENCLAVE_TYPE_VBS: - if (_connHandler.ConnectionOptions.AttestationProtocol != SqlConnectionAttestationProtocol.AAS - && _connHandler.ConnectionOptions.AttestationProtocol != SqlConnectionAttestationProtocol.HGS) + if (attestationProtocol != SqlConnectionAttestationProtocol.AAS + && attestationProtocol != SqlConnectionAttestationProtocol.HGS) { return false; } break; case TdsEnums.ENCLAVE_TYPE_SGX: - if (_connHandler.ConnectionOptions.AttestationProtocol != SqlConnectionAttestationProtocol.AAS) + if (attestationProtocol != SqlConnectionAttestationProtocol.AAS) { return false; } @@ -3509,6 +3510,21 @@ private bool IsValidAttestationProtocol(string enclaveType) return true; } + private string ConvertAttestationProtocolToString(SqlConnectionAttestationProtocol attestationProtocol) + { + switch (attestationProtocol) + { + case SqlConnectionAttestationProtocol.AAS: + return "AAS"; + + case SqlConnectionAttestationProtocol.HGS: + return "HGS"; + + default: + return "NotSpecified"; + } + } + private bool TryReadByteString(TdsParserStateObject stateObj, out string value) { value = string.Empty; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx index 28a3695d75..032fed2c7d 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx @@ -4513,7 +4513,7 @@ The enclave type '{0}' returned from the server is not supported. - Failed to initialize connection. The attestation protocol does not support the enclave type '{0}'. + Failed to initialize connection. The attestation protocol '{0}' does not support the enclave type '{1}'. Error occured when generating enclave package. Attestation Protocol has not been specified in the connection string, but the query requires enclave computations. From a3d46a535cb4516232127bcaa9d26dd3912245a7 Mon Sep 17 00:00:00 2001 From: Javad Date: Thu, 10 Oct 2019 10:16:48 -0700 Subject: [PATCH 08/63] Adding packages to NetFx csproj --- .../netfx/src/Microsoft.Data.SqlClient.csproj | 6 ++++++ 1 file changed, 6 insertions(+) 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 63dd5e0abc..c99542543c 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -352,6 +352,12 @@ 5.5.0 + + 5.5.0 + + + 5.5.0 + 5.5.0 From 6f1003c2544f518904d57b3d69f8a7a6c42b5fc8 Mon Sep 17 00:00:00 2001 From: Javad Date: Thu, 10 Oct 2019 10:25:20 -0700 Subject: [PATCH 09/63] removing extra reference --- .../netfx/src/Microsoft.Data.SqlClient.csproj | 3 --- 1 file changed, 3 deletions(-) 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 c99542543c..cd1514b745 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -358,9 +358,6 @@ 5.5.0 - - 5.5.0 - 4.5.0 From ec0b9f58f22f61fbbecf48d2e74bc3aace5e990b Mon Sep 17 00:00:00 2001 From: Javad Date: Thu, 10 Oct 2019 16:19:25 -0700 Subject: [PATCH 10/63] Update Microsoft.Data.SqlClient.csproj --- .../netfx/src/Microsoft.Data.SqlClient.csproj | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) 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 cd1514b745..7d5f6a2cd8 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -352,12 +352,9 @@ 5.5.0 - + 5.5.0 - - 5.5.0 - 4.5.0 @@ -370,4 +367,4 @@ - \ No newline at end of file + From 256ca73fc977f2c74a7b0cc0ed3d5e3f3cda404d Mon Sep 17 00:00:00 2001 From: Javad Date: Thu, 10 Oct 2019 17:11:39 -0700 Subject: [PATCH 11/63] netcore changes --- .../netcore/src/Resources/SR.Designer.cs | 191 +++++++++++++++++- .../netcore/src/Resources/SR.resx | 63 ++++++ .../netfx/src/Microsoft.Data.SqlClient.csproj | 5 +- 3 files changed, 254 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs index f440879334..cc57d64639 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs @@ -19,7 +19,7 @@ namespace System { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class SR { @@ -879,6 +879,15 @@ internal static string ArgumentOutOfRange_NeedNonNegNum { } } + /// + /// Looks up a localized string similar to The validation of an attestation token failed. The token signature does not match the signature omputed using a public key retrieved from the attestation public key endpoint at '{0}'. Verify the DNS apping for the endpoint. If correct, contact Customer Support Services.. + /// + internal static string AttestationTokenSignatureValidationFailed { + get { + return ResourceManager.GetString("AttestationTokenSignatureValidationFailed", resourceCulture); + } + } + /// /// Looks up a localized string similar to .database.chinacloudapi.cn. /// @@ -924,6 +933,24 @@ internal static string Data_InvalidOffsetLength { } } + /// + /// Looks up a localized string similar to Internal error occurred when retrying the download of the HGS root certificate after the initial request failed. Contact Customer Support Services.. + /// + internal static string EnclaveRetrySleepInSecondsValueException { + get { + return ResourceManager.GetString("EnclaveRetrySleepInSecondsValueException", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Internal error. Unable to invalidate the requested enclave session, because it does not exist in the cache. Contact Customer Support Services.. + /// + internal static string EnclaveSessionInvalidationFailed { + get { + return ResourceManager.GetString("EnclaveSessionInvalidationFailed", resourceCulture); + } + } + /// /// Looks up a localized string similar to {0} returned {1}.. /// @@ -933,6 +960,78 @@ internal static string event_OperationReturnedSomething { } } + /// + /// Looks up a localized string similar to The validation of an attestation token failed. The token received from SQL Server is expired. Contact Customer Support Services.. + /// + internal static string ExpiredAttestationToken { + get { + return ResourceManager.GetString("ExpiredAttestationToken", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to create enclave session as attestation server is busy.. + /// + internal static string FailToCreateEnclaveSession { + get { + return ResourceManager.GetString("FailToCreateEnclaveSession", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The validation of an attestation information failed. The attestation information has an invalid format. Contact Customer Support Services. Error details: '{0}'.. + /// + internal static string FailToParseAttestationInfo { + get { + return ResourceManager.GetString("FailToParseAttestationInfo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The validation of an attestation token failed. The token has an invalid format. Contact Customer Support Services. Error details: '{0}'.. + /// + internal static string FailToParseAttestationToken { + get { + return ResourceManager.GetString("FailToParseAttestationToken", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The attestation service returned an expired HGS root certificate for attestation URL '{0}'. Check the HGS root certificate configured for your HGS instance.. + /// + internal static string GetAttestationSigningCertificateFailedInvalidCertificate { + get { + return ResourceManager.GetString("GetAttestationSigningCertificateFailedInvalidCertificate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The obtained HGS root certificate for attestation URL '{0}' has an invalid format. Verify the attestation URL is correct and the HGS server is online and fully initialized. For more information contact Customer Support Services. Error details: '{1}'.. + /// + internal static string GetAttestationSigningCertificateRequestFailedFormat { + get { + return ResourceManager.GetString("GetAttestationSigningCertificateRequestFailedFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The validation of an attestation token failed. Cannot retrieve a public key from the attestation public key endpoint, or the retrieved key has an invalid format. Error details: '{0}'.. + /// + internal static string GetAttestationTokenSigningKeysFailed { + get { + return ResourceManager.GetString("GetAttestationTokenSigningKeysFailed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Signature verification of the enclave's Diffie-Hellman key failed. Contact Customer Support Services.. + /// + internal static string GetSharedSecretFailed { + get { + return ResourceManager.GetString("GetSharedSecretFailed", resourceCulture); + } + } + /// /// Looks up a localized string similar to Global Transactions are not enabled for this Azure SQL Database. Please contact Azure SQL Database support for assistance.. /// @@ -951,6 +1050,42 @@ internal static string IEnumerableOfSqlDataRecordHasNoRows { } } + /// + /// Looks up a localized string similar to The validation of an attestation token failed due to an error while decoding the enclave public key obtained from SQL Server. Contact Customer Support Services.. + /// + internal static string InvalidArgumentToBase64UrlDecoder { + get { + return ResourceManager.GetString("InvalidArgumentToBase64UrlDecoder", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The validation of an attestation token failed due to an error while computing a hash of the enclave public key obtained from SQL Server. Contact Customer Support Services.. + /// + internal static string InvalidArgumentToSHA256 { + get { + return ResourceManager.GetString("InvalidArgumentToSHA256", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The validation of the attestation token has failed during signature validation. Exception: '{0}'.. + /// + internal static string InvalidAttestationToken { + get { + return ResourceManager.GetString("InvalidAttestationToken", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The validation of an attestation token failed. Claim '{0}' in the token has an invalid value of '{1}'. Verify the attestation policy. If the policy is correct, contact Customer Support Services.. + /// + internal static string InvalidClaimInAttestationToken { + get { + return ResourceManager.GetString("InvalidClaimInAttestationToken", resourceCulture); + } + } + /// /// Looks up a localized string similar to Invalid column ordinals in schema table. ColumnOrdinals, if present, must not have duplicates or gaps.. /// @@ -1140,6 +1275,15 @@ internal static string MDF_UnsupportedVersion { } } + /// + /// Looks up a localized string similar to The validation of the attestation token failed. Claim '{0}' is missing in the token. Verify the attestation policy. If the policy is correct, contact Customer Support Services.. + /// + internal static string MissingClaimInAttestationToken { + get { + return ResourceManager.GetString("MissingClaimInAttestationToken", resourceCulture); + } + } + /// /// Looks up a localized string similar to Protocol error: A received message contains a valid signature but it was not encrypted as required by the effective Protection Level.. /// @@ -5027,5 +5171,50 @@ internal static string TCE_UntrustedKeyPath { return ResourceManager.GetString("TCE_UntrustedKeyPath", resourceCulture); } } + + /// + /// Looks up a localized string similar to Failed to check if the enclave is running in the production mode. Contact Customer Support Services.. + /// + internal static string VerifyEnclaveDebuggable { + get { + return ResourceManager.GetString("VerifyEnclaveDebuggable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Could not verify enclave policy due to a difference between the expected and actual values of the policy on property '{0}'. Actual: '{1}', Expected: '{2}'.. + /// + internal static string VerifyEnclavePolicyFailedFormat { + get { + return ResourceManager.GetString("VerifyEnclavePolicyFailedFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Signature verification of the enclave report failed. The report signature does not match the signature computed using the HGS root certificate. Verify the DNS mapping for the endpoint. If correct, contact Customer Support Services.. + /// + internal static string VerifyEnclaveReportFailed { + get { + return ResourceManager.GetString("VerifyEnclaveReportFailed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The enclave report received from SQL Server is not in the correct format. Contact Customer Support Services.. + /// + internal static string VerifyEnclaveReportFormatFailed { + get { + return ResourceManager.GetString("VerifyEnclaveReportFormatFailed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to build a chain of trust between the enclave host's health report and the HGS root certificate for attestation URL '{0}' with status: '{1}'. Verify the attestation URL matches the URL configured on the SQL Server machine. If both the client and SQL Server use the same attestation service, contact Customer Support Services.. + /// + internal static string VerifyHealthCertificateChainFormat { + get { + return ResourceManager.GetString("VerifyHealthCertificateChainFormat", resourceCulture); + } + } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx index 67e32434bc..ea5834018a 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx @@ -1773,4 +1773,67 @@ Globalization Invariant Mode is not supported. + + The validation of an attestation token failed. The token signature does not match the signature omputed using a public key retrieved from the attestation public key endpoint at '{0}'. Verify the DNS apping for the endpoint. If correct, contact Customer Support Services. + + + Internal error occurred when retrying the download of the HGS root certificate after the initial request failed. Contact Customer Support Services. + + + Internal error. Unable to invalidate the requested enclave session, because it does not exist in the cache. Contact Customer Support Services. + + + The validation of an attestation token failed. The token received from SQL Server is expired. Contact Customer Support Services. + + + Failed to create enclave session as attestation server is busy. + + + The validation of an attestation information failed. The attestation information has an invalid format. Contact Customer Support Services. Error details: '{0}'. + + + The validation of an attestation token failed. The token has an invalid format. Contact Customer Support Services. Error details: '{0}'. + + + The attestation service returned an expired HGS root certificate for attestation URL '{0}'. Check the HGS root certificate configured for your HGS instance. + + + The obtained HGS root certificate for attestation URL '{0}' has an invalid format. Verify the attestation URL is correct and the HGS server is online and fully initialized. For more information contact Customer Support Services. Error details: '{1}'. + + + The validation of an attestation token failed. Cannot retrieve a public key from the attestation public key endpoint, or the retrieved key has an invalid format. Error details: '{0}'. + + + Signature verification of the enclave's Diffie-Hellman key failed. Contact Customer Support Services. + + + The validation of an attestation token failed due to an error while decoding the enclave public key obtained from SQL Server. Contact Customer Support Services. + + + The validation of an attestation token failed due to an error while computing a hash of the enclave public key obtained from SQL Server. Contact Customer Support Services. + + + The validation of the attestation token has failed during signature validation. Exception: '{0}'. + + + The validation of an attestation token failed. Claim '{0}' in the token has an invalid value of '{1}'. Verify the attestation policy. If the policy is correct, contact Customer Support Services. + + + The validation of the attestation token failed. Claim '{0}' is missing in the token. Verify the attestation policy. If the policy is correct, contact Customer Support Services. + + + Failed to check if the enclave is running in the production mode. Contact Customer Support Services. + + + Could not verify enclave policy due to a difference between the expected and actual values of the policy on property '{0}'. Actual: '{1}', Expected: '{2}'. + + + Signature verification of the enclave report failed. The report signature does not match the signature computed using the HGS root certificate. Verify the DNS mapping for the endpoint. If correct, contact Customer Support Services. + + + The enclave report received from SQL Server is not in the correct format. Contact Customer Support Services. + + + Failed to build a chain of trust between the enclave host's health report and the HGS root certificate for attestation URL '{0}' with status: '{1}'. Verify the attestation URL matches the URL configured on the SQL Server machine. If both the client and SQL Server use the same attestation service, contact Customer Support Services. + \ No newline at end of file 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 cd1514b745..63dd5e0abc 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -352,12 +352,9 @@ 5.5.0 - + 5.5.0 - - 5.5.0 - 4.5.0 From 61cb439d44a0b466949a89b2b4ab8de5fbb5c04d Mon Sep 17 00:00:00 2001 From: Javad Date: Thu, 10 Oct 2019 17:29:03 -0700 Subject: [PATCH 12/63] Adding Enclave files to NetCore --- .../src/Microsoft.Data.SqlClient.csproj | 8 + .../AlwaysEncryptedAttestationException.cs | 31 + .../AlwaysEncryptedEnclaveProviderUtils.cs | 91 +++ .../AzureAttestationBasedEnclaveProvider.cs | 642 ++++++++++++++++++ .../Data/SqlClient/EnclaveProviderBase.cs | 261 +++++++ .../Data/SqlClient/EnclaveSessionCache.cs | 103 +++ .../VirtualSecureModeEnclaveProvider.cs | 545 +++++++++++++++ .../VirtualSecureModeEnclaveProviderBase.cs | 450 ++++++++++++ 8 files changed, 2131 insertions(+) create mode 100644 src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.cs create mode 100644 src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs create mode 100644 src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs create mode 100644 src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs create mode 100644 src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveSessionCache.cs create mode 100644 src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.cs create mode 100644 src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.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 dfdf259ece..5807421265 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -573,6 +573,13 @@ + + + + + + + True True @@ -587,6 +594,7 @@ + diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.cs new file mode 100644 index 0000000000..22a3145135 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.Data.SqlClient +{ + + /// + /// Represents errors occuring during an Always Encrypted secure enclave operation + /// + public class AlwaysEncryptedAttestationException : Exception + { + /// + /// + /// + /// + /// + public AlwaysEncryptedAttestationException(string message, Exception innerException) : base(message, innerException) { } + + /// + /// + /// + /// + public AlwaysEncryptedAttestationException(string message) : base(message) { } + + /// + /// + /// + public AlwaysEncryptedAttestationException() : base() { } + } +} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs new file mode 100644 index 0000000000..5de07c002c --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Microsoft.Data.SqlClient +{ + /// + /// Class to hold the enclave's RSA public key + /// + public class EnclavePublicKey + { + /// + /// + /// + public byte[] PublicKey { get; set; } + + /// + /// + /// + /// + public EnclavePublicKey(byte[] payload) + { + PublicKey = payload; + } + } + + /// + /// Class to hold the Enclave's Diffie-Hellman public key and signature + /// + public class EnclaveDiffieHellmanInfo + { + /// + /// + /// + public int Size { get; private set; } + + /// + /// + /// + public byte[] PublicKey { get; private set; } + + /// + /// + /// + public byte[] PublicKeySignature { get; private set; } + + /// + /// + /// + /// + public EnclaveDiffieHellmanInfo(byte[] payload) + { + Size = payload.Length; + + int offset = 0; + int publicKeySize = BitConverter.ToInt32(payload, offset); + offset += sizeof(int); + + int publicKeySignatureSize = BitConverter.ToInt32(payload, offset); + offset += sizeof(int); + + PublicKey = payload.Skip(offset).Take(publicKeySize).ToArray(); + offset += publicKeySize; + + PublicKeySignature = payload.Skip(offset).Take(publicKeySignatureSize).ToArray(); + offset += publicKeySignatureSize; + } + } + + /// + /// + /// + public enum EnclaveType + { + /// + /// + /// + None = 0, + + /// + /// + /// + Vbs = 1, + + /// + /// + /// + Sgx = 2 + } +} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs new file mode 100644 index 0000000000..795523f9d7 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs @@ -0,0 +1,642 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.IdentityModel.Tokens.Jwt; +using System.Linq; +using System.Runtime.Caching; +using System.Runtime.Serialization.Json; +using System.Security.Claims; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Threading; +using Microsoft.IdentityModel.JsonWebTokens; +using Microsoft.IdentityModel.Logging; +using Microsoft.IdentityModel.Protocols; +using Microsoft.IdentityModel.Protocols.OpenIdConnect; +using Microsoft.IdentityModel.Tokens; + + +// Azure Attestation Protocol Flow +// To start the attestation process, Sql Client sends the Protocol Id (i.e. 1), Nonce, Attestation Url and ECDH Public Key +// Sql Server uses attestation Url to attest the enclave and send the JWT to Sql client. +// Along with JWT, Sql server also sends enclave RSA public key, enclave Type, Enclave ECDH Public key. + +// To verify the chain of trust here is how it works +// JWT is signed by well-known signing keys which Sql client can download over https (via OpenIdConnect protocol). +// JWT contains the Enclave public key to safeguard against spoofing enclave RSA public key. +// Enclave ECDH public key signed by enclave RSA key + +// JWT validation +// To get the signing key for the JWT, we use OpenIdConnect API's. It download the signing keys from the well-known endpoint. +// We validate that JWT is signed, valid (i.e. not expired) and check the Issuer. + +// Claim validation: +// Validate the RSA public key send by Sql server matches the value specified in JWT. + +// Enclave Specific checks +// VSM +// Validate the nonce send by Sql client during start of attestation is same as that of specified in the JWT + +// SGX +// JWT for SGX enclave does not contain nonce claim. To workaround this limitation Sql Server sends the RSA public key XOR with the Nonce. +// In Sql server tempered with the nonce value then both Sql Server and client will not able to compute the same shared secret. + +namespace Microsoft.Data.SqlClient +{ + /// + /// Implementation of an Enclave provider (both for Sgx and Vsm) with Azure Attestation + /// + public class AzureAttestationEnclaveProvider : EnclaveProviderBase + { + #region Constants + private const int DiffieHellmanKeySize = 384; + private const int AzureBasedAttestationProtocolId = 1; + private const int SigningKeyRetryInSec = 3; + #endregion + + #region Members + // this is meta data endpoint for AAS provided by Windows team + // i.e. https:///.well-known/openid-configuration + // such as https://sql.azure.attest.com/.well-known/openid-configuration + private const string AttestationUrlSuffix = @"/.well-known/openid-configuration"; + + private static readonly MemoryCache OpenIdConnectConfigurationCache = new MemoryCache("OpenIdConnectConfigurationCache"); + #endregion + + #region Public methods + /// + /// 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. + /// + /// The name of the SQL Server instance containing the enclave. + /// The endpoint of an attestation service, SqlClient contacts to attest the enclave. + /// When this method returns, the requested enclave session or null if the provider doesn't implement session caching. This parameter is treated as uninitialized. + /// A counter that the enclave provider is expected to increment each time SqlClient retrieves the session from the cache. The purpose of this field is to prevent replay attacks. + public override void GetEnclaveSession(string servername, string attestationUrl, out SqlEnclaveSession sqlEnclaveSession, out long counter) + { + GetEnclaveSessionHelper(servername, attestationUrl, true, out sqlEnclaveSession, out counter); + } + + /// + /// Gets the information that SqlClient subsequently uses to initiate the process of attesting the enclave and to establish a secure session with the enclave. + /// + /// The information SqlClient subsequently uses to initiate the process of attesting the enclave and to establish a secure session with the enclave. + public override SqlEnclaveAttestationParameters GetAttestationParameters() + { + ECDiffieHellmanCng clientDHKey = new ECDiffieHellmanCng(DiffieHellmanKeySize); + clientDHKey.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash; + clientDHKey.HashAlgorithm = CngAlgorithm.Sha256; + byte[] attestationParam = PrepareAttestationParameters(); + return new SqlEnclaveAttestationParameters(AzureBasedAttestationProtocolId, attestationParam, 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. + /// + /// The information the provider uses to attest the enclave and generate a symmetric key for the session. The format of this information is specific to the enclave attestation protocol. + /// A Diffie-Hellman algorithm object that encapsulates a client-side key pair. + /// The endpoint of an attestation service for attesting the enclave. + /// The name of the SQL Server instance containing the enclave. + /// The requested enclave session or null if the provider doesn't implement session caching. + /// A counter that the enclave provider is expected to increment each time SqlClient retrieves the session from the cache. The purpose of this field is to prevent replay attacks. + public override void CreateEnclaveSession(byte[] attestationInfo, ECDiffieHellmanCng clientDHKey, string attestationUrl, string servername, out SqlEnclaveSession sqlEnclaveSession, out long counter) + { + sqlEnclaveSession = null; + counter = 0; + try + { + AttestationInfoCacheItem attestationInfoCacheItem = AttestationInfoCache.Remove(Thread.CurrentThread.ManagedThreadId.ToString()) as AttestationInfoCacheItem; + sqlEnclaveSession = GetEnclaveSessionFromCache(servername, attestationUrl, out counter); + if (sqlEnclaveSession == null) + { + if (attestationInfoCacheItem != null) + { + byte[] nonce = attestationInfoCacheItem.AttestNonce; + + IdentityModelEventSource.ShowPII = true; + + // Deserialize the payload + AzureAttestationInfo attestInfo = new AzureAttestationInfo(attestationInfo); + + // Validate the attestation info + VerifyAzureAttestationInfo(attestationUrl, attestInfo.EnclaveType, attestInfo.AttestationToken.AttestationToken, attestInfo.Identity, nonce); + + // Set up shared secret and validate signature + byte[] sharedSecret = GetSharedSecret(attestInfo.Identity, nonce, attestInfo.EnclaveType, attestInfo.EnclaveDHInfo, clientDHKey); + + // add session to cache + sqlEnclaveSession = AddEnclaveSessionToCache(attestationUrl, servername, sharedSecret, attestInfo.SessionId, out counter); + } + else + { + throw new AlwaysEncryptedAttestationException(SR.FailToCreateEnclaveSession); + } + } + } + finally + { + // As per current design, we want to minimize the number of create session calls. To acheive this we block all the GetEnclaveSession calls until the first call to + // GetEnclaveSession -> GetAttestationParameters -> CreateEnclaveSession completes or the event timeout happen. + // Case 1: When the first request successfully creates the session, then all outstanding GetEnclaveSession will use the current session. + // Case 2: When the first request unable to create the encalve session (may be due to some error or the first request doesn't require enclave computation) then in those case we set the event timeout to 0. + UpdateEnclaveSessionLockStatus(sqlEnclaveSession); + } + } + + /// + /// When overridden in a derived class, looks up and evicts an enclave session from the enclave session cache, if the provider implements session caching. + /// + /// The name of the SQL Server instance containing the enclave. + /// The endpoint of an attestation service, SqlClient contacts to attest the enclave. + /// The session to be invalidated. + public override void InvalidateEnclaveSession(string serverName, string enclaveAttestationUrl, SqlEnclaveSession enclaveSessionToInvalidate) + { + InvalidateEnclaveSessionHelper(serverName, enclaveAttestationUrl, enclaveSessionToInvalidate); + } + #endregion + + #region Internal Class + /// + /// A model class respresenting the deserialization of the byte payload the client + /// receives from SQL Server while setting up a session. + /// Protocol format: + /// 1. Total Size of the attestation blob as UINT + /// 2. Size of Enclave RSA public key as UINT + /// 3. Size of Attestation token as UINT + /// 4. Enclave Type as UINT + /// 5. Enclave RSA public key (raw key, of length #2) + /// 6. Attestation token (of length #3) + /// 7. Size of Session Id was UINT + /// 8. Session id value + /// 9. Size of enclave ECDH public key + /// 10. Enclave ECDH public key (of length #9) + /// + internal class AzureAttestationInfo + { + public uint TotalSize { get; set; } + + /// + /// The enclave's RSA Public Key. + /// Needed to establish trust of the enclave. + /// Used to verify the enclave's DiffieHellman info. + /// + public EnclavePublicKey Identity { get; set; } + + /// + /// The enclave report from the SQL Server host's enclave. + /// + public AzureAttestationToken AttestationToken { get; set; } + + /// + /// The id of the current session. + /// Needed to set up a secure session between the client and enclave. + /// + public long SessionId { get; set; } + + public EnclaveType EnclaveType { get; set; } + + /// + /// The DiffieHellman public key and signature of SQL Server host's enclave. + /// Needed to set up a secure session between the client and enclave. + /// + public EnclaveDiffieHellmanInfo EnclaveDHInfo { get; set; } + + public AzureAttestationInfo(byte[] attestationInfo) + { + try + { + int offset = 0; + + // Total size of the attestation info buffer + TotalSize = BitConverter.ToUInt32(attestationInfo, offset); + offset += sizeof(uint); + + // Size of the Enclave public key + int identitySize = BitConverter.ToInt32(attestationInfo, offset); + offset += sizeof(uint); + + // Size of the Azure attestation token + int attestationTokenSize = BitConverter.ToInt32(attestationInfo, offset); + offset += sizeof(uint); + + // Enclave type + int enclaveType = BitConverter.ToInt32(attestationInfo, offset); + EnclaveType = (EnclaveType)enclaveType; + offset += sizeof(uint); + + // Get the enclave public key + byte[] identityBuffer = attestationInfo.Skip(offset).Take(identitySize).ToArray(); + Identity = new EnclavePublicKey(identityBuffer); + offset += identitySize; + + // Get Azure attestation token + byte[] attestationTokenBuffer = attestationInfo.Skip(offset).Take(attestationTokenSize).ToArray(); + AttestationToken = new AzureAttestationToken(attestationTokenBuffer); + offset += attestationTokenSize; + + uint secureSessionInfoResponseSize = BitConverter.ToUInt32(attestationInfo, offset); + offset += sizeof(uint); + + SessionId = BitConverter.ToInt64(attestationInfo, offset); + offset += sizeof(long); + + int secureSessionBufferSize = Convert.ToInt32(secureSessionInfoResponseSize) - sizeof(uint); + byte[] secureSessionBuffer = attestationInfo.Skip(offset).Take(secureSessionBufferSize).ToArray(); + EnclaveDHInfo = new EnclaveDiffieHellmanInfo(secureSessionBuffer); + offset += Convert.ToInt32(EnclaveDHInfo.Size); + } + catch (Exception exception) + { + throw new AlwaysEncryptedAttestationException(String.Format(SR.FailToParseAttestationInfo, exception.Message)); + } + } + } + + /// + /// A managed model representing the output of EnclaveGetAttestationReport + /// https://msdn.microsoft.com/en-us/library/windows/desktop/mt844233(v=vs.85).aspx + /// + internal class AzureAttestationToken + { + public string AttestationToken { get; set; } + + public AzureAttestationToken(byte[] payload) + { + string jwt = System.Text.Encoding.Default.GetString(payload); + AttestationToken = jwt.Trim().Trim('"'); + } + } + #endregion Internal Class + + #region Private helpers + /// + /// Prepare the attestation data in following format + /// Attestation Url length + /// Attestation Url + /// Size of nonce + /// Nonce value + /// + internal byte[] PrepareAttestationParameters() + { + AttestationInfoCacheItem attestationInfoCacheItem = AttestationInfoCache[Thread.CurrentThread.ManagedThreadId.ToString()] as AttestationInfoCacheItem; + if (attestationInfoCacheItem != null) + { + // In c# strings are not null terminated, so adding the null termination before serializing it + string attestationUrlLocal = attestationInfoCacheItem.AttestationUrl + char.MinValue; + byte[] serializedAttestationUrl = Encoding.Unicode.GetBytes(attestationUrlLocal); + byte[] serializedAttestationUrlLength = BitConverter.GetBytes(serializedAttestationUrl.Length); + + // serializing nonce + byte[] serializedNonce = attestationInfoCacheItem.AttestNonce; + byte[] serializedNonceLength = BitConverter.GetBytes(attestationInfoCacheItem.AttestNonce.Length); + + // Computing the total length of the data + int totalDataSize = serializedAttestationUrl.Length + serializedAttestationUrlLength.Length + serializedNonce.Length + serializedNonceLength.Length; + + int dataCopied = 0; + byte[] attestationParam = new byte[totalDataSize]; + + // copy the attestation url and url length + Buffer.BlockCopy(serializedAttestationUrlLength, 0, attestationParam, dataCopied, serializedAttestationUrlLength.Length); + dataCopied += serializedAttestationUrlLength.Length; + + Buffer.BlockCopy(serializedAttestationUrl, 0, attestationParam, dataCopied, serializedAttestationUrl.Length); + dataCopied += serializedAttestationUrl.Length; + + // copy the nonce and nonce length + Buffer.BlockCopy(serializedNonceLength, 0, attestationParam, dataCopied, serializedNonceLength.Length); + dataCopied += serializedNonceLength.Length; + + Buffer.BlockCopy(serializedNonce, 0, attestationParam, dataCopied, serializedNonce.Length); + dataCopied += serializedNonce.Length; + + return attestationParam; + } + else + { + throw new AlwaysEncryptedAttestationException(SR.FailToCreateEnclaveSession); + } + } + + /// + /// Performs Attestation per the protocol used by Azure Attestation Service + /// + /// Url of the attestation service + /// Type of Enclave which we are attesting + /// The Azure enclave attestation token about the SQL Server host's enclave + /// The enclave's RSA public key + /// Nonce value send by client during attestation + private void VerifyAzureAttestationInfo(string attestationUrl, EnclaveType enclaveType, string attestationToken, EnclavePublicKey enclavePublicKey, byte[] nonce) + { + bool shouldForceUpdateSigningKeys = false; + string attestationInstanceUrl = GetAttestationInstanceUrl(attestationUrl); + + bool shouldRetryValidation; + bool isSignatureValid; + string exceptionMessage = string.Empty; + do + { + shouldRetryValidation = false; + + // Get the OpenId config object for the signing keys + OpenIdConnectConfiguration openIdConfig = GetOpenIdConfigForSigningKeys(attestationInstanceUrl, shouldForceUpdateSigningKeys); + + // Verify the token signature against the signing keys downloaded from meta data end point + bool isKeySigningExpired; + isSignatureValid = VerifyTokenSignature(attestationToken, attestationInstanceUrl, openIdConfig.SigningKeys, out isKeySigningExpired, out exceptionMessage); + + // In cases if we fail to validate the token, since we are using the old signing keys + // let's re-download the signing keys again and re-validate the token signature + if (!isSignatureValid && isKeySigningExpired && !shouldForceUpdateSigningKeys) + { + shouldForceUpdateSigningKeys = true; + shouldRetryValidation = true; + } + } + while (shouldRetryValidation); + + if (!isSignatureValid) + { + throw new AlwaysEncryptedAttestationException(String.Format(SR.AttestationTokenSignatureValidationFailed, exceptionMessage)); + } + + // Validate claims in the token + ValidateAttestationClaims(enclaveType, attestationToken, enclavePublicKey, nonce); + } + + /// + /// Returns the innermost exception value + /// + /// Current exception value + /// + private static string GetInnerMostExceptionMessage(Exception exception) + { + Exception exLocal = exception; + while (exLocal.InnerException != null) + { + exLocal = exLocal.InnerException; + } + + return exLocal.Message; + } + + /// + /// For the given attestation url it downloads the token signing keys from the well-known openid configuration end point. + /// It also caches that information for 1 day to avoid DDOS attacks. + /// + /// Url of attestation service + /// Re-download the signing keys irrespective of caching + /// OpenIdConnectConfiguration object for the signing keys + private OpenIdConnectConfiguration GetOpenIdConfigForSigningKeys(string url, bool forceUpdate) + { + OpenIdConnectConfiguration openIdConnectConfig = OpenIdConnectConfigurationCache[url] as OpenIdConnectConfiguration; + if (forceUpdate || openIdConnectConfig == null) + { + // Compute the meta data endpoint + string openIdMetadataEndpoint = url + AttestationUrlSuffix; + + try + { + IConfigurationManager configurationManager = new ConfigurationManager(openIdMetadataEndpoint, new OpenIdConnectConfigurationRetriever()); + openIdConnectConfig = configurationManager.GetConfigurationAsync(CancellationToken.None).Result; + } + catch (Exception exception) + { + throw new AlwaysEncryptedAttestationException(String.Format(SR.GetAttestationTokenSigningKeysFailed, GetInnerMostExceptionMessage(exception)), exception); + } + + OpenIdConnectConfigurationCache.Add(url, openIdConnectConfig, DateTime.UtcNow.AddDays(1)); + } + + return openIdConnectConfig; + } + + /// + /// Return the attestation instance url for given attestation url + /// such as for https://sql.azure.attest.com/attest/SgxEnclave?api-version=2017-11-01 + /// It will return https://sql.azure.attest.com + /// + /// Url of the attestation service + /// altered url + private string GetAttestationInstanceUrl(string attestationUrl) + { + Uri attestationUri = new Uri(attestationUrl); + return attestationUri.GetLeftPart(UriPartial.Authority); + } + + /// + /// Generate the list of valid issuer Url's (in case if tokenIssuerUrl is using default port) + /// + /// Attestation token issuer url + /// List of valid issuer urls (can't be null/empty) + private static ICollection GenerateListOfIssuers(string tokenIssuerUrl) + { + List issuerUrls = new List(); + + Uri tokenIssuerUri = new Uri(tokenIssuerUrl); + int port = tokenIssuerUri.Port; + bool isDefaultPort = tokenIssuerUri.IsDefaultPort; + + string issuerUrl = tokenIssuerUri.GetLeftPart(UriPartial.Authority); + issuerUrls.Add(issuerUrl); + + if (isDefaultPort) + { + issuerUrls.Add(String.Concat(issuerUrl, ":", port.ToString())); + } + + return issuerUrls; + } + + /// + /// Verifies the attestation token is signed by correct signing keys. + /// + /// Complete attestation token + /// Attestation token issuer url + /// List of attestation token issuer signing keys + /// return if token signing key is expired + /// returns exception message to the caller + /// + private bool VerifyTokenSignature(string attestationToken, string tokenIssuerUrl, ICollection issuerSigningKeys, out bool isKeySigningExpired, out string exceptionMessage) + { + exceptionMessage = string.Empty; + bool isSignatureValid = false; + isKeySigningExpired = false; + + // Configure the TokenValidationParameters + TokenValidationParameters validationParameters = + new TokenValidationParameters + { + RequireExpirationTime = true, + ValidateLifetime = true, + ValidateIssuer = true, + ValidateAudience = false, + RequireSignedTokens = true, + ValidIssuers = GenerateListOfIssuers(tokenIssuerUrl), + IssuerSigningKeys = issuerSigningKeys + }; + + try + { + SecurityToken validatedToken; + JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler(); + var token = handler.ValidateToken(attestationToken, validationParameters, out validatedToken); + isSignatureValid = true; + } + catch (SecurityTokenExpiredException securityException) + { + throw new AlwaysEncryptedAttestationException(SR.ExpiredAttestationToken, securityException); + } + catch (SecurityTokenValidationException securityTokenException) + { + isKeySigningExpired = true; + + // Sleep for SigningKeyRetryInSec sec before retrying to download the signing keys again. + Thread.Sleep(SigningKeyRetryInSec * 1000); + exceptionMessage = GetInnerMostExceptionMessage(securityTokenException); + } + catch (Exception exception) + { + throw new AlwaysEncryptedAttestationException(String.Format(SR.InvalidAttestationToken, GetInnerMostExceptionMessage(exception))); + } + + return isSignatureValid; + } + + /// + /// Computes the SHA256 hash of the byte array + /// + /// Input for SHA256 + /// SHA256 of the input data + private byte[] ComputeSHA256(byte[] data) + { + byte[] result = null; + try + { + using (SHA256 sha256 = SHA256.Create()) + { + result = sha256.ComputeHash(data); + } + } + catch (Exception argumentException) + { + throw new AlwaysEncryptedAttestationException(SR.InvalidArgumentToSHA256, argumentException); + } + return result; + } + + /// + /// Validate the claims in the attestation token + /// + /// Type of Enclave which we are attesting + /// The Azure enclave attestation token about the SQL Server host's enclave + /// The enclave's RSA public key + /// Nonce value send by client during attestation + private void ValidateAttestationClaims(EnclaveType enclaveType, string attestationToken, EnclavePublicKey enclavePublicKey, byte[] nonce) + { + // Read the json token + JsonWebToken token = null; + try + { + JsonWebTokenHandler tokenHandler = new JsonWebTokenHandler(); + token = tokenHandler.ReadJsonWebToken(attestationToken); + } + catch (ArgumentException argumentException) + { + throw new AlwaysEncryptedAttestationException(String.Format(SR.FailToParseAttestationToken, argumentException.Message)); + } + + // Get all the claims from the token + Dictionary claims = new Dictionary(); + foreach (Claim claim in token.Claims.ToList()) + { + claims.Add(claim.Type, claim.Value); + } + + // Get Enclave held data claim and validate it with the Base64UrlEncode(enclave public key) + ValidateClaim(claims, "aas-ehd", enclavePublicKey.PublicKey); + + if (enclaveType == EnclaveType.Vbs) + { + // Get rp_data claim and validate it with the Base64UrlEncode(nonce) + ValidateClaim(claims, "rp_data", nonce); + } + } + + /// + /// Validate the claim value against the actual data + /// + /// Collection of all the claims in the JWT + /// Claim to validate + /// Data to validate against the claim + private void ValidateClaim(Dictionary claims, string claimName, byte[] actualData) + { + // Get required claim data + string claimData; + bool hasClaim = claims.TryGetValue(claimName, out claimData); + if (!hasClaim) + { + throw new AlwaysEncryptedAttestationException(String.Format(SR.MissingClaimInAttestationToken, claimName)); + } + + // Get the Base64Url of the actual data and compare it with claim + string encodedActualData = string.Empty; + try + { + encodedActualData = Base64UrlEncoder.Encode(actualData); + } + catch (Exception) + { + throw new AlwaysEncryptedAttestationException(SR.InvalidArgumentToBase64UrlDecoder); + } + + bool hasValidClaim = String.Equals(encodedActualData, claimData, StringComparison.InvariantCultureIgnoreCase); + if (!hasValidClaim) + { + throw new AlwaysEncryptedAttestationException(String.Format(SR.InvalidClaimInAttestationToken, claimName, claimData)); + } + } + + /// + /// Derives the shared secret between the client and enclave. + /// + /// The enclave's RSA public key + /// Nonce value send by client during attestation + /// Type of Enclave which we are attesting + /// The enclave's DiffieHellman key and signature info + /// The client's DiffieHellman key info + /// A byte buffer containing the shared secret + private byte[] GetSharedSecret(EnclavePublicKey enclavePublicKey, byte[] nonce, EnclaveType enclaveType, EnclaveDiffieHellmanInfo enclaveDHInfo, ECDiffieHellmanCng clientDHKey) + { + byte[] enclaveRsaPublicKey = enclavePublicKey.PublicKey; + + // For SGX enclave we Sql server sends the enclave public key XOR'ed with Nonce. + // In case if Sql server replayed old JWT then shared secret will not match and hence client will not able to determine the updated enclave keys. + if (enclaveType == EnclaveType.Sgx) + { + for (int iterator = 0; iterator < enclaveRsaPublicKey.Length; iterator++) + { + enclaveRsaPublicKey[iterator] = (byte)(enclaveRsaPublicKey[iterator] ^ nonce[iterator % nonce.Length]); + } + } + + // Perform signature verification. The enclave's DiffieHellman public key was signed by the enclave's RSA public key. + CngKey cngkey = CngKey.Import(enclaveRsaPublicKey, CngKeyBlobFormat.GenericPublicBlob); + using (RSACng rsacng = new RSACng(cngkey)) + { + if (!rsacng.VerifyData(enclaveDHInfo.PublicKey, enclaveDHInfo.PublicKeySignature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)) + { + throw new ArgumentException(SR.GetSharedSecretFailed); + } + } + + CngKey key = CngKey.Import(enclaveDHInfo.PublicKey, CngKeyBlobFormat.GenericPublicBlob); + return clientDHKey.DeriveKeyMaterial(key); + } + #endregion + } +} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs new file mode 100644 index 0000000000..b34f3b2ac9 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs @@ -0,0 +1,261 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +using System; + +using System.Runtime.Caching; +using System.Security.Cryptography; +using System.Text; +using System.Threading; + + +// Enclave session locking model +// 1. For doing the enclave attestation, driver makes either 1, 2 or 3 API calls(in order) +// - GetEnclaveSession +// - GetAttestationParameters +// - CreateEnclaveSession + +// First API call when an enclave session is cached. +// First 2 API calls when we are running a non-enclave query with no session cached. +// All 3 API calls in order when you run an enclave query with no session cached. + +// In case if the enclave session is cached and validate(not expired), GetEnclaveSession API returns the SqlEnclaveSession. +// In case if the enclave session is not cached then driver end up calling GetAttestationParameters and CreateEnclaveSession. +// Note: When we have non-enclave query, then in those cases we never call CreateEnclaveSession. This is one of main pivot point for designing the below locking model. +// As per current API design driver passes attestation url and the server name during GetEnclaveSession but not in GetAttestationParameters. +// In order to create the attestation parameter enclave provider needs to know the attestation url. To overcome this limitation we added a AttestationInfoCache memory cache +// which save the attestation url and nonce with current thread as the lookup key. +// Later we use the AttestationInfoCache object to retrieve the attestation url in GetAttestationParameters which we saved during CreateEnclaveSession call. + +// 2. In case during application start, if app spins of multiple threads at the same time (during stress test or benchmarking) where DB connection is Always encrypted enabled, +// then with the existing design we end up creating multiple enclave session. Each enclave session adds an extra memory overhead to the system and also it generates multiple calls to attestation +// service, which customer may be paying for. +// Current design try to collapse multiple GetEnclaveSession calls into a single call to CreateEnclaveSession.We achieve this goal by introducing a lock in GetEnclaveSession +// such that when we have some outstanding call doing the attestation, then all the other call to GetEnclaveSession wait for the ongoing attestation loop to complete. +// To avoid infinite thread starvation, we also added a lock timeout. +// If the ongoing attestation request completes successfully, then it creates the enclave session and release the lock so that all the subsequent request reads uses the cached value. +// In cases if the network is extremely slow and the lock timeout expires before the current ongoing attestation complete, in those cases we end up triggering the enclave attestation +// on the current thread. + +// Scenario (1) +// we have 2 threads, where the both threads require enclave computation. +// When thread one invokes GetEnclaveSession then it successfully sets the event (as its the first request in the system). +// Later when the 2nd thread comes along then it gets blocked on sessionLockEvent.WaitOne till attestation on thread 1 completes or event timeout. + +// case 1: Attestation on thread 1 completes before event timeout happens +// In this case thread 1 signal the event after completing the attestation and save the enclave session value. +// Thread 2 gets the event signaled and read the cache enclave session value and return. + +// case 2: Attestation on thread 1 does not complete before lock time happens +// In this case thread 1 is unable to signal on time. Hence thread 2 starts its own attestation process and reduces the timeout to 0 so that any +// further request doesn't get block on timeout. +// If the attestation request on either thread completes it will signal the event and reset the time out to default value. +// In this case if we have multiple threads (say n threads) waiting for the attestation request to complete and it never completes on the first thread then we end up making n attestation requests. + +// Scenario (2) +// We have 2 threads, where first query on thread 1 does not require enclave computation but subsequent query on thread 1 does, whereas on thread 2 all query needs enclave computation. +// When thread one invokes GetEnclaveSession then it successfully sets the event (as it the first request in the system). +// Later when the 2nd thread comes along then it gets blocked on sessionLockEvent.WaitOne till attestation on thread 1 completes or event timeout. + +// Running first query on thread 1 while thread 2 waiting (no timeout) +// In this case thread 1 never signal the event (while running 1st query) as it does not require enclave computation. +// So thread 2 keeps waiting either for timeout to happen or thread 1 again comes in setup the enclave session to signal it. + +// Running second query on thread 1 while thread 2 waiting (no timeout) +// In this case thread 1 don't have to wait for event as it’s already did it while running 1st query. +// Now thread 2 keeps waiting either for timeout to happen or thread 1 finish up setting the session. + +namespace Microsoft.Data.SqlClient +{ + /// + /// Base class for Enclave provider + /// + public abstract class EnclaveProviderBase : SqlColumnEncryptionEnclaveProvider + { + #region Constants + private const int NonceSize = 256; + private const int AttestationInfoCacheTimeoutInMinutes = 10; + private const int LockTimeoutMaxInMilliseconds = 15 * 1000; // 15 seconds + #endregion + + #region Members + private static readonly EnclaveSessionCache SessionCache = new EnclaveSessionCache(); + + private static AutoResetEvent sessionLockEvent = new AutoResetEvent(true); + + private static int lockTimeoutInMilliseconds = LockTimeoutMaxInMilliseconds; + + private static bool isSessionLockAcquired = false; + + private static readonly Object lockUpdateSessionLock = new Object(); + + internal class AttestationInfoCacheItem + { + public string AttestationUrl { get; private set; } + + public byte[] AttestNonce { get; private set; } + + public AttestationInfoCacheItem(string attestationUri, byte[] nonce) + { + AttestationUrl = attestationUri; + AttestNonce = nonce; + } + } + + /// + /// It is used to save the attestation url and nonce value across API calls + /// + protected static readonly MemoryCache AttestationInfoCache = new MemoryCache("AttestationInfoCache"); + #endregion + + #region Public methods + /// + /// Helper method to get the enclave session from the cache if present + /// + /// The name of the SQL Server instance containing the enclave. + /// The endpoint of an attestation service, SqlClient contacts to attest the enclave. + /// True, if we want to create the nonce value. true for AAS + /// When this method returns, the requested enclave session or null if the provider doesn't implement session caching. This parameter is treated as uninitialized. + /// A counter that the enclave provider is expected to increment each time SqlClient retrieves the session from the cache. The purpose of this field is to prevent replay attacks. + protected void GetEnclaveSessionHelper(string servername, string attestationUrl, bool shouldGenerateNonce, out SqlEnclaveSession sqlEnclaveSession, out long counter) + { + sqlEnclaveSession = SessionCache.GetEnclaveSession(servername, attestationUrl, out counter); + if (sqlEnclaveSession == null) + { + bool sessionCacheLockTaken = false; + bool sameThreadRetry = false; + + // In case if on some thread we are running SQL workload which don't require attestation, then in those cases we don't want same thread to wait for event to be signaled. + // hence skipping it + AttestationInfoCacheItem attestationInfoCacheItem = AttestationInfoCache[Thread.CurrentThread.ManagedThreadId.ToString()] as AttestationInfoCacheItem; + if (attestationInfoCacheItem != null) + { + sameThreadRetry = true; + } + else + { + // We are explicitly not signalling the event here, as we want to hold the event till driver calls CreateEnclaveSession + // If we signal the event now, then multiple thread end up calling GetAttestationParameters which triggers the attestation workflow. + sessionCacheLockTaken = sessionLockEvent.WaitOne(lockTimeoutInMilliseconds); + + if (sessionCacheLockTaken) + { + lock (lockUpdateSessionLock) + { + isSessionLockAcquired = true; + } + } + } + + // In case of multi-threaded application, first thread will set the event and all the subsequent threads will wait here either until the enclave + // session is created or timeout happens. + if (sessionCacheLockTaken || sameThreadRetry) + { + // While the current thread is waiting for event to be signaled and in the meanwhile we already completed the attestation on different thread + // then we need to signal the event here + sqlEnclaveSession = SessionCache.GetEnclaveSession(servername, attestationUrl, out counter); + if (sqlEnclaveSession != null && !sameThreadRetry) + { + lock (lockUpdateSessionLock) + { + isSessionLockAcquired = false; + sessionLockEvent.Set(); + } + } + } + else + { + // In case if we are unable to signal the event, then it represents either + // 1. On other thread we have an ongoing attestation request which is taking more time may due to slow network or + // 2. Current workload doesn't require enclave computation due to which driver is not invoking the CreateEnclaveSession, hence sqlEnclaveSession is never set. + // In both cases we need to reduce the timeout to 0 so that subsequent request should not wait. + Interlocked.Exchange(ref lockTimeoutInMilliseconds, 0); + } + + if (sqlEnclaveSession == null) + { + if (!sameThreadRetry) + { + // Client decides to initiate the process of attesting the enclave and to establish a secure session with the enclave. + // To ensure that server send new attestation request instead of replaying / re-sending the old token, we will create a nonce for current attestation request. + byte[] nonce = new byte[NonceSize]; + if (shouldGenerateNonce) + { + using (RandomNumberGenerator rng = new RNGCryptoServiceProvider()) + { + rng.GetBytes(nonce); + } + } + + attestationInfoCacheItem = new AttestationInfoCacheItem(attestationUrl, nonce); + } + + AttestationInfoCache.Set(Thread.CurrentThread.ManagedThreadId.ToString(), attestationInfoCacheItem, DateTime.UtcNow.AddMinutes(AttestationInfoCacheTimeoutInMinutes)); + } + } + } + + /// + /// Reset the session lock status + /// + /// + protected void UpdateEnclaveSessionLockStatus(SqlEnclaveSession sqlEnclaveSession) + { + // As per current design, we want to minimize the number of create session calls. To acheive this we block all the GetEnclaveSession calls until the first call to + // GetEnclaveSession -> GetAttestationParameters -> CreateEnclaveSession completes or the event timeout happens. + // Case 1: When the first request successfully creates the session, then all outstanding GetEnclaveSession will use the current session. + // Case 2: When the first request unable to create the encalve session (may be due to some error or the first request doesn't require enclave computation) then in those case we set the event timeout to 0. + if (sqlEnclaveSession != null && isSessionLockAcquired) + { + lock (lockUpdateSessionLock) + { + if (isSessionLockAcquired) + { + isSessionLockAcquired = false; + Interlocked.Exchange(ref lockTimeoutInMilliseconds, LockTimeoutMaxInMilliseconds); + sessionLockEvent.Set(); + } + } + } + } + + /// + /// Helper method to remove the enclave session from the cache + /// + /// The name of the SQL Server instance containing the enclave. + /// The endpoint of an attestation service, SqlClient contacts to attest the enclave. + /// + protected void InvalidateEnclaveSessionHelper(string servername, string attestationUrl, SqlEnclaveSession enclaveSessionToInvalidate) + { + SessionCache.InvalidateSession(servername, attestationUrl, enclaveSessionToInvalidate); + } + + /// + /// Helper method for getting the enclave session from the session cache + /// + /// The endpoint of an attestation service, SqlClient contacts to attest the enclave. + /// The name of the SQL Server instance containing the enclave. + /// A counter that the enclave provider is expected to increment each time SqlClient retrieves the session from the cache. The purpose of this field is to prevent replay attacks. + /// + protected SqlEnclaveSession GetEnclaveSessionFromCache(string attestationUrl, string servername, out long counter) + { + return SessionCache.GetEnclaveSession(servername, attestationUrl, out counter); + } + + /// + /// Helper method for adding the enclave session to the session cache + /// + /// The endpoint of an attestation service, SqlClient contacts to attest the enclave. + /// The name of the SQL Server instance containing the enclave. + /// Shared secret computed using enclave ECDH public key + /// Session id for the current enclave session + /// A counter that the enclave provider is expected to increment each time SqlClient retrieves the session from the cache. The purpose of this field is to prevent replay attacks. + /// + protected SqlEnclaveSession AddEnclaveSessionToCache(string attestationUrl, string servername, byte[] sharedSecret, long sessionId, out long counter) + { + return SessionCache.CreateSession(attestationUrl, servername, sharedSecret, sessionId, out counter); + } + } + #endregion +} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveSessionCache.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveSessionCache.cs new file mode 100644 index 0000000000..d20afe0c4e --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveSessionCache.cs @@ -0,0 +1,103 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +using System; +using System.Runtime.Caching; +using System.Threading; + +namespace Microsoft.Data.SqlClient +{ + /// + /// Maintains a cache of SqlEnclaveSession instances + /// + internal class EnclaveSessionCache + { + private readonly MemoryCache enclaveMemoryCache = new MemoryCache("EnclaveMemoryCache"); + private readonly Object enclaveCacheLock = new Object(); + + /// + /// Nonce for each message sent by the client to the server to prevent replay attacks by the server, + /// given that for Always Encrypted scenarios, the server is considered an "untrusted" man-in-the-middle. + /// + private long _counter; + + /// + /// Cache timeout of 8 hours to be consistent with jwt validity. + /// + private static int enclaveCacheTimeOutInHours = 8; + + /// + /// Retrieves a SqlEnclaveSession from the cache + /// + /// + /// + /// + public SqlEnclaveSession GetEnclaveSession(string servername, string attestationUrl, out long counter) + { + string cacheKey = GenerateCacheKey(servername, attestationUrl); + SqlEnclaveSession enclaveSession = enclaveMemoryCache[cacheKey] as SqlEnclaveSession; + counter = Interlocked.Increment(ref _counter); + return enclaveSession; + } + + /// + /// Invalidates a SqlEnclaveSession entry in the cache + /// + /// + /// + /// + public void InvalidateSession(string serverName, string enclaveAttestationUrl, SqlEnclaveSession enclaveSessionToInvalidate) + { + string cacheKey = GenerateCacheKey(serverName, enclaveAttestationUrl); + + lock (enclaveCacheLock) + { + long counter; + SqlEnclaveSession enclaveSession = GetEnclaveSession(serverName, enclaveAttestationUrl, out counter); + + if (enclaveSession != null && enclaveSession.SessionId == enclaveSessionToInvalidate.SessionId) + { + SqlEnclaveSession enclaveSessionRemoved = enclaveMemoryCache.Remove(cacheKey) as SqlEnclaveSession; + if (enclaveSessionRemoved == null) + { + throw new InvalidOperationException(SR.EnclaveSessionInvalidationFailed); + } + } + } + } + + /// + /// Creates a new SqlEnclaveSession and adds it to the cache + /// + /// + /// + /// + /// + /// + public SqlEnclaveSession CreateSession(string attestationUrl, string serverName, byte[] sharedSecret, long sessionId, out long counter) + { + string cacheKey = GenerateCacheKey(serverName, attestationUrl); + SqlEnclaveSession enclaveSession = null; + lock (enclaveCacheLock) + { + enclaveSession = new SqlEnclaveSession(sharedSecret, sessionId); + enclaveMemoryCache.Add(cacheKey, enclaveSession, DateTime.UtcNow.AddHours(enclaveCacheTimeOutInHours)); + counter = Interlocked.Increment(ref _counter); + } + + return enclaveSession; + } + + /// + /// Generates the cache key for the enclave session cache + /// + /// + /// + /// + private string GenerateCacheKey(string serverName, string attestationUrl) + { + return (serverName + attestationUrl).ToLowerInvariant(); + } + } +} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.cs new file mode 100644 index 0000000000..63b8a76fad --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.cs @@ -0,0 +1,545 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Runtime.Serialization.Json; +using System.Security.Cryptography.X509Certificates; +using System.Threading; + +namespace Microsoft.Data.SqlClient +{ + /// + /// Implementation of an Enclave provider for Windows Virtual Secure Mode enclaves + /// + public class HostGuardianServiceEnclaveProvider : VirtualizationBasedSecurityEnclaveProviderBase + { + #region Members + + // this is endpoint given to us by HGS team from windows + private const string AttestationUrlSuffix = @"/v2.0/signingCertificates"; + + /// + /// + /// + public uint MaxNumRetries; + + private int enclaveRetrySleepInSeconds = 3; + + /// + /// + /// + public int EnclaveRetrySleepInSeconds + { + get + { + return enclaveRetrySleepInSeconds; + } + set + { + if (value < 1) + { + throw new ArgumentException(SR.EnclaveRetrySleepInSecondsValueException); + } + + enclaveRetrySleepInSeconds = value; + } + } + + #endregion + + #region Private helpers + + + /// + /// Return the endpoint for given attestation url + /// + /// The url to alter for corresponding provider + /// altered url + protected override string GetAttestationUrl(string attestationUrl) + { + return attestationUrl.TrimEnd('/') + AttestationUrlSuffix; + } + + /// + /// Makes a web request to the provided url and returns the response as a byte[] + /// + /// The url to make the request to + /// The response as a byte[] + protected override byte[] MakeRequest(string url) + { + Exception exception = null; + + for (int n = 0; n < MaxNumRetries + 1 /* Initial attempt + numRetries */; n++) + { + try + { + if (n != 0) + { + Thread.Sleep(EnclaveRetrySleepInSeconds * 1000); + } + + WebRequest request = WebRequest.Create(url); + + using (WebResponse response = request.GetResponse()) + using (Stream stream = response.GetResponseStream()) + { + var deserializer = new DataContractJsonSerializer(typeof(byte[])); + return (byte[])deserializer.ReadObject(stream); + } + } + catch (Exception e) + { + exception = e; + } + } + + throw new AlwaysEncryptedAttestationException(String.Format(SR.GetAttestationSigningCertificateRequestFailedFormat, url, exception.Message), exception); + } + + #endregion + } + + #region Models + + /// + /// A model class respresenting the deserialization of the byte payload the client + /// receives from SQL Server while setting up a session. + /// + internal class AttestationInfo + { + public uint TotalSize { get; set; } + + /// + /// The enclave's RSA Public Key. + /// Needed to establish trust of the enclave. + /// Used to verify the enclave's DiffieHellman info. + /// + public EnclavePublicKey Identity { get; set; } + + /// + /// The SQL Server host's health report the server received from the attestation service + /// and forwarded to the client. + /// Needed to establish trust of the enclave report received from SQL Server. + /// Used to verify the enclave report's signature. + /// + public HealthReport HealthReport { get; set; } + + /// + /// The enclave report from the SQL Server host's enclave. + /// + public EnclaveReportPackage EnclaveReportPackage { get; set; } + + /// + /// The id of the current session. + /// Needed to set up a secure session between the client and enclave. + /// + public long SessionId { get; set; } + + /// + /// The DiffieHellman public key and signature of SQL Server host's enclave. + /// Needed to set up a secure session between the client and enclave. + /// + public EnclaveDiffieHellmanInfo EnclaveDHInfo { get; set; } + + public AttestationInfo(byte[] attestationInfo) + { + int offset = 0; + + TotalSize = BitConverter.ToUInt32(attestationInfo, offset); + offset += sizeof(uint); + + int identitySize = BitConverter.ToInt32(attestationInfo, offset); + offset += sizeof(uint); + + int healthReportSize = BitConverter.ToInt32(attestationInfo, offset); + offset += sizeof(uint); + + int enclaveReportSize = BitConverter.ToInt32(attestationInfo, offset); + offset += sizeof(uint); + + byte[] identityBuffer = attestationInfo.Skip(offset).Take(identitySize).ToArray(); + Identity = new EnclavePublicKey(identityBuffer); + offset += identitySize; + + byte[] healthReportBuffer = attestationInfo.Skip(offset).Take(healthReportSize).ToArray(); + HealthReport = new HealthReport(healthReportBuffer); + offset += healthReportSize; + + byte[] enclaveReportBuffer = attestationInfo.Skip(offset).Take(enclaveReportSize).ToArray(); + EnclaveReportPackage = new EnclaveReportPackage(enclaveReportBuffer); + offset += EnclaveReportPackage.GetSizeInPayload(); + + uint secureSessionInfoResponseSize = BitConverter.ToUInt32(attestationInfo, offset); + offset += sizeof(uint); + + SessionId = BitConverter.ToInt64(attestationInfo, offset); + offset += sizeof(long); + + int secureSessionBufferSize = Convert.ToInt32(secureSessionInfoResponseSize) - sizeof(uint); + byte[] secureSessionBuffer = attestationInfo.Skip(offset).Take(secureSessionBufferSize).ToArray(); + EnclaveDHInfo = new EnclaveDiffieHellmanInfo(secureSessionBuffer); + offset += Convert.ToInt32(EnclaveDHInfo.Size); + } + } + + /// + /// A model class to hold the SQL Server's host health report in an X509Certificate2 + /// + internal class HealthReport + { + private int Size { get; set; } + + public X509Certificate2 Certificate { get; set; } + + public HealthReport(byte[] payload) + { + Size = payload.Length; + Certificate = new X509Certificate2(payload); + } + + public int GetSizeInPayload() + { + return Size; + } + } + + /// + /// A managed model representing the output of EnclaveGetAttestationReport + /// https://msdn.microsoft.com/en-us/library/windows/desktop/mt844233(v=vs.85).aspx + /// + internal class EnclaveReportPackage + { + private int Size { get; set; } + + public EnclaveReportPackageHeader PackageHeader { get; set; } + + public EnclaveReport Report { get; set; } + + public List Modules { get; set; } + + public byte[] ReportAsBytes { get; set; } + + public byte[] SignatureBlob { get; set; } + + public EnclaveReportPackage(byte[] payload) + { + Size = payload.Length; + + int offset = 0; + PackageHeader = new EnclaveReportPackageHeader(payload.Skip(offset).ToArray()); + offset += PackageHeader.GetSizeInPayload(); + + Report = new EnclaveReport(payload.Skip(offset).ToArray()); + offset += Report.GetSizeInPayload(); + + // Modules are not used for anything currently, ignore parsing for now + // + // Modules = new List(); + // int reportSizeRemaining = Convert.ToInt32(Report.ReportSize) - Report.GetSizeInPayload(); + // while (reportSizeRemaining > 0) + // { + // var module = new VSMEnclaveReportModule(payload.Skip(offset).ToArray()); + // Modules.Add(module); + // reportSizeRemaining -= module.GetSizeInPayload(); + // offset += module.GetSizeInPayload(); + // } + + // Moving the offset back to the start of the report, + // we need the report as a byte buffer for signature verification. + offset = PackageHeader.GetSizeInPayload(); + int dataToHashSize = Convert.ToInt32(PackageHeader.SignedStatementSize); + ReportAsBytes = payload.Skip(offset).Take(dataToHashSize).ToArray(); + offset += dataToHashSize; + + int signatureSize = Convert.ToInt32(PackageHeader.SignatureSize); + SignatureBlob = payload.Skip(offset).Take(signatureSize).ToArray(); + offset += signatureSize; + } + + public int GetSizeInPayload() + { + return Size; + } + } + + /// + /// A managed model of struct VBS_ENCLAVE_REPORT_PKG_HEADER + /// https://msdn.microsoft.com/en-us/library/windows/desktop/mt844257(v=vs.85).aspx + /// + internal class EnclaveReportPackageHeader + { + public uint PackageSize { get; set; } + + public uint Version { get; set; } + + public uint SignatureScheme { get; set; } + + public uint SignedStatementSize { get; set; } + + public uint SignatureSize { get; set; } + + public uint Reserved { get; set; } + + public EnclaveReportPackageHeader(byte[] payload) + { + int offset = 0; + PackageSize = BitConverter.ToUInt32(payload, offset); + offset += sizeof(uint); + + Version = BitConverter.ToUInt32(payload, offset); + offset += sizeof(uint); + + SignatureScheme = BitConverter.ToUInt32(payload, offset); + offset += sizeof(uint); + + SignedStatementSize = BitConverter.ToUInt32(payload, offset); + offset += sizeof(uint); + + SignatureSize = BitConverter.ToUInt32(payload, offset); + offset += sizeof(uint); + + Reserved = BitConverter.ToUInt32(payload, offset); + offset += sizeof(uint); + } + + public int GetSizeInPayload() + { + return 6 * sizeof(uint); + } + } + + /// + /// A managed model of struct VBS_ENCLAVE_REPORT + /// https://msdn.microsoft.com/en-us/library/windows/desktop/mt844255(v=vs.85).aspx + /// + internal class EnclaveReport + { + private int Size { get; set; } + + public uint ReportSize { get; set; } + + public uint ReportVersion { get; set; } + + public byte[] EnclaveData { get; set; } + + private const int EnclaveDataLength = 64; + + public EnclaveIdentity Identity { get; set; } + + public EnclaveReport(byte[] payload) + { + Size = payload.Length; + + int offset = 0; + + ReportSize = BitConverter.ToUInt32(payload, offset); + offset += sizeof(uint); + + ReportVersion = BitConverter.ToUInt32(payload, offset); + offset += sizeof(uint); + + EnclaveData = payload.Skip(offset).Take(EnclaveDataLength).ToArray(); + offset += EnclaveDataLength; + + Identity = new EnclaveIdentity(payload.Skip(offset).ToArray()); + offset += Identity.GetSizeInPayload(); + } + + public int GetSizeInPayload() + { + return sizeof(uint) * 2 + sizeof(byte) * 64 + Identity.GetSizeInPayload(); + } + } + + /// + /// A managed model of struct ENCLAVE_IDENTITY + /// https://msdn.microsoft.com/en-us/library/windows/desktop/mt844239(v=vs.85).aspx + /// + internal class EnclaveIdentity + { + private int Size { get; set; } + + private static readonly int ImageEnclaveLongIdLength = 32; + + private static readonly int ImageEnclaveShortIdLength = 16; + + public byte[] OwnerId = new byte[ImageEnclaveLongIdLength]; + + public byte[] UniqueId = new byte[ImageEnclaveLongIdLength]; + + public byte[] AuthorId = new byte[ImageEnclaveLongIdLength]; + + public byte[] FamilyId = new byte[ImageEnclaveShortIdLength]; + + public byte[] ImageId = new byte[ImageEnclaveShortIdLength]; + + public uint EnclaveSvn { get; set; } + + public uint SecureKernelSvn { get; set; } + + public uint PlatformSvn { get; set; } + + public uint Flags { get; set; } + + public uint SigningLevel { get; set; } + + public uint Reserved { get; set; } + + public EnclaveIdentity() { } + + public EnclaveIdentity(byte[] payload) + { + Size = payload.Length; + + int offset = 0; + + int ownerIdLength = ImageEnclaveLongIdLength; + OwnerId = payload.Skip(offset).Take(ownerIdLength).ToArray(); + offset += ownerIdLength; + + int uniqueIdLength = ImageEnclaveLongIdLength; + UniqueId = payload.Skip(offset).Take(uniqueIdLength).ToArray(); + offset += uniqueIdLength; + + int authorIdLength = ImageEnclaveLongIdLength; + AuthorId = payload.Skip(offset).Take(authorIdLength).ToArray(); + offset += authorIdLength; + + int familyIdLength = ImageEnclaveShortIdLength; + FamilyId = payload.Skip(offset).Take(familyIdLength).ToArray(); + offset += familyIdLength; + + int imageIdLength = ImageEnclaveShortIdLength; + ImageId = payload.Skip(offset).Take(imageIdLength).ToArray(); + offset += imageIdLength; + + EnclaveSvn = BitConverter.ToUInt32(payload, offset); + offset += sizeof(uint); + + SecureKernelSvn = BitConverter.ToUInt32(payload, offset); + offset += sizeof(uint); + + PlatformSvn = BitConverter.ToUInt32(payload, offset); + offset += sizeof(uint); + + Flags = BitConverter.ToUInt32(payload, offset); + offset += sizeof(uint); + + SigningLevel = BitConverter.ToUInt32(payload, offset); + offset += sizeof(uint); + + Reserved = BitConverter.ToUInt32(payload, offset); + offset += sizeof(uint); + } + + public int GetSizeInPayload() + { + return sizeof(byte) * ImageEnclaveLongIdLength * 3 + sizeof(byte) * ImageEnclaveShortIdLength * 2 + sizeof(uint) * 6; + } + } + + /// + /// A managed model of struct VBS_ENCLAVE_REPORT_VARDATA_HEADER + /// https://msdn.microsoft.com/en-us/library/windows/desktop/mt827065(v=vs.85).aspx + /// + internal class EnclaveReportModuleHeader + { + public uint DataType { get; set; } + + public uint ModuleSize { get; set; } + + public EnclaveReportModuleHeader(byte[] payload) + { + int offset = 0; + DataType = BitConverter.ToUInt32(payload, offset); + offset += sizeof(uint); + + ModuleSize = BitConverter.ToUInt32(payload, offset); + offset += sizeof(uint); + } + + public int GetSizeInPayload() + { + return 2 * sizeof(uint); + } + } + + /// + /// A managed model of struct VBS_ENCLAVE_REPORT_MODULE + /// https://msdn.microsoft.com/en-us/library/windows/desktop/mt844256(v=vs.85).aspx + /// + internal class EnclaveReportModule + { + private static readonly int ImageEnclaveLongIdLength = 32; + + private static readonly int ImageEnclaveShortIdLength = 16; + + public EnclaveReportModuleHeader Header { get; set; } + + public byte[] UniqueId = new byte[ImageEnclaveLongIdLength]; + + public byte[] AuthorId = new byte[ImageEnclaveLongIdLength]; + + public byte[] FamilyId = new byte[ImageEnclaveShortIdLength]; + + public byte[] ImageId = new byte[ImageEnclaveShortIdLength]; + + public uint Svn { get; set; } + + public string ModuleName { get; set; } + + public EnclaveReportModule(byte[] payload) + { + int offset = 0; + Header = new EnclaveReportModuleHeader(payload); + offset += Convert.ToInt32(Header.GetSizeInPayload()); + + int uniqueIdLength = ImageEnclaveLongIdLength; + UniqueId = payload.Skip(offset).Take(uniqueIdLength).ToArray(); + offset += uniqueIdLength; + + int authorIdLength = ImageEnclaveLongIdLength; + AuthorId = payload.Skip(offset).Take(authorIdLength).ToArray(); + offset += authorIdLength; + + int familyIdLength = ImageEnclaveShortIdLength; + FamilyId = payload.Skip(offset).Take(familyIdLength).ToArray(); + offset += familyIdLength; + + int imageIdLength = ImageEnclaveShortIdLength; + ImageId = payload.Skip(offset).Take(familyIdLength).ToArray(); + offset += imageIdLength; + + Svn = BitConverter.ToUInt32(payload, offset); + offset += sizeof(uint); + + int strLen = Convert.ToInt32(Header.ModuleSize) - offset; + ModuleName = BitConverter.ToString(payload, offset, 1); + offset += sizeof(char) * 1; + } + + public int GetSizeInPayload() + { + return Header.GetSizeInPayload() + Convert.ToInt32(Header.ModuleSize); + } + } + + /// + /// An enum representing the Flags property of ENCLAVE_IDENTITY + /// https://msdn.microsoft.com/en-us/library/windows/desktop/mt844239(v=vs.85).aspx + /// + internal enum EnclaveIdentityFlags + { + ENCLAVE_FLAG_NONE = 0x00000000, + ENCLAVE_FLAG_FULL_DEBUG_ENABLED = 0x00000001, + ENCLAVE_FLAG_DYNAMIC_DEBUG_ENABLED = 0x00000002, + ENCLAVE_FLAG_DYNAMIC_DEBUG_ACTIVE = 0x00000004 + } + + #endregion +} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs new file mode 100644 index 0000000000..9e1775e13c --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs @@ -0,0 +1,450 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +using System; +using System.Data.SqlClient; +using System.IO; +using System.Linq; +using System.Net; +using System.Runtime.Caching; +using System.Runtime.Serialization.Json; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Threading; + +namespace Microsoft.Data.SqlClient +{ + /// + /// + /// + public abstract class VirtualizationBasedSecurityEnclaveProviderBase : EnclaveProviderBase + { + #region Members + + private static readonly MemoryCache rootSigningCertificateCache = new MemoryCache("RootSigningCertificateCache"); + + #endregion + + #region Constants + + private const int DiffieHellmanKeySize = 384; + private const int VsmHGSProtocolId = 3; + + /// + /// ENCLAVE_IDENTITY related constants + /// + private static readonly EnclaveIdentity ExpectedPolicy = new EnclaveIdentity() + { + OwnerId = new byte[] + { + 0x10, 0x20, 0x30, 0x40, 0x41, 0x31, 0x21, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, + + UniqueId = new byte[] { }, + + // This field is calculated as follows: + // "Fixed Microsoft GUID" = {845319A6-706C-47BC-A7E8-5137B0BC750D} + // CN of the certificate that signed the file + // Opus Info - Authenticated Attribute that contains a Name and a URL + // + // In our case the Opus Info is: + // Description: Microsoft SQL Server Always Encrypted VBS Enclave Library, + // Description URL: https://go.microsoft.com/fwlink/?linkid=2018716 + AuthorId = new byte[] + { + 0x04, 0x37, 0xCA, 0xE2, 0x53, 0x7D, 0x8B, 0x9B, + 0x07, 0x76, 0xB6, 0x1B, 0x11, 0xE6, 0xCE, 0xD3, + 0xD2, 0x32, 0xE9, 0x30, 0x8F, 0x60, 0xE2, 0x1A, + 0xDA, 0xB2, 0xFD, 0x91, 0xE3, 0xDA, 0x95, 0x98 + }, + + FamilyId = new byte[] + { + 0xFE, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, + + ImageId = new byte[] + { + // This value should be same as defined in sqlserver code Sql/Ntdbms/aetm/enclave/dllmain.cpp + 0x19, 0x17, 0x12, 0x00, 0x01, 0x05, 0x20, 0x13, + 0x00, 0x05, 0x14, 0x03, 0x12, 0x01, 0x22, 0x05 + }, + + EnclaveSvn = 0, + + SecureKernelSvn = 0, + + PlatformSvn = 1, + + // 0: ENCLAVE_VBS_FLAG_NO_DEBUG in ds_main; Flag does not permit debug enclaves + Flags = 0, + + SigningLevel = 0, + + Reserved = 0 + }; + + #endregion + + #region Public methods + + /// + /// 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. + /// + /// The name of the SQL Server instance containing the enclave. + /// The endpoint of an attestation service, SqlClient contacts to attest the enclave. + /// When this method returns, the requested enclave session or null if the provider doesn't implement session caching. This parameter is treated as uninitialized. + /// A counter that the enclave provider is expected to increment each time SqlClient retrieves the session from the cache. The purpose of this field is to prevent replay attacks. + public override void GetEnclaveSession(string servername, string attestationUrl, out SqlEnclaveSession sqlEnclaveSession, out long counter) + { + GetEnclaveSessionHelper(servername, attestationUrl, false, out sqlEnclaveSession, out counter); + } + + /// + /// Gets the information that SqlClient subsequently uses to initiate the process of attesting the enclave and to establish a secure session with the enclave. + /// + /// The information SqlClient subsequently uses to initiate the process of attesting the enclave and to establish a secure session with the enclave. + public override SqlEnclaveAttestationParameters GetAttestationParameters() + { + ECDiffieHellmanCng clientDHKey = new ECDiffieHellmanCng(DiffieHellmanKeySize); + clientDHKey.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash; + clientDHKey.HashAlgorithm = CngAlgorithm.Sha256; + return new SqlEnclaveAttestationParameters(VsmHGSProtocolId, 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. + /// + /// The information the provider uses to attest the enclave and generate a symmetric key for the session. The format of this information is specific to the enclave attestation protocol. + /// A Diffie-Hellman algorithm object that encapsulates a client-side key pair. + /// The endpoint of an attestation service for attesting the enclave. + /// The name of the SQL Server instance containing the enclave. + /// The requested enclave session or null if the provider doesn't implement session caching. + /// A counter that the enclave provider is expected to increment each time SqlClient retrieves the session from the cache. The purpose of this field is to prevent replay attacks. + public override void CreateEnclaveSession(byte[] attestationInfo, ECDiffieHellmanCng clientDHKey, string attestationUrl, string servername, out SqlEnclaveSession sqlEnclaveSession, out long counter) + { + sqlEnclaveSession = null; + counter = 0; + try + { + AttestationInfoCacheItem attestationInfoCacheItem = AttestationInfoCache.Remove(Thread.CurrentThread.ManagedThreadId.ToString()) as AttestationInfoCacheItem; + sqlEnclaveSession = GetEnclaveSessionFromCache(servername, attestationUrl, out counter); + if (sqlEnclaveSession == null) + { + if (attestationInfoCacheItem != null) + { + // Deserialize the payload + AttestationInfo info = new AttestationInfo(attestationInfo); + + // Verify enclave policy matches expected policy + VerifyEnclavePolicy(info.EnclaveReportPackage); + + // Perform Attestation per VSM protocol + VerifyAttestationInfo(attestationUrl, info.HealthReport, info.EnclaveReportPackage); + + // Set up shared secret and validate signature + byte[] sharedSecret = GetSharedSecret(info.Identity, info.EnclaveDHInfo, clientDHKey); + + // add session to cache + sqlEnclaveSession = AddEnclaveSessionToCache(attestationUrl, servername, sharedSecret, info.SessionId, out counter); + } + else + { + throw new AlwaysEncryptedAttestationException(SR.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 name of the SQL Server instance containing the enclave. + /// The endpoint of an attestation service, SqlClient contacts to attest the enclave. + /// The session to be invalidated. + public override void InvalidateEnclaveSession(string serverName, string enclaveAttestationUrl, SqlEnclaveSession enclaveSessionToInvalidate) + { + InvalidateEnclaveSessionHelper(serverName, enclaveAttestationUrl, enclaveSessionToInvalidate); + } + + #endregion + + #region Private helpers + + /// + /// Performs Attestation per the protocol used by Virtual Secure Modules. + /// + /// Url of the attestation service + /// The health report about the SQL Server host + /// The enclave report about the SQL Server host's enclave + private void VerifyAttestationInfo(string attestationUrl, HealthReport healthReport, EnclaveReportPackage enclaveReportPackage) + { + bool shouldRetryValidation; + bool shouldForceUpdateSigningKeys = false; + do + { + shouldRetryValidation = false; + + // Get HGS Root signing certs from HGS + X509Certificate2Collection signingCerts = GetSigningCertificate(attestationUrl, shouldForceUpdateSigningKeys); + + // Verify SQL Health report root chain of trust is the HGS root signing cert + X509ChainStatusFlags chainStatus = VerifyHealthReportAgainstRootCertificate(signingCerts, healthReport.Certificate); + if (chainStatus != X509ChainStatusFlags.NoError) + { + // In cases if we fail to validate the health report, it might be possible that we are using old signing keys + // let's re-download the signing keys again and re-validate the health report + if (!shouldForceUpdateSigningKeys) + { + shouldForceUpdateSigningKeys = true; + shouldRetryValidation = true; + } + else + { + throw new AlwaysEncryptedAttestationException(String.Format(SR.VerifyHealthCertificateChainFormat, attestationUrl, chainStatus)); + } + } + } while (shouldRetryValidation); + + // Verify enclave report is signed by IDK_S from health report + VerifyEnclaveReportSignature(enclaveReportPackage, healthReport.Certificate); + } + + /// + /// Makes a web request to the provided url and returns the response as a byte[] + /// + /// The url to make the request to + /// The response as a byte[] + protected abstract byte[] MakeRequest(string url); + + /// + /// Gets the root signing certificate for the provided attestation service. + /// If the certificate does not exist in the cache, this will make a call to the + /// attestation service's "/signingCertificates" endpoint. This endpoint can + /// return multiple certificates if the attestation service consists + /// of multiple nodes. + /// + /// Url of attestation service + /// Re-download the signing certificate irrespective of caching + /// The root signing certificate(s) for the attestation service + private X509Certificate2Collection GetSigningCertificate(string attestationUrl, bool forceUpdate) + { + attestationUrl = GetAttestationUrl(attestationUrl); + X509Certificate2Collection signingCertificates = (X509Certificate2Collection)rootSigningCertificateCache[attestationUrl]; + if (forceUpdate || signingCertificates == null || AnyCertificatesExpired(signingCertificates)) + { + byte[] data = MakeRequest(attestationUrl); + var certificateCollection = new X509Certificate2Collection(); + + try + { + certificateCollection.Import(data); + } + catch (CryptographicException exception) + { + throw new AlwaysEncryptedAttestationException(String.Format(SR.GetAttestationSigningCertificateFailedInvalidCertificate, attestationUrl), exception); + } + + rootSigningCertificateCache.Add(attestationUrl, certificateCollection, DateTime.Now.AddDays(1)); + } + + return (X509Certificate2Collection)rootSigningCertificateCache[attestationUrl]; + } + + /// + /// Return the endpoint for given attestation url + /// + /// The url to alter for corresponding provider + /// altered url + protected abstract string GetAttestationUrl(string attestationUrl); + + /// + /// Checks if any certificates in the collection are expired + /// + /// A collection of certificates + /// true if any certificates or expired, false otherwise + private bool AnyCertificatesExpired(X509Certificate2Collection certificates) + { + return certificates.OfType().Any(c => c.NotAfter < DateTime.Now); + } + + /// + /// Verifies that a chain of trust can be built from the health report provided + /// by SQL Server and the attestation service's root signing certificate(s). + /// + /// The root signing certificate(s) of the attestation service + /// The health report about the SQL Server host in the form of an X509Certificate2 + /// An X509ChainStatusFlags indicating why the chain failed to build + private X509ChainStatusFlags VerifyHealthReportAgainstRootCertificate(X509Certificate2Collection signingCerts, X509Certificate2 healthReportCert) + { + var chain = new X509Chain(); + + foreach (var cert in signingCerts) + { + chain.ChainPolicy.ExtraStore.Add(cert); + } + + chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; + + if (!chain.Build(healthReportCert)) + { + bool untrustedRoot = false; + + // iterate over the chain status to check why the build failed + foreach (X509ChainStatus status in chain.ChainStatus) + { + if (status.Status == X509ChainStatusFlags.UntrustedRoot) + { + untrustedRoot = true; + } + else + { + return status.Status; + } + } + + // if the chain failed with untrusted root, this could be because the client doesn't have the root cert + // installed. If the chain's untrusted root cert has the same thumbprint as the signing cert, then we + // do trust it. + if (untrustedRoot) + { + // iterate through the certificate chain, starting at the root since it's likely the + // signing certificate is the root + for (int i = 0; i < chain.ChainElements.Count; i++) + { + X509ChainElement element = chain.ChainElements[chain.ChainElements.Count - 1 - i]; + + foreach (X509Certificate2 cert in signingCerts) + { + if (element.Certificate.Thumbprint == cert.Thumbprint) + { + return X509ChainStatusFlags.NoError; + } + } + } + + // in the case where we didn't find matching thumbprint + return X509ChainStatusFlags.UntrustedRoot; + } + } + + return X509ChainStatusFlags.NoError; + } + + /// + /// Verifies the enclave report signature using the health report. + /// + /// The enclave report about the SQL Server host's enclave + /// The health report about the SQL Server host in the form of an X509Certificate2 + private void VerifyEnclaveReportSignature(EnclaveReportPackage enclaveReportPackage, X509Certificate2 healthReportCert) + { + // Check if report is formatted correctly + UInt32 calculatedSize = Convert.ToUInt32(enclaveReportPackage.PackageHeader.GetSizeInPayload()) + enclaveReportPackage.PackageHeader.SignedStatementSize + enclaveReportPackage.PackageHeader.SignatureSize; + + if (calculatedSize != enclaveReportPackage.PackageHeader.PackageSize) + { + throw new ArgumentException(SR.VerifyEnclaveReportFormatFailed); + } + + // IDK_S is contained in healthReport cert public key + RSA rsacsp = healthReportCert.GetRSAPublicKey(); + RSAParameters rsaparams = rsacsp.ExportParameters(includePrivateParameters: false); + RSACng rsacng = new RSACng(); + rsacng.ImportParameters(rsaparams); + + if (!rsacng.VerifyData(enclaveReportPackage.ReportAsBytes, enclaveReportPackage.SignatureBlob, HashAlgorithmName.SHA256, RSASignaturePadding.Pss)) + { + throw new ArgumentException(SR.VerifyEnclaveReportFailed); + + } + } + + /// + /// Verifies the enclave policy matches expected policy. + /// + /// The enclave report about the SQL Server host's enclave + private void VerifyEnclavePolicy(EnclaveReportPackage enclaveReportPackage) + { + EnclaveIdentity identity = enclaveReportPackage.Report.Identity; + + VerifyEnclavePolicyProperty("OwnerId", identity.OwnerId, ExpectedPolicy.OwnerId); + VerifyEnclavePolicyProperty("AuthorId", identity.AuthorId, ExpectedPolicy.AuthorId); + VerifyEnclavePolicyProperty("FamilyId", identity.FamilyId, ExpectedPolicy.FamilyId); + VerifyEnclavePolicyProperty("ImageId", identity.ImageId, ExpectedPolicy.ImageId); + VerifyEnclavePolicyProperty("EnclaveSvn", identity.EnclaveSvn, ExpectedPolicy.EnclaveSvn); + VerifyEnclavePolicyProperty("SecureKernelSvn", identity.SecureKernelSvn, ExpectedPolicy.SecureKernelSvn); + VerifyEnclavePolicyProperty("PlatformSvn", identity.PlatformSvn, ExpectedPolicy.PlatformSvn); + + + // This is a check that the enclave is running without debug support or not. + // + if (identity.Flags != ExpectedPolicy.Flags) + { + throw new InvalidOperationException(SR.VerifyEnclaveDebuggable); + } + } + + /// + /// Verifies a byte[] enclave policy property + /// + /// The enclave property name + /// The actual enclave property from the enclave report + /// The expected enclave property + private void VerifyEnclavePolicyProperty(string property, byte[] actual, byte[] expected) + { + if (!actual.SequenceEqual(expected)) + { + string exceptionMessage = String.Format(SR.VerifyEnclavePolicyFailedFormat, property, BitConverter.ToString(actual), BitConverter.ToString(expected)); + throw new ArgumentException(exceptionMessage); + } + } + + /// + /// Verifies a uint enclave policy property + /// + /// The enclave property name + /// The actual enclave property from the enclave report + /// The expected enclave property + private void VerifyEnclavePolicyProperty(string property, uint actual, uint expected) + { + if (actual < expected) + { + string exceptionMessage = String.Format(SR.VerifyEnclavePolicyFailedFormat, property, actual, expected); + throw new ArgumentException(exceptionMessage); + } + } + + /// + /// Derives the shared secret between the client and enclave. + /// + /// The enclave's RSA public key + /// The enclave's DiffieHellman key and signature info + /// The client's DiffieHellman key info + /// A byte buffer containing the shared secret + private byte[] GetSharedSecret(EnclavePublicKey enclavePublicKey, EnclaveDiffieHellmanInfo enclaveDHInfo, ECDiffieHellmanCng clientDHKey) + { + // Perform signature verification. The enclave's DiffieHellman public key was signed by the enclave's RSA public key. + CngKey cngkey = CngKey.Import(enclavePublicKey.PublicKey, CngKeyBlobFormat.GenericPublicBlob); + RSACng rsacng = new RSACng(cngkey); + if (!rsacng.VerifyData(enclaveDHInfo.PublicKey, enclaveDHInfo.PublicKeySignature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)) + { + throw new ArgumentException(SR.GetSharedSecretFailed); + } + + CngKey key = CngKey.Import(enclaveDHInfo.PublicKey, CngKeyBlobFormat.GenericPublicBlob); + return clientDHKey.DeriveKeyMaterial(key); + } + + #endregion + } +} From 6dbc68d798338379a28eb9205ea70447dd626876 Mon Sep 17 00:00:00 2001 From: Karina Zhou Date: Fri, 11 Oct 2019 10:27:57 -0700 Subject: [PATCH 13/63] Add dependencies --- .../netfx/src/Microsoft.Data.SqlClient.csproj | 5 +---- tools/specs/Microsoft.Data.SqlClient.nuspec | 2 ++ 2 files changed, 3 insertions(+), 4 deletions(-) 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 cd1514b745..9bebdecfc9 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -352,10 +352,7 @@ 5.5.0 - - 5.5.0 - - + 5.5.0 diff --git a/tools/specs/Microsoft.Data.SqlClient.nuspec b/tools/specs/Microsoft.Data.SqlClient.nuspec index 5e085630cf..decbb9f59f 100644 --- a/tools/specs/Microsoft.Data.SqlClient.nuspec +++ b/tools/specs/Microsoft.Data.SqlClient.nuspec @@ -30,6 +30,8 @@ When using NuGet 3.x this package requires at least version 3.4. + + From c640569a83bdb2608902daeb2d193cfe79e21c29 Mon Sep 17 00:00:00 2001 From: Karina Zhou Date: Fri, 11 Oct 2019 13:12:08 -0700 Subject: [PATCH 14/63] Add enclave provider files in netcoreapp only --- .../netcore/src/Microsoft.Data.SqlClient.csproj | 14 +++++++------- ...aysEncryptedAttestationException.NetCoreApp.cs} | 0 ...aysEncryptedEnclaveProviderUtils.NetCoreApp.cs} | 0 ...eAttestationBasedEnclaveProvider.NetCoreApp.cs} | 0 ...erBase.cs => EnclaveProviderBase.NetCoreApp.cs} | 0 ...nCache.cs => EnclaveSessionCache.NetCoreApp.cs} | 0 ...VirtualSecureModeEnclaveProvider.NetCoreApp.cs} | 0 ...ualSecureModeEnclaveProviderBase.NetCoreApp.cs} | 0 8 files changed, 7 insertions(+), 7 deletions(-) rename src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/{AlwaysEncryptedAttestationException.cs => AlwaysEncryptedAttestationException.NetCoreApp.cs} (100%) rename src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/{AlwaysEncryptedEnclaveProviderUtils.cs => AlwaysEncryptedEnclaveProviderUtils.NetCoreApp.cs} (100%) rename src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/{AzureAttestationBasedEnclaveProvider.cs => AzureAttestationBasedEnclaveProvider.NetCoreApp.cs} (100%) rename src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/{EnclaveProviderBase.cs => EnclaveProviderBase.NetCoreApp.cs} (100%) rename src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/{EnclaveSessionCache.cs => EnclaveSessionCache.NetCoreApp.cs} (100%) rename src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/{VirtualSecureModeEnclaveProvider.cs => VirtualSecureModeEnclaveProvider.NetCoreApp.cs} (100%) rename src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/{VirtualSecureModeEnclaveProviderBase.cs => VirtualSecureModeEnclaveProviderBase.NetCoreApp.cs} (100%) 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 5807421265..e5b4a97311 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -45,6 +45,13 @@ + + + + + + + @@ -573,13 +580,6 @@ - - - - - - - True True diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.NetCoreApp.cs similarity index 100% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.cs rename to src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.NetCoreApp.cs diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.NetCoreApp.cs similarity index 100% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs rename to src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.NetCoreApp.cs diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.NetCoreApp.cs similarity index 100% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs rename to src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.NetCoreApp.cs diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveProviderBase.NetCoreApp.cs similarity index 100% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs rename to src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveProviderBase.NetCoreApp.cs diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveSessionCache.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveSessionCache.NetCoreApp.cs similarity index 100% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveSessionCache.cs rename to src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveSessionCache.NetCoreApp.cs diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.NetCoreApp.cs similarity index 100% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.cs rename to src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.NetCoreApp.cs diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.NetCoreApp.cs similarity index 100% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs rename to src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.NetCoreApp.cs From 579c5f22e7830e312242070cd636b8bd03f88c3f Mon Sep 17 00:00:00 2001 From: Karina Zhou Date: Fri, 11 Oct 2019 15:35:27 -0700 Subject: [PATCH 15/63] make attestation stuff internal --- .../AlwaysEncryptedAttestationException.NetCoreApp.cs | 2 +- .../AlwaysEncryptedEnclaveProviderUtils.NetCoreApp.cs | 6 +++--- .../AzureAttestationBasedEnclaveProvider.NetCoreApp.cs | 2 +- .../Data/SqlClient/EnclaveProviderBase.NetCoreApp.cs | 2 +- .../VirtualSecureModeEnclaveProvider.NetCoreApp.cs | 4 ++-- .../VirtualSecureModeEnclaveProviderBase.NetCoreApp.cs | 2 +- .../Data/SqlClient/AlwaysEncryptedAttestationException.cs | 2 +- .../Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs | 6 +++--- .../Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs | 2 +- .../src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs | 2 +- .../Data/SqlClient/VirtualSecureModeEnclaveProvider.cs | 4 ++-- .../Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs | 2 +- 12 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.NetCoreApp.cs index 22a3145135..4d1e11c0c4 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.NetCoreApp.cs @@ -8,7 +8,7 @@ namespace Microsoft.Data.SqlClient /// /// Represents errors occuring during an Always Encrypted secure enclave operation /// - public class AlwaysEncryptedAttestationException : Exception + internal class AlwaysEncryptedAttestationException : Exception { /// /// diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.NetCoreApp.cs index 5de07c002c..0e8e2c336f 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.NetCoreApp.cs @@ -8,7 +8,7 @@ namespace Microsoft.Data.SqlClient /// /// Class to hold the enclave's RSA public key /// - public class EnclavePublicKey + internal class EnclavePublicKey { /// /// @@ -28,7 +28,7 @@ public EnclavePublicKey(byte[] payload) /// /// Class to hold the Enclave's Diffie-Hellman public key and signature /// - public class EnclaveDiffieHellmanInfo + internal class EnclaveDiffieHellmanInfo { /// /// @@ -71,7 +71,7 @@ public EnclaveDiffieHellmanInfo(byte[] payload) /// /// /// - public enum EnclaveType + internal enum EnclaveType { /// /// diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.NetCoreApp.cs index 795523f9d7..b0ce663cc5 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.NetCoreApp.cs @@ -50,7 +50,7 @@ namespace Microsoft.Data.SqlClient /// /// Implementation of an Enclave provider (both for Sgx and Vsm) with Azure Attestation /// - public class AzureAttestationEnclaveProvider : EnclaveProviderBase + internal class AzureAttestationEnclaveProvider : EnclaveProviderBase { #region Constants private const int DiffieHellmanKeySize = 384; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveProviderBase.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveProviderBase.NetCoreApp.cs index b34f3b2ac9..7b0f61d2f4 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveProviderBase.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveProviderBase.NetCoreApp.cs @@ -71,7 +71,7 @@ namespace Microsoft.Data.SqlClient /// /// Base class for Enclave provider /// - public abstract class EnclaveProviderBase : SqlColumnEncryptionEnclaveProvider + internal abstract class EnclaveProviderBase : SqlColumnEncryptionEnclaveProvider { #region Constants private const int NonceSize = 256; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.NetCoreApp.cs index 63b8a76fad..0df96a0994 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.NetCoreApp.cs @@ -16,7 +16,7 @@ namespace Microsoft.Data.SqlClient /// /// Implementation of an Enclave provider for Windows Virtual Secure Mode enclaves /// - public class HostGuardianServiceEnclaveProvider : VirtualizationBasedSecurityEnclaveProviderBase + internal class HostGuardianServiceEnclaveProvider : VirtualizationBasedSecurityEnclaveProviderBase { #region Members @@ -26,7 +26,7 @@ public class HostGuardianServiceEnclaveProvider : VirtualizationBasedSecurityEnc /// /// /// - public uint MaxNumRetries; + public uint MaxNumRetries { get; set; } private int enclaveRetrySleepInSeconds = 3; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.NetCoreApp.cs index 9e1775e13c..7b750cf5ff 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.NetCoreApp.cs @@ -18,7 +18,7 @@ namespace Microsoft.Data.SqlClient /// /// /// - public abstract class VirtualizationBasedSecurityEnclaveProviderBase : EnclaveProviderBase + internal abstract class VirtualizationBasedSecurityEnclaveProviderBase : EnclaveProviderBase { #region Members diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.cs index 7716a91539..f9e4e4be97 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.cs @@ -7,7 +7,7 @@ namespace Microsoft.Data.SqlClient { /// - public class AlwaysEncryptedAttestationException : Exception + internal class AlwaysEncryptedAttestationException : Exception { /// public AlwaysEncryptedAttestationException(string message, Exception innerException) : base(message, innerException) { } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs index ebd70821fe..feb776e315 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs @@ -11,7 +11,7 @@ namespace Microsoft.Data.SqlClient /// Class to hold the enclave's RSA public key /// /// - public class EnclavePublicKey + internal class EnclavePublicKey { /// /// @@ -29,7 +29,7 @@ public EnclavePublicKey(byte[] payload) /// /// Class to hold the Enclave's Diffie-Hellman public key and signature /// - public class EnclaveDiffieHellmanInfo + internal class EnclaveDiffieHellmanInfo { /// /// @@ -72,7 +72,7 @@ public EnclaveDiffieHellmanInfo(byte[] payload) /// /// /// - public enum EnclaveType + internal enum EnclaveType { /// /// diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs index 020f0bcb20..451adf08c9 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs @@ -47,7 +47,7 @@ namespace Microsoft.Data.SqlClient /// /// Implementation of an Enclave provider (both for Sgx and Vsm) with Azure Attestation /// - public class AzureAttestationEnclaveProvider : EnclaveProviderBase + internal class AzureAttestationEnclaveProvider : EnclaveProviderBase { #region Constants private const int DiffieHellmanKeySize = 384; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs index 917bd90af4..e8342e792c 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs @@ -69,7 +69,7 @@ namespace Microsoft.Data.SqlClient /// /// Base class for Enclave provider /// - public abstract class EnclaveProviderBase : SqlColumnEncryptionEnclaveProvider + internal abstract class EnclaveProviderBase : SqlColumnEncryptionEnclaveProvider { #region Constants private const int NonceSize = 256; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.cs index 7e1b75cd91..ab7e1018fb 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.cs @@ -16,7 +16,7 @@ namespace Microsoft.Data.SqlClient /// /// Implementation of an Enclave provider for Windows Virtual Secure Mode enclaves /// - public class HostGuardianServiceEnclaveProvider : VirtualizationBasedSecurityEnclaveProviderBase + internal class HostGuardianServiceEnclaveProvider : VirtualizationBasedSecurityEnclaveProviderBase { #region Members @@ -26,7 +26,7 @@ public class HostGuardianServiceEnclaveProvider : VirtualizationBasedSecurityEnc /// /// To be added. /// - public int MaxNumRetries; + public int MaxNumRetries { get; set; } private int enclaveRetrySleepInSeconds = 3; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs index 7a6be7ed54..3117fd26ef 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs @@ -14,7 +14,7 @@ namespace Microsoft.Data.SqlClient /// /// To be added. /// - public abstract class VirtualizationBasedSecurityEnclaveProviderBase : EnclaveProviderBase + internal abstract class VirtualizationBasedSecurityEnclaveProviderBase : EnclaveProviderBase { #region Members From a343aa472a77b41a239adfe5122372434cf837a6 Mon Sep 17 00:00:00 2001 From: v-jizho2 Date: Fri, 11 Oct 2019 16:41:28 -0700 Subject: [PATCH 16/63] make attestation stuff internal (#259) --- .../AlwaysEncryptedAttestationException.NetCoreApp.cs | 2 +- .../AlwaysEncryptedEnclaveProviderUtils.NetCoreApp.cs | 6 +++--- .../AzureAttestationBasedEnclaveProvider.NetCoreApp.cs | 2 +- .../Data/SqlClient/EnclaveProviderBase.NetCoreApp.cs | 2 +- .../VirtualSecureModeEnclaveProvider.NetCoreApp.cs | 4 ++-- .../VirtualSecureModeEnclaveProviderBase.NetCoreApp.cs | 2 +- .../Data/SqlClient/AlwaysEncryptedAttestationException.cs | 2 +- .../Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs | 6 +++--- .../Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs | 2 +- .../src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs | 2 +- .../Data/SqlClient/VirtualSecureModeEnclaveProvider.cs | 4 ++-- .../Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs | 2 +- 12 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.NetCoreApp.cs index 22a3145135..4d1e11c0c4 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.NetCoreApp.cs @@ -8,7 +8,7 @@ namespace Microsoft.Data.SqlClient /// /// Represents errors occuring during an Always Encrypted secure enclave operation /// - public class AlwaysEncryptedAttestationException : Exception + internal class AlwaysEncryptedAttestationException : Exception { /// /// diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.NetCoreApp.cs index 5de07c002c..0e8e2c336f 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.NetCoreApp.cs @@ -8,7 +8,7 @@ namespace Microsoft.Data.SqlClient /// /// Class to hold the enclave's RSA public key /// - public class EnclavePublicKey + internal class EnclavePublicKey { /// /// @@ -28,7 +28,7 @@ public EnclavePublicKey(byte[] payload) /// /// Class to hold the Enclave's Diffie-Hellman public key and signature /// - public class EnclaveDiffieHellmanInfo + internal class EnclaveDiffieHellmanInfo { /// /// @@ -71,7 +71,7 @@ public EnclaveDiffieHellmanInfo(byte[] payload) /// /// /// - public enum EnclaveType + internal enum EnclaveType { /// /// diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.NetCoreApp.cs index 795523f9d7..b0ce663cc5 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.NetCoreApp.cs @@ -50,7 +50,7 @@ namespace Microsoft.Data.SqlClient /// /// Implementation of an Enclave provider (both for Sgx and Vsm) with Azure Attestation /// - public class AzureAttestationEnclaveProvider : EnclaveProviderBase + internal class AzureAttestationEnclaveProvider : EnclaveProviderBase { #region Constants private const int DiffieHellmanKeySize = 384; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveProviderBase.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveProviderBase.NetCoreApp.cs index b34f3b2ac9..7b0f61d2f4 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveProviderBase.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveProviderBase.NetCoreApp.cs @@ -71,7 +71,7 @@ namespace Microsoft.Data.SqlClient /// /// Base class for Enclave provider /// - public abstract class EnclaveProviderBase : SqlColumnEncryptionEnclaveProvider + internal abstract class EnclaveProviderBase : SqlColumnEncryptionEnclaveProvider { #region Constants private const int NonceSize = 256; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.NetCoreApp.cs index 63b8a76fad..0df96a0994 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.NetCoreApp.cs @@ -16,7 +16,7 @@ namespace Microsoft.Data.SqlClient /// /// Implementation of an Enclave provider for Windows Virtual Secure Mode enclaves /// - public class HostGuardianServiceEnclaveProvider : VirtualizationBasedSecurityEnclaveProviderBase + internal class HostGuardianServiceEnclaveProvider : VirtualizationBasedSecurityEnclaveProviderBase { #region Members @@ -26,7 +26,7 @@ public class HostGuardianServiceEnclaveProvider : VirtualizationBasedSecurityEnc /// /// /// - public uint MaxNumRetries; + public uint MaxNumRetries { get; set; } private int enclaveRetrySleepInSeconds = 3; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.NetCoreApp.cs index 9e1775e13c..7b750cf5ff 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.NetCoreApp.cs @@ -18,7 +18,7 @@ namespace Microsoft.Data.SqlClient /// /// /// - public abstract class VirtualizationBasedSecurityEnclaveProviderBase : EnclaveProviderBase + internal abstract class VirtualizationBasedSecurityEnclaveProviderBase : EnclaveProviderBase { #region Members diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.cs index 7716a91539..f9e4e4be97 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.cs @@ -7,7 +7,7 @@ namespace Microsoft.Data.SqlClient { /// - public class AlwaysEncryptedAttestationException : Exception + internal class AlwaysEncryptedAttestationException : Exception { /// public AlwaysEncryptedAttestationException(string message, Exception innerException) : base(message, innerException) { } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs index ebd70821fe..feb776e315 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs @@ -11,7 +11,7 @@ namespace Microsoft.Data.SqlClient /// Class to hold the enclave's RSA public key /// /// - public class EnclavePublicKey + internal class EnclavePublicKey { /// /// @@ -29,7 +29,7 @@ public EnclavePublicKey(byte[] payload) /// /// Class to hold the Enclave's Diffie-Hellman public key and signature /// - public class EnclaveDiffieHellmanInfo + internal class EnclaveDiffieHellmanInfo { /// /// @@ -72,7 +72,7 @@ public EnclaveDiffieHellmanInfo(byte[] payload) /// /// /// - public enum EnclaveType + internal enum EnclaveType { /// /// diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs index 020f0bcb20..451adf08c9 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs @@ -47,7 +47,7 @@ namespace Microsoft.Data.SqlClient /// /// Implementation of an Enclave provider (both for Sgx and Vsm) with Azure Attestation /// - public class AzureAttestationEnclaveProvider : EnclaveProviderBase + internal class AzureAttestationEnclaveProvider : EnclaveProviderBase { #region Constants private const int DiffieHellmanKeySize = 384; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs index 917bd90af4..e8342e792c 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs @@ -69,7 +69,7 @@ namespace Microsoft.Data.SqlClient /// /// Base class for Enclave provider /// - public abstract class EnclaveProviderBase : SqlColumnEncryptionEnclaveProvider + internal abstract class EnclaveProviderBase : SqlColumnEncryptionEnclaveProvider { #region Constants private const int NonceSize = 256; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.cs index 7e1b75cd91..ab7e1018fb 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.cs @@ -16,7 +16,7 @@ namespace Microsoft.Data.SqlClient /// /// Implementation of an Enclave provider for Windows Virtual Secure Mode enclaves /// - public class HostGuardianServiceEnclaveProvider : VirtualizationBasedSecurityEnclaveProviderBase + internal class HostGuardianServiceEnclaveProvider : VirtualizationBasedSecurityEnclaveProviderBase { #region Members @@ -26,7 +26,7 @@ public class HostGuardianServiceEnclaveProvider : VirtualizationBasedSecurityEnc /// /// To be added. /// - public int MaxNumRetries; + public int MaxNumRetries { get; set; } private int enclaveRetrySleepInSeconds = 3; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs index 7a6be7ed54..3117fd26ef 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs @@ -14,7 +14,7 @@ namespace Microsoft.Data.SqlClient /// /// To be added. /// - public abstract class VirtualizationBasedSecurityEnclaveProviderBase : EnclaveProviderBase + internal abstract class VirtualizationBasedSecurityEnclaveProviderBase : EnclaveProviderBase { #region Members From 4a102096f434427338ab4022f2879ce4a0604771 Mon Sep 17 00:00:00 2001 From: Javad Date: Tue, 15 Oct 2019 13:43:05 -0700 Subject: [PATCH 17/63] Taking public API documentation out --- ...ncryptedAttestationException.NetCoreApp.cs | 36 +--- ...ncryptedEnclaveProviderUtils.NetCoreApp.cs | 47 +---- ...estationBasedEnclaveProvider.NetCoreApp.cs | 196 +++++------------- .../EnclaveProviderBase.NetCoreApp.cs | 58 +----- .../EnclaveSessionCache.NetCoreApp.cs | 50 +---- ...ualSecureModeEnclaveProvider.NetCoreApp.cs | 115 ++++------ ...ecureModeEnclaveProviderBase.NetCoreApp.cs | 132 +++--------- ...rectoryAuthenticationTimeoutRetryHelper.cs | 2 - .../AlwaysEncryptedAttestationException.cs | 4 - .../AlwaysEncryptedEnclaveProviderUtils.cs | 38 +--- .../AzureAttestationBasedEnclaveProvider.cs | 110 ++++------ .../Data/SqlClient/EnclaveProviderBase.cs | 47 +---- .../Data/SqlClient/EnclaveSessionCache.cs | 44 +--- .../VirtualSecureModeEnclaveProvider.cs | 108 +++------- .../VirtualSecureModeEnclaveProviderBase.cs | 123 ++--------- 15 files changed, 259 insertions(+), 851 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.NetCoreApp.cs index 4d1e11c0c4..cc652a2e1a 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.NetCoreApp.cs @@ -1,31 +1,17 @@ -using System; -using System.Collections.Generic; -using System.Text; +// 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; namespace Microsoft.Data.SqlClient { - - /// - /// Represents errors occuring during an Always Encrypted secure enclave operation - /// - internal class AlwaysEncryptedAttestationException : Exception - { - /// - /// - /// - /// - /// - public AlwaysEncryptedAttestationException(string message, Exception innerException) : base(message, innerException) { } + internal class AlwaysEncryptedAttestationException : Exception + { + public AlwaysEncryptedAttestationException(string message, Exception innerException) : base(message, innerException) { } - /// - /// - /// - /// - public AlwaysEncryptedAttestationException(string message) : base(message) { } + public AlwaysEncryptedAttestationException(string message) : base(message) { } - /// - /// - /// - public AlwaysEncryptedAttestationException() : base() { } - } + public AlwaysEncryptedAttestationException() : base() { } + } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.NetCoreApp.cs index 0e8e2c336f..53b3d14b81 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.NetCoreApp.cs @@ -1,54 +1,31 @@ -using System; -using System.Collections.Generic; +// 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.Linq; -using System.Text; namespace Microsoft.Data.SqlClient { - /// - /// Class to hold the enclave's RSA public key - /// internal class EnclavePublicKey { - /// - /// - /// public byte[] PublicKey { get; set; } - /// - /// - /// - /// public EnclavePublicKey(byte[] payload) { PublicKey = payload; } } - /// - /// Class to hold the Enclave's Diffie-Hellman public key and signature - /// + // Class to hold the Enclave's Diffie-Hellman public key and signature internal class EnclaveDiffieHellmanInfo { - /// - /// - /// public int Size { get; private set; } - /// - /// - /// public byte[] PublicKey { get; private set; } - /// - /// - /// public byte[] PublicKeySignature { get; private set; } - /// - /// - /// - /// public EnclaveDiffieHellmanInfo(byte[] payload) { Size = payload.Length; @@ -68,24 +45,12 @@ public EnclaveDiffieHellmanInfo(byte[] payload) } } - /// - /// - /// internal enum EnclaveType { - /// - /// - /// None = 0, - /// - /// - /// Vbs = 1, - /// - /// - /// Sgx = 2 } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.NetCoreApp.cs index b0ce663cc5..e2df559455 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.NetCoreApp.cs @@ -1,16 +1,14 @@ -//------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -//------------------------------------------------------------ +// 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.IdentityModel.Tokens.Jwt; using System.Linq; using System.Runtime.Caching; -using System.Runtime.Serialization.Json; using System.Security.Claims; using System.Security.Cryptography; -using System.Security.Cryptography.X509Certificates; using System.Text; using System.Threading; using Microsoft.IdentityModel.JsonWebTokens; @@ -47,9 +45,7 @@ namespace Microsoft.Data.SqlClient { - /// - /// Implementation of an Enclave provider (both for Sgx and Vsm) with Azure Attestation - /// + // Implementation of an Enclave provider (both for Sgx and Vsm) with Azure Attestation internal class AzureAttestationEnclaveProvider : EnclaveProviderBase { #region Constants @@ -68,23 +64,14 @@ internal class AzureAttestationEnclaveProvider : EnclaveProviderBase #endregion #region Public methods - /// - /// 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. - /// - /// The name of the SQL Server instance containing the enclave. - /// The endpoint of an attestation service, SqlClient contacts to attest the enclave. - /// When this method returns, the requested enclave session or null if the provider doesn't implement session caching. This parameter is treated as uninitialized. - /// A counter that the enclave provider is expected to increment each time SqlClient retrieves the session from the cache. The purpose of this field is to prevent replay attacks. + // 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. public override void GetEnclaveSession(string servername, string attestationUrl, out SqlEnclaveSession sqlEnclaveSession, out long counter) { GetEnclaveSessionHelper(servername, attestationUrl, true, out sqlEnclaveSession, out counter); } - /// - /// Gets the information that SqlClient subsequently uses to initiate the process of attesting the enclave and to establish a secure session with the enclave. - /// - /// The information SqlClient subsequently uses to initiate the process of attesting the enclave and to establish a secure session with the enclave. + // Gets the information that SqlClient subsequently uses to initiate the process of attesting the enclave and to establish a secure session with the enclave. public override SqlEnclaveAttestationParameters GetAttestationParameters() { ECDiffieHellmanCng clientDHKey = new ECDiffieHellmanCng(DiffieHellmanKeySize); @@ -94,15 +81,7 @@ public override SqlEnclaveAttestationParameters GetAttestationParameters() return new SqlEnclaveAttestationParameters(AzureBasedAttestationProtocolId, attestationParam, 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. - /// - /// The information the provider uses to attest the enclave and generate a symmetric key for the session. The format of this information is specific to the enclave attestation protocol. - /// A Diffie-Hellman algorithm object that encapsulates a client-side key pair. - /// The endpoint of an attestation service for attesting the enclave. - /// The name of the SQL Server instance containing the enclave. - /// The requested enclave session or null if the provider doesn't implement session caching. - /// A counter that the enclave provider is expected to increment each time SqlClient retrieves the session from the cache. The purpose of this field is to prevent replay attacks. + // 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. public override void CreateEnclaveSession(byte[] attestationInfo, ECDiffieHellmanCng clientDHKey, string attestationUrl, string servername, out SqlEnclaveSession sqlEnclaveSession, out long counter) { sqlEnclaveSession = null; @@ -147,12 +126,7 @@ public override void CreateEnclaveSession(byte[] attestationInfo, ECDiffieHellma } } - /// - /// 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 name of the SQL Server instance containing the enclave. - /// The endpoint of an attestation service, SqlClient contacts to attest the enclave. - /// The session to be invalidated. + // When overridden in a derived class, looks up and evicts an enclave session from the enclave session cache, if the provider implements session caching. public override void InvalidateEnclaveSession(string serverName, string enclaveAttestationUrl, SqlEnclaveSession enclaveSessionToInvalidate) { InvalidateEnclaveSessionHelper(serverName, enclaveAttestationUrl, enclaveSessionToInvalidate); @@ -160,49 +134,40 @@ public override void InvalidateEnclaveSession(string serverName, string enclaveA #endregion #region Internal Class - /// - /// A model class respresenting the deserialization of the byte payload the client - /// receives from SQL Server while setting up a session. - /// Protocol format: - /// 1. Total Size of the attestation blob as UINT - /// 2. Size of Enclave RSA public key as UINT - /// 3. Size of Attestation token as UINT - /// 4. Enclave Type as UINT - /// 5. Enclave RSA public key (raw key, of length #2) - /// 6. Attestation token (of length #3) - /// 7. Size of Session Id was UINT - /// 8. Session id value - /// 9. Size of enclave ECDH public key - /// 10. Enclave ECDH public key (of length #9) - /// + + // A model class respresenting the deserialization of the byte payload the client + // receives from SQL Server while setting up a session. + // Protocol format: + // 1. Total Size of the attestation blob as UINT + // 2. Size of Enclave RSA public key as UINT + // 3. Size of Attestation token as UINT + // 4. Enclave Type as UINT + // 5. Enclave RSA public key (raw key, of length #2) + // 6. Attestation token (of length #3) + // 7. Size of Session Id was UINT + // 8. Session id value + // 9. Size of enclave ECDH public key + // 10. Enclave ECDH public key (of length #9) internal class AzureAttestationInfo { public uint TotalSize { get; set; } - /// - /// The enclave's RSA Public Key. - /// Needed to establish trust of the enclave. - /// Used to verify the enclave's DiffieHellman info. - /// + // The enclave's RSA Public Key. + // Needed to establish trust of the enclave. + // Used to verify the enclave's DiffieHellman info. public EnclavePublicKey Identity { get; set; } - /// - /// The enclave report from the SQL Server host's enclave. - /// + // The enclave report from the SQL Server host's enclave. public AzureAttestationToken AttestationToken { get; set; } - /// - /// The id of the current session. - /// Needed to set up a secure session between the client and enclave. - /// + // The id of the current session. + // Needed to set up a secure session between the client and enclave. public long SessionId { get; set; } public EnclaveType EnclaveType { get; set; } - /// - /// The DiffieHellman public key and signature of SQL Server host's enclave. - /// Needed to set up a secure session between the client and enclave. - /// + // The DiffieHellman public key and signature of SQL Server host's enclave. + // Needed to set up a secure session between the client and enclave. public EnclaveDiffieHellmanInfo EnclaveDHInfo { get; set; } public AzureAttestationInfo(byte[] attestationInfo) @@ -256,10 +221,8 @@ public AzureAttestationInfo(byte[] attestationInfo) } } - /// - /// A managed model representing the output of EnclaveGetAttestationReport - /// https://msdn.microsoft.com/en-us/library/windows/desktop/mt844233(v=vs.85).aspx - /// + // A managed model representing the output of EnclaveGetAttestationReport + // https://msdn.microsoft.com/en-us/library/windows/desktop/mt844233(v=vs.85).aspx internal class AzureAttestationToken { public string AttestationToken { get; set; } @@ -273,13 +236,11 @@ public AzureAttestationToken(byte[] payload) #endregion Internal Class #region Private helpers - /// - /// Prepare the attestation data in following format - /// Attestation Url length - /// Attestation Url - /// Size of nonce - /// Nonce value - /// + // Prepare the attestation data in following format + // Attestation Url length + // Attestation Url + // Size of nonce + // Nonce value internal byte[] PrepareAttestationParameters() { AttestationInfoCacheItem attestationInfoCacheItem = AttestationInfoCache[Thread.CurrentThread.ManagedThreadId.ToString()] as AttestationInfoCacheItem; @@ -322,14 +283,7 @@ internal byte[] PrepareAttestationParameters() } } - /// - /// Performs Attestation per the protocol used by Azure Attestation Service - /// - /// Url of the attestation service - /// Type of Enclave which we are attesting - /// The Azure enclave attestation token about the SQL Server host's enclave - /// The enclave's RSA public key - /// Nonce value send by client during attestation + // Performs Attestation per the protocol used by Azure Attestation Service private void VerifyAzureAttestationInfo(string attestationUrl, EnclaveType enclaveType, string attestationToken, EnclavePublicKey enclavePublicKey, byte[] nonce) { bool shouldForceUpdateSigningKeys = false; @@ -368,11 +322,7 @@ private void VerifyAzureAttestationInfo(string attestationUrl, EnclaveType encla ValidateAttestationClaims(enclaveType, attestationToken, enclavePublicKey, nonce); } - /// - /// Returns the innermost exception value - /// - /// Current exception value - /// + // Returns the innermost exception value private static string GetInnerMostExceptionMessage(Exception exception) { Exception exLocal = exception; @@ -384,13 +334,8 @@ private static string GetInnerMostExceptionMessage(Exception exception) return exLocal.Message; } - /// - /// For the given attestation url it downloads the token signing keys from the well-known openid configuration end point. - /// It also caches that information for 1 day to avoid DDOS attacks. - /// - /// Url of attestation service - /// Re-download the signing keys irrespective of caching - /// OpenIdConnectConfiguration object for the signing keys + // For the given attestation url it downloads the token signing keys from the well-known openid configuration end point. + // It also caches that information for 1 day to avoid DDOS attacks. private OpenIdConnectConfiguration GetOpenIdConfigForSigningKeys(string url, bool forceUpdate) { OpenIdConnectConfiguration openIdConnectConfig = OpenIdConnectConfigurationCache[url] as OpenIdConnectConfiguration; @@ -415,24 +360,16 @@ private OpenIdConnectConfiguration GetOpenIdConfigForSigningKeys(string url, boo return openIdConnectConfig; } - /// - /// Return the attestation instance url for given attestation url - /// such as for https://sql.azure.attest.com/attest/SgxEnclave?api-version=2017-11-01 - /// It will return https://sql.azure.attest.com - /// - /// Url of the attestation service - /// altered url + // Return the attestation instance url for given attestation url + // such as for https://sql.azure.attest.com/attest/SgxEnclave?api-version=2017-11-01 + // It will return https://sql.azure.attest.com private string GetAttestationInstanceUrl(string attestationUrl) { Uri attestationUri = new Uri(attestationUrl); return attestationUri.GetLeftPart(UriPartial.Authority); } - /// - /// Generate the list of valid issuer Url's (in case if tokenIssuerUrl is using default port) - /// - /// Attestation token issuer url - /// List of valid issuer urls (can't be null/empty) + // Generate the list of valid issuer Url's (in case if tokenIssuerUrl is using default port) private static ICollection GenerateListOfIssuers(string tokenIssuerUrl) { List issuerUrls = new List(); @@ -452,15 +389,7 @@ private static ICollection GenerateListOfIssuers(string tokenIssuerUrl) return issuerUrls; } - /// - /// Verifies the attestation token is signed by correct signing keys. - /// - /// Complete attestation token - /// Attestation token issuer url - /// List of attestation token issuer signing keys - /// return if token signing key is expired - /// returns exception message to the caller - /// + // Verifies the attestation token is signed by correct signing keys. private bool VerifyTokenSignature(string attestationToken, string tokenIssuerUrl, ICollection issuerSigningKeys, out bool isKeySigningExpired, out string exceptionMessage) { exceptionMessage = string.Empty; @@ -507,11 +436,7 @@ private bool VerifyTokenSignature(string attestationToken, string tokenIssuerUrl return isSignatureValid; } - /// - /// Computes the SHA256 hash of the byte array - /// - /// Input for SHA256 - /// SHA256 of the input data + // Computes the SHA256 hash of the byte array private byte[] ComputeSHA256(byte[] data) { byte[] result = null; @@ -529,13 +454,7 @@ private byte[] ComputeSHA256(byte[] data) return result; } - /// - /// Validate the claims in the attestation token - /// - /// Type of Enclave which we are attesting - /// The Azure enclave attestation token about the SQL Server host's enclave - /// The enclave's RSA public key - /// Nonce value send by client during attestation + // Validate the claims in the attestation token private void ValidateAttestationClaims(EnclaveType enclaveType, string attestationToken, EnclavePublicKey enclavePublicKey, byte[] nonce) { // Read the json token @@ -567,12 +486,7 @@ private void ValidateAttestationClaims(EnclaveType enclaveType, string attestati } } - /// - /// Validate the claim value against the actual data - /// - /// Collection of all the claims in the JWT - /// Claim to validate - /// Data to validate against the claim + // Validate the claim value against the actual data private void ValidateClaim(Dictionary claims, string claimName, byte[] actualData) { // Get required claim data @@ -601,15 +515,7 @@ private void ValidateClaim(Dictionary claims, string claimName, } } - /// - /// Derives the shared secret between the client and enclave. - /// - /// The enclave's RSA public key - /// Nonce value send by client during attestation - /// Type of Enclave which we are attesting - /// The enclave's DiffieHellman key and signature info - /// The client's DiffieHellman key info - /// A byte buffer containing the shared secret + // Derives the shared secret between the client and enclave. private byte[] GetSharedSecret(EnclavePublicKey enclavePublicKey, byte[] nonce, EnclaveType enclaveType, EnclaveDiffieHellmanInfo enclaveDHInfo, ECDiffieHellmanCng clientDHKey) { byte[] enclaveRsaPublicKey = enclavePublicKey.PublicKey; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveProviderBase.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveProviderBase.NetCoreApp.cs index 7b0f61d2f4..f252670cbc 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveProviderBase.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveProviderBase.NetCoreApp.cs @@ -1,15 +1,12 @@ -//------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -//------------------------------------------------------------ +// 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.Runtime.Caching; using System.Security.Cryptography; -using System.Text; using System.Threading; - // Enclave session locking model // 1. For doing the enclave attestation, driver makes either 1, 2 or 3 API calls(in order) // - GetEnclaveSession @@ -67,10 +64,8 @@ // Now thread 2 keeps waiting either for timeout to happen or thread 1 finish up setting the session. namespace Microsoft.Data.SqlClient -{ - /// - /// Base class for Enclave provider - /// +{ + // Base class for Enclave provider internal abstract class EnclaveProviderBase : SqlColumnEncryptionEnclaveProvider { #region Constants @@ -103,21 +98,12 @@ public AttestationInfoCacheItem(string attestationUri, byte[] nonce) } } - /// - /// It is used to save the attestation url and nonce value across API calls - /// + // It is used to save the attestation url and nonce value across API calls protected static readonly MemoryCache AttestationInfoCache = new MemoryCache("AttestationInfoCache"); #endregion #region Public methods - /// - /// Helper method to get the enclave session from the cache if present - /// - /// The name of the SQL Server instance containing the enclave. - /// The endpoint of an attestation service, SqlClient contacts to attest the enclave. - /// True, if we want to create the nonce value. true for AAS - /// When this method returns, the requested enclave session or null if the provider doesn't implement session caching. This parameter is treated as uninitialized. - /// A counter that the enclave provider is expected to increment each time SqlClient retrieves the session from the cache. The purpose of this field is to prevent replay attacks. + // Helper method to get the enclave session from the cache if present protected void GetEnclaveSessionHelper(string servername, string attestationUrl, bool shouldGenerateNonce, out SqlEnclaveSession sqlEnclaveSession, out long counter) { sqlEnclaveSession = SessionCache.GetEnclaveSession(servername, attestationUrl, out counter); @@ -196,10 +182,7 @@ protected void GetEnclaveSessionHelper(string servername, string attestationUrl, } } - /// - /// Reset the session lock status - /// - /// + // Reset the session lock status protected void UpdateEnclaveSessionLockStatus(SqlEnclaveSession sqlEnclaveSession) { // As per current design, we want to minimize the number of create session calls. To acheive this we block all the GetEnclaveSession calls until the first call to @@ -220,38 +203,19 @@ protected void UpdateEnclaveSessionLockStatus(SqlEnclaveSession sqlEnclaveSessio } } - /// - /// Helper method to remove the enclave session from the cache - /// - /// The name of the SQL Server instance containing the enclave. - /// The endpoint of an attestation service, SqlClient contacts to attest the enclave. - /// + // Helper method to remove the enclave session from the cache protected void InvalidateEnclaveSessionHelper(string servername, string attestationUrl, SqlEnclaveSession enclaveSessionToInvalidate) { SessionCache.InvalidateSession(servername, attestationUrl, enclaveSessionToInvalidate); } - /// - /// Helper method for getting the enclave session from the session cache - /// - /// The endpoint of an attestation service, SqlClient contacts to attest the enclave. - /// The name of the SQL Server instance containing the enclave. - /// A counter that the enclave provider is expected to increment each time SqlClient retrieves the session from the cache. The purpose of this field is to prevent replay attacks. - /// + // Helper method for getting the enclave session from the session cache protected SqlEnclaveSession GetEnclaveSessionFromCache(string attestationUrl, string servername, out long counter) { return SessionCache.GetEnclaveSession(servername, attestationUrl, out counter); } - /// - /// Helper method for adding the enclave session to the session cache - /// - /// The endpoint of an attestation service, SqlClient contacts to attest the enclave. - /// The name of the SQL Server instance containing the enclave. - /// Shared secret computed using enclave ECDH public key - /// Session id for the current enclave session - /// A counter that the enclave provider is expected to increment each time SqlClient retrieves the session from the cache. The purpose of this field is to prevent replay attacks. - /// + // Helper method for adding the enclave session to the session cache protected SqlEnclaveSession AddEnclaveSessionToCache(string attestationUrl, string servername, byte[] sharedSecret, long sessionId, out long counter) { return SessionCache.CreateSession(attestationUrl, servername, sharedSecret, sessionId, out counter); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveSessionCache.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveSessionCache.NetCoreApp.cs index d20afe0c4e..b4f591ae9b 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveSessionCache.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveSessionCache.NetCoreApp.cs @@ -1,6 +1,6 @@ -//------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -//------------------------------------------------------------ +// 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.Runtime.Caching; @@ -8,31 +8,20 @@ namespace Microsoft.Data.SqlClient { - /// - /// Maintains a cache of SqlEnclaveSession instances - /// + // Maintains a cache of SqlEnclaveSession instances internal class EnclaveSessionCache { private readonly MemoryCache enclaveMemoryCache = new MemoryCache("EnclaveMemoryCache"); private readonly Object enclaveCacheLock = new Object(); - /// - /// Nonce for each message sent by the client to the server to prevent replay attacks by the server, - /// given that for Always Encrypted scenarios, the server is considered an "untrusted" man-in-the-middle. - /// + // Nonce for each message sent by the client to the server to prevent replay attacks by the server, + // given that for Always Encrypted scenarios, the server is considered an "untrusted" man-in-the-middle. private long _counter; - /// - /// Cache timeout of 8 hours to be consistent with jwt validity. - /// + // Cache timeout of 8 hours to be consistent with jwt validity. private static int enclaveCacheTimeOutInHours = 8; - /// - /// Retrieves a SqlEnclaveSession from the cache - /// - /// - /// - /// + // Retrieves a SqlEnclaveSession from the cache public SqlEnclaveSession GetEnclaveSession(string servername, string attestationUrl, out long counter) { string cacheKey = GenerateCacheKey(servername, attestationUrl); @@ -41,12 +30,7 @@ public SqlEnclaveSession GetEnclaveSession(string servername, string attestation return enclaveSession; } - /// - /// Invalidates a SqlEnclaveSession entry in the cache - /// - /// - /// - /// + // Invalidates a SqlEnclaveSession entry in the cache public void InvalidateSession(string serverName, string enclaveAttestationUrl, SqlEnclaveSession enclaveSessionToInvalidate) { string cacheKey = GenerateCacheKey(serverName, enclaveAttestationUrl); @@ -67,14 +51,7 @@ public void InvalidateSession(string serverName, string enclaveAttestationUrl, S } } - /// - /// Creates a new SqlEnclaveSession and adds it to the cache - /// - /// - /// - /// - /// - /// + // Creates a new SqlEnclaveSession and adds it to the cache public SqlEnclaveSession CreateSession(string attestationUrl, string serverName, byte[] sharedSecret, long sessionId, out long counter) { string cacheKey = GenerateCacheKey(serverName, attestationUrl); @@ -89,12 +66,7 @@ public SqlEnclaveSession CreateSession(string attestationUrl, string serverName, return enclaveSession; } - /// - /// Generates the cache key for the enclave session cache - /// - /// - /// - /// + // Generates the cache key for the enclave session cache private string GenerateCacheKey(string serverName, string attestationUrl) { return (serverName + attestationUrl).ToLowerInvariant(); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.NetCoreApp.cs index 0df96a0994..8eac963a0c 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.NetCoreApp.cs @@ -1,6 +1,6 @@ -//------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -//------------------------------------------------------------ +// 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; @@ -13,9 +13,7 @@ namespace Microsoft.Data.SqlClient { - /// - /// Implementation of an Enclave provider for Windows Virtual Secure Mode enclaves - /// + // Implementation of an Enclave provider for Windows Virtual Secure Mode enclaves internal class HostGuardianServiceEnclaveProvider : VirtualizationBasedSecurityEnclaveProviderBase { #region Members @@ -23,16 +21,10 @@ internal class HostGuardianServiceEnclaveProvider : VirtualizationBasedSecurityE // this is endpoint given to us by HGS team from windows private const string AttestationUrlSuffix = @"/v2.0/signingCertificates"; - /// - /// - /// public uint MaxNumRetries { get; set; } private int enclaveRetrySleepInSeconds = 3; - /// - /// - /// public int EnclaveRetrySleepInSeconds { get @@ -54,22 +46,13 @@ public int EnclaveRetrySleepInSeconds #region Private helpers - - /// - /// Return the endpoint for given attestation url - /// - /// The url to alter for corresponding provider - /// altered url + // Return the endpoint for given attestation url protected override string GetAttestationUrl(string attestationUrl) { return attestationUrl.TrimEnd('/') + AttestationUrlSuffix; } - /// - /// Makes a web request to the provided url and returns the response as a byte[] - /// - /// The url to make the request to - /// The response as a byte[] + // Makes a web request to the provided url and returns the response as a byte[] protected override byte[] MakeRequest(string url) { Exception exception = null; @@ -106,44 +89,32 @@ protected override byte[] MakeRequest(string url) #region Models - /// - /// A model class respresenting the deserialization of the byte payload the client - /// receives from SQL Server while setting up a session. - /// + // A model class respresenting the deserialization of the byte payload the client + // receives from SQL Server while setting up a session. internal class AttestationInfo { public uint TotalSize { get; set; } - /// - /// The enclave's RSA Public Key. - /// Needed to establish trust of the enclave. - /// Used to verify the enclave's DiffieHellman info. - /// + // The enclave's RSA Public Key. + // Needed to establish trust of the enclave. + // Used to verify the enclave's DiffieHellman info. public EnclavePublicKey Identity { get; set; } - /// - /// The SQL Server host's health report the server received from the attestation service - /// and forwarded to the client. - /// Needed to establish trust of the enclave report received from SQL Server. - /// Used to verify the enclave report's signature. - /// + // The SQL Server host's health report the server received from the attestation service + // and forwarded to the client. + // Needed to establish trust of the enclave report received from SQL Server. + // Used to verify the enclave report's signature. public HealthReport HealthReport { get; set; } - /// - /// The enclave report from the SQL Server host's enclave. - /// + // The enclave report from the SQL Server host's enclave. public EnclaveReportPackage EnclaveReportPackage { get; set; } - /// - /// The id of the current session. - /// Needed to set up a secure session between the client and enclave. - /// + // The id of the current session. + // Needed to set up a secure session between the client and enclave. public long SessionId { get; set; } - /// - /// The DiffieHellman public key and signature of SQL Server host's enclave. - /// Needed to set up a secure session between the client and enclave. - /// + // The DiffieHellman public key and signature of SQL Server host's enclave. + // Needed to set up a secure session between the client and enclave. public EnclaveDiffieHellmanInfo EnclaveDHInfo { get; set; } public AttestationInfo(byte[] attestationInfo) @@ -187,9 +158,7 @@ public AttestationInfo(byte[] attestationInfo) } } - /// - /// A model class to hold the SQL Server's host health report in an X509Certificate2 - /// + // A model class to hold the SQL Server's host health report in an X509Certificate2 internal class HealthReport { private int Size { get; set; } @@ -208,10 +177,8 @@ public int GetSizeInPayload() } } - /// - /// A managed model representing the output of EnclaveGetAttestationReport - /// https://msdn.microsoft.com/en-us/library/windows/desktop/mt844233(v=vs.85).aspx - /// + // A managed model representing the output of EnclaveGetAttestationReport + // https://msdn.microsoft.com/en-us/library/windows/desktop/mt844233(v=vs.85).aspx internal class EnclaveReportPackage { private int Size { get; set; } @@ -267,10 +234,8 @@ public int GetSizeInPayload() } } - /// - /// A managed model of struct VBS_ENCLAVE_REPORT_PKG_HEADER - /// https://msdn.microsoft.com/en-us/library/windows/desktop/mt844257(v=vs.85).aspx - /// + // A managed model of struct VBS_ENCLAVE_REPORT_PKG_HEADER + // https://msdn.microsoft.com/en-us/library/windows/desktop/mt844257(v=vs.85).aspx internal class EnclaveReportPackageHeader { public uint PackageSize { get; set; } @@ -313,10 +278,8 @@ public int GetSizeInPayload() } } - /// - /// A managed model of struct VBS_ENCLAVE_REPORT - /// https://msdn.microsoft.com/en-us/library/windows/desktop/mt844255(v=vs.85).aspx - /// + // A managed model of struct VBS_ENCLAVE_REPORT + // https://msdn.microsoft.com/en-us/library/windows/desktop/mt844255(v=vs.85).aspx internal class EnclaveReport { private int Size { get; set; } @@ -356,10 +319,8 @@ public int GetSizeInPayload() } } - /// - /// A managed model of struct ENCLAVE_IDENTITY - /// https://msdn.microsoft.com/en-us/library/windows/desktop/mt844239(v=vs.85).aspx - /// + // A managed model of struct ENCLAVE_IDENTITY + // https://msdn.microsoft.com/en-us/library/windows/desktop/mt844239(v=vs.85).aspx internal class EnclaveIdentity { private int Size { get; set; } @@ -443,10 +404,8 @@ public int GetSizeInPayload() } } - /// - /// A managed model of struct VBS_ENCLAVE_REPORT_VARDATA_HEADER - /// https://msdn.microsoft.com/en-us/library/windows/desktop/mt827065(v=vs.85).aspx - /// + // A managed model of struct VBS_ENCLAVE_REPORT_VARDATA_HEADER + // https://msdn.microsoft.com/en-us/library/windows/desktop/mt827065(v=vs.85).aspx internal class EnclaveReportModuleHeader { public uint DataType { get; set; } @@ -469,10 +428,8 @@ public int GetSizeInPayload() } } - /// - /// A managed model of struct VBS_ENCLAVE_REPORT_MODULE - /// https://msdn.microsoft.com/en-us/library/windows/desktop/mt844256(v=vs.85).aspx - /// + // A managed model of struct VBS_ENCLAVE_REPORT_MODULE + // https://msdn.microsoft.com/en-us/library/windows/desktop/mt844256(v=vs.85).aspx internal class EnclaveReportModule { private static readonly int ImageEnclaveLongIdLength = 32; @@ -529,10 +486,8 @@ public int GetSizeInPayload() } } - /// - /// An enum representing the Flags property of ENCLAVE_IDENTITY - /// https://msdn.microsoft.com/en-us/library/windows/desktop/mt844239(v=vs.85).aspx - /// + // An enum representing the Flags property of ENCLAVE_IDENTITY + // https://msdn.microsoft.com/en-us/library/windows/desktop/mt844239(v=vs.85).aspx internal enum EnclaveIdentityFlags { ENCLAVE_FLAG_NONE = 0x00000000, diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.NetCoreApp.cs index 7b750cf5ff..864e7d8436 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.NetCoreApp.cs @@ -1,23 +1,16 @@ -//------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -//------------------------------------------------------------ +// 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.Data.SqlClient; -using System.IO; using System.Linq; -using System.Net; using System.Runtime.Caching; -using System.Runtime.Serialization.Json; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Threading; namespace Microsoft.Data.SqlClient { - /// - /// - /// internal abstract class VirtualizationBasedSecurityEnclaveProviderBase : EnclaveProviderBase { #region Members @@ -31,9 +24,7 @@ internal abstract class VirtualizationBasedSecurityEnclaveProviderBase : Enclave private const int DiffieHellmanKeySize = 384; private const int VsmHGSProtocolId = 3; - /// - /// ENCLAVE_IDENTITY related constants - /// + // ENCLAVE_IDENTITY related constants private static readonly EnclaveIdentity ExpectedPolicy = new EnclaveIdentity() { OwnerId = new byte[] @@ -93,23 +84,14 @@ internal abstract class VirtualizationBasedSecurityEnclaveProviderBase : Enclave #region Public methods - /// - /// 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. - /// - /// The name of the SQL Server instance containing the enclave. - /// The endpoint of an attestation service, SqlClient contacts to attest the enclave. - /// When this method returns, the requested enclave session or null if the provider doesn't implement session caching. This parameter is treated as uninitialized. - /// A counter that the enclave provider is expected to increment each time SqlClient retrieves the session from the cache. The purpose of this field is to prevent replay attacks. + // 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. public override void GetEnclaveSession(string servername, string attestationUrl, out SqlEnclaveSession sqlEnclaveSession, out long counter) { GetEnclaveSessionHelper(servername, attestationUrl, false, out sqlEnclaveSession, out counter); } - /// - /// Gets the information that SqlClient subsequently uses to initiate the process of attesting the enclave and to establish a secure session with the enclave. - /// - /// The information SqlClient subsequently uses to initiate the process of attesting the enclave and to establish a secure session with the enclave. + // Gets the information that SqlClient subsequently uses to initiate the process of attesting the enclave and to establish a secure session with the enclave. public override SqlEnclaveAttestationParameters GetAttestationParameters() { ECDiffieHellmanCng clientDHKey = new ECDiffieHellmanCng(DiffieHellmanKeySize); @@ -118,15 +100,7 @@ public override SqlEnclaveAttestationParameters GetAttestationParameters() return new SqlEnclaveAttestationParameters(VsmHGSProtocolId, 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. - /// - /// The information the provider uses to attest the enclave and generate a symmetric key for the session. The format of this information is specific to the enclave attestation protocol. - /// A Diffie-Hellman algorithm object that encapsulates a client-side key pair. - /// The endpoint of an attestation service for attesting the enclave. - /// The name of the SQL Server instance containing the enclave. - /// The requested enclave session or null if the provider doesn't implement session caching. - /// A counter that the enclave provider is expected to increment each time SqlClient retrieves the session from the cache. The purpose of this field is to prevent replay attacks. + // 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. public override void CreateEnclaveSession(byte[] attestationInfo, ECDiffieHellmanCng clientDHKey, string attestationUrl, string servername, out SqlEnclaveSession sqlEnclaveSession, out long counter) { sqlEnclaveSession = null; @@ -166,12 +140,7 @@ public override void CreateEnclaveSession(byte[] attestationInfo, ECDiffieHellma } } - /// - /// 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 name of the SQL Server instance containing the enclave. - /// The endpoint of an attestation service, SqlClient contacts to attest the enclave. - /// The session to be invalidated. + // When overridden in a derived class, looks up and evicts an enclave session from the enclave session cache, if the provider implements session caching. public override void InvalidateEnclaveSession(string serverName, string enclaveAttestationUrl, SqlEnclaveSession enclaveSessionToInvalidate) { InvalidateEnclaveSessionHelper(serverName, enclaveAttestationUrl, enclaveSessionToInvalidate); @@ -181,12 +150,7 @@ public override void InvalidateEnclaveSession(string serverName, string enclaveA #region Private helpers - /// - /// Performs Attestation per the protocol used by Virtual Secure Modules. - /// - /// Url of the attestation service - /// The health report about the SQL Server host - /// The enclave report about the SQL Server host's enclave + // Performs Attestation per the protocol used by Virtual Secure Modules. private void VerifyAttestationInfo(string attestationUrl, HealthReport healthReport, EnclaveReportPackage enclaveReportPackage) { bool shouldRetryValidation; @@ -220,23 +184,14 @@ private void VerifyAttestationInfo(string attestationUrl, HealthReport healthRep VerifyEnclaveReportSignature(enclaveReportPackage, healthReport.Certificate); } - /// - /// Makes a web request to the provided url and returns the response as a byte[] - /// - /// The url to make the request to - /// The response as a byte[] + // Makes a web request to the provided url and returns the response as a byte[] protected abstract byte[] MakeRequest(string url); - /// - /// Gets the root signing certificate for the provided attestation service. - /// If the certificate does not exist in the cache, this will make a call to the - /// attestation service's "/signingCertificates" endpoint. This endpoint can - /// return multiple certificates if the attestation service consists - /// of multiple nodes. - /// - /// Url of attestation service - /// Re-download the signing certificate irrespective of caching - /// The root signing certificate(s) for the attestation service + // Gets the root signing certificate for the provided attestation service. + // If the certificate does not exist in the cache, this will make a call to the + // attestation service's "/signingCertificates" endpoint. This endpoint can + // return multiple certificates if the attestation service consists + // of multiple nodes. private X509Certificate2Collection GetSigningCertificate(string attestationUrl, bool forceUpdate) { attestationUrl = GetAttestationUrl(attestationUrl); @@ -261,30 +216,17 @@ private X509Certificate2Collection GetSigningCertificate(string attestationUrl, return (X509Certificate2Collection)rootSigningCertificateCache[attestationUrl]; } - /// - /// Return the endpoint for given attestation url - /// - /// The url to alter for corresponding provider - /// altered url + // Return the endpoint for given attestation url protected abstract string GetAttestationUrl(string attestationUrl); - /// - /// Checks if any certificates in the collection are expired - /// - /// A collection of certificates - /// true if any certificates or expired, false otherwise + // Checks if any certificates in the collection are expired private bool AnyCertificatesExpired(X509Certificate2Collection certificates) { return certificates.OfType().Any(c => c.NotAfter < DateTime.Now); } - /// - /// Verifies that a chain of trust can be built from the health report provided - /// by SQL Server and the attestation service's root signing certificate(s). - /// - /// The root signing certificate(s) of the attestation service - /// The health report about the SQL Server host in the form of an X509Certificate2 - /// An X509ChainStatusFlags indicating why the chain failed to build + // Verifies that a chain of trust can be built from the health report provided + // by SQL Server and the attestation service's root signing certificate(s). private X509ChainStatusFlags VerifyHealthReportAgainstRootCertificate(X509Certificate2Collection signingCerts, X509Certificate2 healthReportCert) { var chain = new X509Chain(); @@ -341,11 +283,7 @@ private X509ChainStatusFlags VerifyHealthReportAgainstRootCertificate(X509Certif return X509ChainStatusFlags.NoError; } - /// - /// Verifies the enclave report signature using the health report. - /// - /// The enclave report about the SQL Server host's enclave - /// The health report about the SQL Server host in the form of an X509Certificate2 + // Verifies the enclave report signature using the health report. private void VerifyEnclaveReportSignature(EnclaveReportPackage enclaveReportPackage, X509Certificate2 healthReportCert) { // Check if report is formatted correctly @@ -369,10 +307,7 @@ private void VerifyEnclaveReportSignature(EnclaveReportPackage enclaveReportPack } } - /// - /// Verifies the enclave policy matches expected policy. - /// - /// The enclave report about the SQL Server host's enclave + // Verifies the enclave policy matches expected policy. private void VerifyEnclavePolicy(EnclaveReportPackage enclaveReportPackage) { EnclaveIdentity identity = enclaveReportPackage.Report.Identity; @@ -385,7 +320,6 @@ private void VerifyEnclavePolicy(EnclaveReportPackage enclaveReportPackage) VerifyEnclavePolicyProperty("SecureKernelSvn", identity.SecureKernelSvn, ExpectedPolicy.SecureKernelSvn); VerifyEnclavePolicyProperty("PlatformSvn", identity.PlatformSvn, ExpectedPolicy.PlatformSvn); - // This is a check that the enclave is running without debug support or not. // if (identity.Flags != ExpectedPolicy.Flags) @@ -394,12 +328,7 @@ private void VerifyEnclavePolicy(EnclaveReportPackage enclaveReportPackage) } } - /// - /// Verifies a byte[] enclave policy property - /// - /// The enclave property name - /// The actual enclave property from the enclave report - /// The expected enclave property + // Verifies a byte[] enclave policy property private void VerifyEnclavePolicyProperty(string property, byte[] actual, byte[] expected) { if (!actual.SequenceEqual(expected)) @@ -409,12 +338,7 @@ private void VerifyEnclavePolicyProperty(string property, byte[] actual, byte[] } } - /// - /// Verifies a uint enclave policy property - /// - /// The enclave property name - /// The actual enclave property from the enclave report - /// The expected enclave property + // Verifies a uint enclave policy property private void VerifyEnclavePolicyProperty(string property, uint actual, uint expected) { if (actual < expected) @@ -424,13 +348,7 @@ private void VerifyEnclavePolicyProperty(string property, uint actual, uint expe } } - /// - /// Derives the shared secret between the client and enclave. - /// - /// The enclave's RSA public key - /// The enclave's DiffieHellman key and signature info - /// The client's DiffieHellman key info - /// A byte buffer containing the shared secret + // Derives the shared secret between the client and enclave. private byte[] GetSharedSecret(EnclavePublicKey enclavePublicKey, EnclaveDiffieHellmanInfo enclaveDHInfo, ECDiffieHellmanCng clientDHKey) { // Perform signature verification. The enclave's DiffieHellman public key was signed by the enclave's RSA public key. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationTimeoutRetryHelper.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationTimeoutRetryHelper.cs index 04ea9417d7..1ecfc4d681 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationTimeoutRetryHelper.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationTimeoutRetryHelper.cs @@ -9,8 +9,6 @@ namespace Microsoft.Data.SqlClient { - - /// /// AD auth retry states. /// diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.cs index f9e4e4be97..cc652a2e1a 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.cs @@ -6,16 +6,12 @@ namespace Microsoft.Data.SqlClient { - /// internal class AlwaysEncryptedAttestationException : Exception { - /// public AlwaysEncryptedAttestationException(string message, Exception innerException) : base(message, innerException) { } - /// public AlwaysEncryptedAttestationException(string message) : base(message) { } - /// public AlwaysEncryptedAttestationException() : base() { } } } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs index feb776e315..2ffe98f1fd 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs @@ -7,49 +7,24 @@ namespace Microsoft.Data.SqlClient { - /// - /// Class to hold the enclave's RSA public key - /// - /// internal class EnclavePublicKey { - /// - /// - /// - /// public byte[] PublicKey { get; set; } - /// public EnclavePublicKey(byte[] payload) { PublicKey = payload; } } - /// - /// Class to hold the Enclave's Diffie-Hellman public key and signature - /// internal class EnclaveDiffieHellmanInfo { - /// - /// - /// public int Size { get; private set; } - /// - /// - /// public byte[] PublicKey { get; private set; } - /// - /// - /// public byte[] PublicKeySignature { get; private set; } - /// - /// - /// - /// public EnclaveDiffieHellmanInfo(byte[] payload) { Size = payload.Length; @@ -69,24 +44,13 @@ public EnclaveDiffieHellmanInfo(byte[] payload) } } - /// - /// - /// internal enum EnclaveType { - /// - /// - /// + None = 0, - /// - /// - /// Vbs = 1, - /// - /// - /// Sgx = 2 } } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs index 451adf08c9..9bd1949c26 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs @@ -44,9 +44,8 @@ namespace Microsoft.Data.SqlClient { - /// - /// Implementation of an Enclave provider (both for Sgx and Vsm) with Azure Attestation - /// + + // Implementation of an Enclave provider (both for Sgx and Vsm) with Azure Attestation internal class AzureAttestationEnclaveProvider : EnclaveProviderBase { #region Constants @@ -65,23 +64,15 @@ internal class AzureAttestationEnclaveProvider : EnclaveProviderBase #endregion #region Public methods - /// - /// 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. - /// - /// The name of the SQL Server instance containing the enclave. - /// The endpoint of an attestation service, SqlClient contacts to attest the enclave. - /// When this method returns, the requested enclave session or null if the provider doesn't implement session caching. This parameter is treated as uninitialized. - /// A counter that the enclave provider is expected to increment each time SqlClient retrieves the session from the cache. The purpose of this field is to prevent replay attacks. + + // 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. public override void GetEnclaveSession(string servername, string attestationUrl, out SqlEnclaveSession sqlEnclaveSession, out long counter) { GetEnclaveSessionHelper(servername, attestationUrl, true, out sqlEnclaveSession, out counter); } - /// - /// Gets the information that SqlClient subsequently uses to initiate the process of attesting the enclave and to establish a secure session with the enclave. - /// - /// The information SqlClient subsequently uses to initiate the process of attesting the enclave and to establish a secure session with the enclave. + // Gets the information that SqlClient subsequently uses to initiate the process of attesting the enclave and to establish a secure session with the enclave. public override SqlEnclaveAttestationParameters GetAttestationParameters() { ECDiffieHellmanCng clientDHKey = new ECDiffieHellmanCng(DiffieHellmanKeySize); @@ -91,15 +82,8 @@ public override SqlEnclaveAttestationParameters GetAttestationParameters() return new SqlEnclaveAttestationParameters(AzureBasedAttestationProtocolId, attestationParam, 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. - /// - /// The information the provider uses to attest the enclave and generate a symmetric key for the session. The format of this information is specific to the enclave attestation protocol. - /// A Diffie-Hellman algorithm object that encapsulates a client-side key pair. - /// The endpoint of an attestation service for attesting the enclave. - /// The name of the SQL Server instance containing the enclave. - /// The requested enclave session or null if the provider doesn't implement session caching. - /// A counter that the enclave provider is expected to increment each time SqlClient retrieves the session from the cache. The purpose of this field is to prevent replay attacks. + + // 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. public override void CreateEnclaveSession(byte[] attestationInfo, ECDiffieHellmanCng clientDHKey, string attestationUrl, string servername, out SqlEnclaveSession sqlEnclaveSession, out long counter) { sqlEnclaveSession = null; @@ -144,12 +128,8 @@ public override void CreateEnclaveSession(byte[] attestationInfo, ECDiffieHellma } } - /// - /// 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 name of the SQL Server instance containing the enclave. - /// The endpoint of an attestation service, SqlClient contacts to attest the enclave. - /// The session to be invalidated. + + // When overridden in a derived class, looks up and evicts an enclave session from the enclave session cache, if the provider implements session caching. public override void InvalidateEnclaveSession(string serverName, string enclaveAttestationUrl, SqlEnclaveSession enclaveSessionToInvalidate) { InvalidateEnclaveSessionHelper(serverName, enclaveAttestationUrl, enclaveSessionToInvalidate); @@ -157,49 +137,39 @@ public override void InvalidateEnclaveSession(string serverName, string enclaveA #endregion #region Internal Class - /// - /// A model class respresenting the deserialization of the byte payload the client - /// receives from SQL Server while setting up a session. - /// Protocol format: - /// 1. Total Size of the attestation blob as UINT - /// 2. Size of Enclave RSA public key as UINT - /// 3. Size of Attestation token as UINT - /// 4. Enclave Type as UINT - /// 5. Enclave RSA public key (raw key, of length #2) - /// 6. Attestation token (of length #3) - /// 7. Size of Session Id was UINT - /// 8. Session id value - /// 9. Size of enclave ECDH public key - /// 10. Enclave ECDH public key (of length #9) - /// + // A model class respresenting the deserialization of the byte payload the client + // receives from SQL Server while setting up a session. + // Protocol format: + // 1. Total Size of the attestation blob as UINT + // 2. Size of Enclave RSA public key as UINT + // 3. Size of Attestation token as UINT + // 4. Enclave Type as UINT + // 5. Enclave RSA public key (raw key, of length #2) + // 6. Attestation token (of length #3) + // 7. Size of Session Id was UINT + // 8. Session id value + // 9. Size of enclave ECDH public key + // 10. Enclave ECDH public key (of length #9) internal class AzureAttestationInfo { public uint TotalSize { get; set; } - /// - /// The enclave's RSA Public Key. - /// Needed to establish trust of the enclave. - /// Used to verify the enclave's DiffieHellman info. - /// + // The enclave's RSA Public Key. + // Needed to establish trust of the enclave. + // Used to verify the enclave's DiffieHellman info. public EnclavePublicKey Identity { get; set; } - /// - /// The enclave report from the SQL Server host's enclave. - /// + // The enclave report from the SQL Server host's enclave. public AzureAttestationToken AttestationToken { get; set; } - /// - /// The id of the current session. - /// Needed to set up a secure session between the client and enclave. - /// + // The id of the current session. + // Needed to set up a secure session between the client and enclave. public long SessionId { get; set; } public EnclaveType EnclaveType { get; set; } - /// - /// The DiffieHellman public key and signature of SQL Server host's enclave. - /// Needed to set up a secure session between the client and enclave. - /// + // The DiffieHellman public key and signature of SQL Server host's enclave. + // Needed to set up a secure session between the client and enclave. public EnclaveDiffieHellmanInfo EnclaveDHInfo { get; set; } public AzureAttestationInfo(byte[] attestationInfo) @@ -253,10 +223,8 @@ public AzureAttestationInfo(byte[] attestationInfo) } } - /// - /// A managed model representing the output of EnclaveGetAttestationReport - /// https://msdn.microsoft.com/en-us/library/windows/desktop/mt844233(v=vs.85).aspx - /// + // A managed model representing the output of EnclaveGetAttestationReport + // https://msdn.microsoft.com/en-us/library/windows/desktop/mt844233(v=vs.85).aspx internal class AzureAttestationToken { public string AttestationToken { get; set; } @@ -270,13 +238,11 @@ public AzureAttestationToken(byte[] payload) #endregion Internal Class #region Private helpers - /// - /// Prepare the attestation data in following format - /// Attestation Url length - /// Attestation Url - /// Size of nonce - /// Nonce value - /// + // Prepare the attestation data in following format + // Attestation Url length + // Attestation Url + // Size of nonce + // Nonce value internal byte[] PrepareAttestationParameters() { AttestationInfoCacheItem attestationInfoCacheItem = AttestationInfoCache[Thread.CurrentThread.ManagedThreadId.ToString()] as AttestationInfoCacheItem; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs index e8342e792c..394da6f76e 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs @@ -66,9 +66,6 @@ namespace Microsoft.Data.SqlClient { - /// - /// Base class for Enclave provider - /// internal abstract class EnclaveProviderBase : SqlColumnEncryptionEnclaveProvider { #region Constants @@ -100,21 +97,13 @@ public AttestationInfoCacheItem(string attestationUri, byte[] nonce) } } - /// - /// It is used to save the attestation url and nonce value across API calls - /// + // It is used to save the attestation url and nonce value across API calls protected static readonly MemoryCache AttestationInfoCache = new MemoryCache("AttestationInfoCache"); #endregion #region Public methods - /// - /// Helper method to get the enclave session from the cache if present - /// - /// The name of the SQL Server instance containing the enclave. - /// The endpoint of an attestation service, SqlClient contacts to attest the enclave. - /// True, if we want to create the nonce value. true for AAS - /// When this method returns, the requested enclave session or null if the provider doesn't implement session caching. This parameter is treated as uninitialized. - /// A counter that the enclave provider is expected to increment each time SqlClient retrieves the session from the cache. The purpose of this field is to prevent replay attacks. + + // Helper method to get the enclave session from the cache if present protected void GetEnclaveSessionHelper(string servername, string attestationUrl, bool shouldGenerateNonce, out SqlEnclaveSession sqlEnclaveSession, out long counter) { sqlEnclaveSession = SessionCache.GetEnclaveSession(servername, attestationUrl, out counter); @@ -193,10 +182,7 @@ protected void GetEnclaveSessionHelper(string servername, string attestationUrl, } } - /// - /// Reset the session lock status - /// - /// + // Reset the session lock status protected void UpdateEnclaveSessionLockStatus(SqlEnclaveSession sqlEnclaveSession) { // As per current design, we want to minimize the number of create session calls. To acheive this we block all the GetEnclaveSession calls until the first call to @@ -217,38 +203,19 @@ protected void UpdateEnclaveSessionLockStatus(SqlEnclaveSession sqlEnclaveSessio } } - /// - /// Helper method to remove the enclave session from the cache - /// - /// The name of the SQL Server instance containing the enclave. - /// The endpoint of an attestation service, SqlClient contacts to attest the enclave. - /// + // Helper method to remove the enclave session from the cache protected void InvalidateEnclaveSessionHelper(string servername, string attestationUrl, SqlEnclaveSession enclaveSessionToInvalidate) { SessionCache.InvalidateSession(servername, attestationUrl, enclaveSessionToInvalidate); } - /// - /// Helper method for getting the enclave session from the session cache - /// - /// The endpoint of an attestation service, SqlClient contacts to attest the enclave. - /// The name of the SQL Server instance containing the enclave. - /// A counter that the enclave provider is expected to increment each time SqlClient retrieves the session from the cache. The purpose of this field is to prevent replay attacks. - /// + // Helper method for getting the enclave session from the session cache protected SqlEnclaveSession GetEnclaveSessionFromCache(string attestationUrl, string servername, out long counter) { return SessionCache.GetEnclaveSession(servername, attestationUrl, out counter); } - /// - /// Helper method for adding the enclave session to the session cache - /// - /// The endpoint of an attestation service, SqlClient contacts to attest the enclave. - /// The name of the SQL Server instance containing the enclave. - /// Shared secret computed using enclave ECDH public key - /// Session id for the current enclave session - /// A counter that the enclave provider is expected to increment each time SqlClient retrieves the session from the cache. The purpose of this field is to prevent replay attacks. - /// + // Helper method for adding the enclave session to the session cache protected SqlEnclaveSession AddEnclaveSessionToCache(string attestationUrl, string servername, byte[] sharedSecret, long sessionId, out long counter) { return SessionCache.CreateSession(attestationUrl, servername, sharedSecret, sessionId, out counter); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveSessionCache.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveSessionCache.cs index ee3e248a29..75e4bcd617 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveSessionCache.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveSessionCache.cs @@ -8,31 +8,20 @@ namespace Microsoft.Data.SqlClient { - /// - /// Maintains a cache of SqlEnclaveSession instances - /// + // Maintains a cache of SqlEnclaveSession instances internal class EnclaveSessionCache { private readonly MemoryCache enclaveMemoryCache = new MemoryCache("EnclaveMemoryCache"); private readonly Object enclaveCacheLock = new Object(); - /// - /// Nonce for each message sent by the client to the server to prevent replay attacks by the server, - /// given that for Always Encrypted scenarios, the server is considered an "untrusted" man-in-the-middle. - /// + // Nonce for each message sent by the client to the server to prevent replay attacks by the server, + // given that for Always Encrypted scenarios, the server is considered an "untrusted" man-in-the-middle. private long _counter; - /// - /// Cache timeout of 8 hours to be consistent with jwt validity. - /// + // Cache timeout of 8 hours to be consistent with jwt validity. private static int enclaveCacheTimeOutInHours = 8; - /// - /// Retrieves a SqlEnclaveSession from the cache - /// - /// - /// - /// + // Retrieves a SqlEnclaveSession from the cache public SqlEnclaveSession GetEnclaveSession(string servername, string attestationUrl, out long counter) { string cacheKey = GenerateCacheKey(servername, attestationUrl); @@ -41,12 +30,7 @@ public SqlEnclaveSession GetEnclaveSession(string servername, string attestation return enclaveSession; } - /// - /// Invalidates a SqlEnclaveSession entry in the cache - /// - /// - /// - /// + // Invalidates a SqlEnclaveSession entry in the cache public void InvalidateSession(string serverName, string enclaveAttestationUrl, SqlEnclaveSession enclaveSessionToInvalidate) { string cacheKey = GenerateCacheKey(serverName, enclaveAttestationUrl); @@ -67,14 +51,7 @@ public void InvalidateSession(string serverName, string enclaveAttestationUrl, S } } - /// - /// Creates a new SqlEnclaveSession and adds it to the cache - /// - /// - /// - /// - /// - /// + // Creates a new SqlEnclaveSession and adds it to the cache public SqlEnclaveSession CreateSession(string attestationUrl, string serverName, byte[] sharedSecret, long sessionId, out long counter) { string cacheKey = GenerateCacheKey(serverName, attestationUrl); @@ -89,12 +66,7 @@ public SqlEnclaveSession CreateSession(string attestationUrl, string serverName, return enclaveSession; } - /// - /// Generates the cache key for the enclave session cache - /// - /// - /// - /// + // Generates the cache key for the enclave session cache private string GenerateCacheKey(string serverName, string attestationUrl) { return (serverName + attestationUrl).ToLowerInvariant(); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.cs index ab7e1018fb..212d7a0047 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.cs @@ -13,9 +13,7 @@ namespace Microsoft.Data.SqlClient { - /// - /// Implementation of an Enclave provider for Windows Virtual Secure Mode enclaves - /// + // Implementation of an Enclave provider for Windows Virtual Secure Mode enclaves internal class HostGuardianServiceEnclaveProvider : VirtualizationBasedSecurityEnclaveProviderBase { #region Members @@ -23,16 +21,10 @@ internal class HostGuardianServiceEnclaveProvider : VirtualizationBasedSecurityE // this is endpoint given to us by HGS team from windows private const string AttestationUrlSuffix = @"/v2.0/signingCertificates"; - /// - /// To be added. - /// public int MaxNumRetries { get; set; } private int enclaveRetrySleepInSeconds = 3; - /// - /// To be added. - /// public int EnclaveRetrySleepInSeconds { get @@ -54,21 +46,13 @@ public int EnclaveRetrySleepInSeconds #region Private helpers - /// - /// Return the endpoint for given attestation url - /// - /// The url to alter for corresponding provider - /// altered url + // Return the endpoint for given attestation url protected override string GetAttestationUrl(string attestationUrl) { return attestationUrl.TrimEnd('/') + AttestationUrlSuffix; } - /// - /// Makes a web request to the provided url and returns the response as a byte[] - /// - /// The url to make the request to - /// The response as a byte[] + // Makes a web request to the provided url and returns the response as a byte[] protected override byte[] MakeRequest(string url) { Exception exception = null; @@ -105,44 +89,32 @@ protected override byte[] MakeRequest(string url) #region Models - /// - /// A model class respresenting the deserialization of the byte payload the client - /// receives from SQL Server while setting up a session. - /// + // A model class respresenting the deserialization of the byte payload the client + // receives from SQL Server while setting up a session. internal class AttestationInfo { public uint TotalSize { get; set; } - /// - /// The enclave's RSA Public Key. - /// Needed to establish trust of the enclave. - /// Used to verify the enclave's DiffieHellman info. - /// + // The enclave's RSA Public Key. + // Needed to establish trust of the enclave. + // Used to verify the enclave's DiffieHellman info. public EnclavePublicKey Identity { get; set; } - /// - /// The SQL Server host's health report the server received from the attestation service - /// and forwarded to the client. - /// Needed to establish trust of the enclave report received from SQL Server. - /// Used to verify the enclave report's signature. - /// + // The SQL Server host's health report the server received from the attestation service + // and forwarded to the client. + // Needed to establish trust of the enclave report received from SQL Server. + // Used to verify the enclave report's signature. public HealthReport HealthReport { get; set; } - /// - /// The enclave report from the SQL Server host's enclave. - /// + // The enclave report from the SQL Server host's enclave. public EnclaveReportPackage EnclaveReportPackage { get; set; } - /// - /// The id of the current session. - /// Needed to set up a secure session between the client and enclave. - /// + // The id of the current session. + // Needed to set up a secure session between the client and enclave. public long SessionId { get; set; } - /// - /// The DiffieHellman public key and signature of SQL Server host's enclave. - /// Needed to set up a secure session between the client and enclave. - /// + // The DiffieHellman public key and signature of SQL Server host's enclave. + // Needed to set up a secure session between the client and enclave. public EnclaveDiffieHellmanInfo EnclaveDHInfo { get; set; } public AttestationInfo(byte[] attestationInfo) @@ -186,9 +158,7 @@ public AttestationInfo(byte[] attestationInfo) } } - /// - /// A model class to hold the SQL Server's host health report in an X509Certificate2 - /// + // A model class to hold the SQL Server's host health report in an X509Certificate2 internal class HealthReport { private int Size { get; set; } @@ -207,10 +177,8 @@ public int GetSizeInPayload() } } - /// - /// A managed model representing the output of EnclaveGetAttestationReport - /// https://msdn.microsoft.com/en-us/library/windows/desktop/mt844233(v=vs.85).aspx - /// + // A managed model representing the output of EnclaveGetAttestationReport + // https://msdn.microsoft.com/en-us/library/windows/desktop/mt844233(v=vs.85).aspx internal class EnclaveReportPackage { private int Size { get; set; } @@ -266,10 +234,8 @@ public int GetSizeInPayload() } } - /// - /// A managed model of struct VBS_ENCLAVE_REPORT_PKG_HEADER - /// https://msdn.microsoft.com/en-us/library/windows/desktop/mt844257(v=vs.85).aspx - /// + // A managed model of struct VBS_ENCLAVE_REPORT_PKG_HEADER + // https://msdn.microsoft.com/en-us/library/windows/desktop/mt844257(v=vs.85).aspx internal class EnclaveReportPackageHeader { public uint PackageSize { get; set; } @@ -312,10 +278,8 @@ public int GetSizeInPayload() } } - /// - /// A managed model of struct VBS_ENCLAVE_REPORT - /// https://msdn.microsoft.com/en-us/library/windows/desktop/mt844255(v=vs.85).aspx - /// + // A managed model of struct VBS_ENCLAVE_REPORT + // https://msdn.microsoft.com/en-us/library/windows/desktop/mt844255(v=vs.85).aspx internal class EnclaveReport { private int Size { get; set; } @@ -355,10 +319,8 @@ public int GetSizeInPayload() } } - /// - /// A managed model of struct ENCLAVE_IDENTITY - /// https://msdn.microsoft.com/en-us/library/windows/desktop/mt844239(v=vs.85).aspx - /// + // A managed model of struct ENCLAVE_IDENTITY + // https://msdn.microsoft.com/en-us/library/windows/desktop/mt844239(v=vs.85).aspx internal class EnclaveIdentity { private int Size { get; set; } @@ -442,10 +404,8 @@ public int GetSizeInPayload() } } - /// - /// A managed model of struct VBS_ENCLAVE_REPORT_VARDATA_HEADER - /// https://msdn.microsoft.com/en-us/library/windows/desktop/mt827065(v=vs.85).aspx - /// + // A managed model of struct VBS_ENCLAVE_REPORT_VARDATA_HEADER + // https://msdn.microsoft.com/en-us/library/windows/desktop/mt827065(v=vs.85).aspx internal class EnclaveReportModuleHeader { public uint DataType { get; set; } @@ -468,10 +428,8 @@ public int GetSizeInPayload() } } - /// - /// A managed model of struct VBS_ENCLAVE_REPORT_MODULE - /// https://msdn.microsoft.com/en-us/library/windows/desktop/mt844256(v=vs.85).aspx - /// + // A managed model of struct VBS_ENCLAVE_REPORT_MODULE + // https://msdn.microsoft.com/en-us/library/windows/desktop/mt844256(v=vs.85).aspx internal class EnclaveReportModule { private static readonly int ImageEnclaveLongIdLength = 32; @@ -528,10 +486,8 @@ public int GetSizeInPayload() } } - /// - /// An enum representing the Flags property of ENCLAVE_IDENTITY - /// https://msdn.microsoft.com/en-us/library/windows/desktop/mt844239(v=vs.85).aspx - /// + // An enum representing the Flags property of ENCLAVE_IDENTITY + // https://msdn.microsoft.com/en-us/library/windows/desktop/mt844239(v=vs.85).aspx internal enum EnclaveIdentityFlags { ENCLAVE_FLAG_NONE = 0x00000000, diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs index 3117fd26ef..e90978f717 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs @@ -11,9 +11,6 @@ namespace Microsoft.Data.SqlClient { - /// - /// To be added. - /// internal abstract class VirtualizationBasedSecurityEnclaveProviderBase : EnclaveProviderBase { #region Members @@ -27,9 +24,7 @@ internal abstract class VirtualizationBasedSecurityEnclaveProviderBase : Enclave private const int DiffieHellmanKeySize = 384; private const int VsmHGSProtocolId = 3; - /// - /// ENCLAVE_IDENTITY related constants - /// + // ENCLAVE_IDENTITY related constants private static readonly EnclaveIdentity ExpectedPolicy = new EnclaveIdentity() { OwnerId = new byte[] @@ -89,23 +84,15 @@ internal abstract class VirtualizationBasedSecurityEnclaveProviderBase : Enclave #region Public methods - /// - /// 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. - /// - /// The name of the SQL Server instance containing the enclave. - /// The endpoint of an attestation service, SqlClient contacts to attest the enclave. - /// When this method returns, the requested enclave session or null if the provider doesn't implement session caching. This parameter is treated as uninitialized. - /// A counter that the enclave provider is expected to increment each time SqlClient retrieves the session from the cache. The purpose of this field is to prevent replay attacks. + // 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. public override void GetEnclaveSession(string servername, string attestationUrl, out SqlEnclaveSession sqlEnclaveSession, out long counter) { GetEnclaveSessionHelper(servername, attestationUrl, false, out sqlEnclaveSession, out counter); } - /// - /// Gets the information that SqlClient subsequently uses to initiate the process of attesting the enclave and to establish a secure session with the enclave. - /// - /// The information SqlClient subsequently uses to initiate the process of attesting the enclave and to establish a secure session with the enclave. + // Gets the information that SqlClient subsequently uses to initiate the process of attesting the enclave and to establish a secure session with the enclave. + // The information SqlClient subsequently uses to initiate the process of attesting the enclave and to establish a secure session with the enclave. public override SqlEnclaveAttestationParameters GetAttestationParameters() { ECDiffieHellmanCng clientDHKey = new ECDiffieHellmanCng(DiffieHellmanKeySize); @@ -114,15 +101,7 @@ public override SqlEnclaveAttestationParameters GetAttestationParameters() return new SqlEnclaveAttestationParameters(VsmHGSProtocolId, 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. - /// - /// The information the provider uses to attest the enclave and generate a symmetric key for the session. The format of this information is specific to the enclave attestation protocol. - /// A Diffie-Hellman algorithm object that encapsulates a client-side key pair. - /// The endpoint of an attestation service for attesting the enclave. - /// The name of the SQL Server instance containing the enclave. - /// The requested enclave session or null if the provider doesn't implement session caching. - /// A counter that the enclave provider is expected to increment each time SqlClient retrieves the session from the cache. The purpose of this field is to prevent replay attacks. + // 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. public override void CreateEnclaveSession(byte[] attestationInfo, ECDiffieHellmanCng clientDHKey, string attestationUrl, string servername, out SqlEnclaveSession sqlEnclaveSession, out long counter) { sqlEnclaveSession = null; @@ -162,12 +141,7 @@ public override void CreateEnclaveSession(byte[] attestationInfo, ECDiffieHellma } } - /// - /// 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 name of the SQL Server instance containing the enclave. - /// The endpoint of an attestation service, SqlClient contacts to attest the enclave. - /// The session to be invalidated. + // When overridden in a derived class, looks up and evicts an enclave session from the enclave session cache, if the provider implements session caching. public override void InvalidateEnclaveSession(string serverName, string enclaveAttestationUrl, SqlEnclaveSession enclaveSessionToInvalidate) { InvalidateEnclaveSessionHelper(serverName, enclaveAttestationUrl, enclaveSessionToInvalidate); @@ -177,12 +151,7 @@ public override void InvalidateEnclaveSession(string serverName, string enclaveA #region Private helpers - /// - /// Performs Attestation per the protocol used by Virtual Secure Modules. - /// - /// Url of the attestation service - /// The health report about the SQL Server host - /// The enclave report about the SQL Server host's enclave + // Performs Attestation per the protocol used by Virtual Secure Modules. private void VerifyAttestationInfo(string attestationUrl, HealthReport healthReport, EnclaveReportPackage enclaveReportPackage) { bool shouldRetryValidation; @@ -216,23 +185,14 @@ private void VerifyAttestationInfo(string attestationUrl, HealthReport healthRep VerifyEnclaveReportSignature(enclaveReportPackage, healthReport.Certificate); } - /// - /// Makes a web request to the provided url and returns the response as a byte[] - /// - /// The url to make the request to - /// The response as a byte[] + // Makes a web request to the provided url and returns the response as a byte[] protected abstract byte[] MakeRequest(string url); - /// - /// Gets the root signing certificate for the provided attestation service. - /// If the certificate does not exist in the cache, this will make a call to the - /// attestation service's "/signingCertificates" endpoint. This endpoint can - /// return multiple certificates if the attestation service consists - /// of multiple nodes. - /// - /// Url of attestation service - /// Re-download the signing certificate irrespective of caching - /// The root signing certificate(s) for the attestation service + // Gets the root signing certificate for the provided attestation service. + // If the certificate does not exist in the cache, this will make a call to the + // attestation service's "/signingCertificates" endpoint. This endpoint can + // return multiple certificates if the attestation service consists + // of multiple nodes. private X509Certificate2Collection GetSigningCertificate(string attestationUrl, bool forceUpdate) { attestationUrl = GetAttestationUrl(attestationUrl); @@ -257,30 +217,17 @@ private X509Certificate2Collection GetSigningCertificate(string attestationUrl, return (X509Certificate2Collection)rootSigningCertificateCache[attestationUrl]; } - /// - /// Return the endpoint for given attestation url - /// - /// The url to alter for corresponding provider - /// altered url + // Return the endpoint for given attestation url protected abstract string GetAttestationUrl(string attestationUrl); - /// - /// Checks if any certificates in the collection are expired - /// - /// A collection of certificates - /// true if any certificates or expired, false otherwise + // Checks if any certificates in the collection are expired private bool AnyCertificatesExpired(X509Certificate2Collection certificates) { return certificates.OfType().Any(c => c.NotAfter < DateTime.Now); } - /// - /// Verifies that a chain of trust can be built from the health report provided - /// by SQL Server and the attestation service's root signing certificate(s). - /// - /// The root signing certificate(s) of the attestation service - /// The health report about the SQL Server host in the form of an X509Certificate2 - /// An X509ChainStatusFlags indicating why the chain failed to build + // Verifies that a chain of trust can be built from the health report provided + // by SQL Server and the attestation service's root signing certificate(s). private X509ChainStatusFlags VerifyHealthReportAgainstRootCertificate(X509Certificate2Collection signingCerts, X509Certificate2 healthReportCert) { var chain = new X509Chain(); @@ -337,11 +284,7 @@ private X509ChainStatusFlags VerifyHealthReportAgainstRootCertificate(X509Certif return X509ChainStatusFlags.NoError; } - /// - /// Verifies the enclave report signature using the health report. - /// - /// The enclave report about the SQL Server host's enclave - /// The health report about the SQL Server host in the form of an X509Certificate2 + // Verifies the enclave report signature using the health report. private void VerifyEnclaveReportSignature(EnclaveReportPackage enclaveReportPackage, X509Certificate2 healthReportCert) { // Check if report is formatted correctly @@ -364,10 +307,7 @@ private void VerifyEnclaveReportSignature(EnclaveReportPackage enclaveReportPack } } - /// - /// Verifies the enclave policy matches expected policy. - /// - /// The enclave report about the SQL Server host's enclave + // Verifies the enclave policy matches expected policy. private void VerifyEnclavePolicy(EnclaveReportPackage enclaveReportPackage) { EnclaveIdentity identity = enclaveReportPackage.Report.Identity; @@ -380,7 +320,6 @@ private void VerifyEnclavePolicy(EnclaveReportPackage enclaveReportPackage) VerifyEnclavePolicyProperty("SecureKernelSvn", identity.SecureKernelSvn, ExpectedPolicy.SecureKernelSvn); VerifyEnclavePolicyProperty("PlatformSvn", identity.PlatformSvn, ExpectedPolicy.PlatformSvn); - // This is a check that the enclave is running without debug support or not. // if (identity.Flags != ExpectedPolicy.Flags) @@ -389,12 +328,7 @@ private void VerifyEnclavePolicy(EnclaveReportPackage enclaveReportPackage) } } - /// - /// Verifies a byte[] enclave policy property - /// - /// The enclave property name - /// The actual enclave property from the enclave report - /// The expected enclave property + // Verifies a byte[] enclave policy property private void VerifyEnclavePolicyProperty(string property, byte[] actual, byte[] expected) { if (!actual.SequenceEqual(expected)) @@ -404,12 +338,7 @@ private void VerifyEnclavePolicyProperty(string property, byte[] actual, byte[] } } - /// - /// Verifies a uint enclave policy property - /// - /// The enclave property name - /// The actual enclave property from the enclave report - /// The expected enclave property + // Verifies a uint enclave policy property private void VerifyEnclavePolicyProperty(string property, uint actual, uint expected) { if (actual < expected) @@ -419,13 +348,7 @@ private void VerifyEnclavePolicyProperty(string property, uint actual, uint expe } } - /// - /// Derives the shared secret between the client and enclave. - /// - /// The enclave's RSA public key - /// The enclave's DiffieHellman key and signature info - /// The client's DiffieHellman key info - /// A byte buffer containing the shared secret + // Derives the shared secret between the client and enclave. private byte[] GetSharedSecret(EnclavePublicKey enclavePublicKey, EnclaveDiffieHellmanInfo enclaveDHInfo, ECDiffieHellmanCng clientDHKey) { // Perform signature verification. The enclave's DiffieHellman public key was signed by the enclave's RSA public key. From 5630fc33673ad7eb77f0cd8b3d18e5b1c027af69 Mon Sep 17 00:00:00 2001 From: Javad Date: Tue, 15 Oct 2019 13:46:05 -0700 Subject: [PATCH 18/63] Removes the xml file from public API documentation --- .../AlwaysEncryptedAttestationException.xml | 25 ------------------- 1 file changed, 25 deletions(-) delete mode 100644 doc/snippets/Microsoft.Data.SqlClient/AlwaysEncryptedAttestationException.xml diff --git a/doc/snippets/Microsoft.Data.SqlClient/AlwaysEncryptedAttestationException.xml b/doc/snippets/Microsoft.Data.SqlClient/AlwaysEncryptedAttestationException.xml deleted file mode 100644 index 7b3391f86a..0000000000 --- a/doc/snippets/Microsoft.Data.SqlClient/AlwaysEncryptedAttestationException.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - Represents errors occuring during an Always Encrypted secure enclave operation - - To be added. - - - - Default Constructor. - - To be added. - - - Constructor - - - - Constructor with Parameters. - - - - - From a414c3f002d50f0709423058c44d89b329260cfb Mon Sep 17 00:00:00 2001 From: Javad Date: Tue, 15 Oct 2019 15:15:13 -0700 Subject: [PATCH 19/63] removing SqlColumnEncryptionEnclaveProviderConfigurationManager references from netCore --- .../src/Microsoft.Data.SqlClient.csproj | 1 - ...tionEnclaveProviderConfigurationManager.cs | 73 ------------------- .../Microsoft/Data/SqlClient/SqlConnection.cs | 16 ---- 3 files changed, 90 deletions(-) delete mode 100644 src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProviderConfigurationManager.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 e5b4a97311..b0a0b70040 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -277,7 +277,6 @@ - diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProviderConfigurationManager.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProviderConfigurationManager.cs deleted file mode 100644 index 27aedaf2f1..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProviderConfigurationManager.cs +++ /dev/null @@ -1,73 +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.Configuration; - -namespace Microsoft.Data.SqlClient -{ - /// - /// The configuration section definition for reading app.config. - /// - internal class SqlColumnEncryptionEnclaveProviderConfigurationSection : ConfigurationSection - { - /// - /// User-defined SqlColumnEncryptionEnclaveProviders. - /// - [ConfigurationProperty("providers")] - public ProviderSettingsCollection Providers => (ProviderSettingsCollection)base["providers"]; - - } - - internal class SqlColumnEncryptionEnclaveProviderConfigurationManager - { - private readonly Dictionary _enclaveProviders = new Dictionary(); - - /// - /// Constructor. - /// - public SqlColumnEncryptionEnclaveProviderConfigurationManager(SqlColumnEncryptionEnclaveProviderConfigurationSection configSection) - { - if (configSection != null && configSection.Providers != null && configSection.Providers.Count > 0) - { - foreach (ProviderSettings providerSettings in configSection.Providers) - { - var providerName = providerSettings.Name.ToLowerInvariant(); - SqlColumnEncryptionEnclaveProvider provider; - - try - { - var providerType = Type.GetType(providerSettings.Type, true); - provider = (SqlColumnEncryptionEnclaveProvider)Activator.CreateInstance(providerType); - } - catch (Exception e) - { - throw SQL.CannotCreateSqlColumnEncryptionEnclaveProvider(providerName, providerSettings.Type, e); - } - - _enclaveProviders[providerName] = provider; - } - } - } - - /// - /// Lookup SqlColumnEncryptionEnclaveProvider for a given SqlColumnEncryptionEnclaveProviderName - /// - /// - /// SqlColumnEncryptionEnclaveProvider for a give sqlColumnEncryptionEnclaveProviderName if found, else returns null - public SqlColumnEncryptionEnclaveProvider GetSqlColumnEncryptionEnclaveProvider(string SqlColumnEncryptionEnclaveProviderName) - { - if (string.IsNullOrEmpty(SqlColumnEncryptionEnclaveProviderName)) - throw SQL.SqlColumnEncryptionEnclaveProviderNameCannotBeEmpty(); - SqlColumnEncryptionEnclaveProviderName = SqlColumnEncryptionEnclaveProviderName.ToLowerInvariant(); - - SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = null; - _enclaveProviders.TryGetValue(SqlColumnEncryptionEnclaveProviderName, out sqlColumnEncryptionEnclaveProvider); - - return sqlColumnEncryptionEnclaveProvider; - } - - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs index 9c3c12e353..011ea03c46 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -28,22 +28,6 @@ namespace Microsoft.Data.SqlClient /// public sealed partial class SqlConnection : DbConnection, ICloneable { - static SqlConnection() - { - SqlColumnEncryptionEnclaveProviderConfigurationSection sqlColumnEncryptionEnclaveProviderConfigurationSection = null; - try - { - sqlColumnEncryptionEnclaveProviderConfigurationSection = (SqlColumnEncryptionEnclaveProviderConfigurationSection)ConfigurationManager.GetSection("SqlColumnEncryptionEnclaveProviders"); - } - catch (ConfigurationErrorsException e) - { - throw SQL.CannotGetSqlColumnEncryptionEnclaveProviderConfig(e); - } - - sqlColumnEncryptionEnclaveProviderConfigurationManager = new SqlColumnEncryptionEnclaveProviderConfigurationManager(sqlColumnEncryptionEnclaveProviderConfigurationSection); - } - - static internal readonly SqlColumnEncryptionEnclaveProviderConfigurationManager sqlColumnEncryptionEnclaveProviderConfigurationManager; private bool _AsyncCommandInProgress; From 41970f57ffdc4d37d57b6ef2415dc98bf3310217 Mon Sep 17 00:00:00 2001 From: Javad Date: Tue, 15 Oct 2019 15:19:09 -0700 Subject: [PATCH 20/63] code clean up for netCore, SqlConnection class --- .../netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs index 011ea03c46..51ccc824b6 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -7,7 +7,6 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Configuration; using System.Data; using System.Data.Common; using System.Diagnostics; From b20fb1b174eeba7462f9a79429ad3f8930ec7a81 Mon Sep 17 00:00:00 2001 From: Karina Zhou Date: Tue, 15 Oct 2019 16:41:30 -0700 Subject: [PATCH 21/63] Add AE enclave support in netcore --- .../Microsoft.Data.SqlClient.NetCoreApp.cs | 19 +++ .../Data/Common/DbConnectionStringCommon.cs | 132 ++++++++++++++++++ .../SqlClient/EnclaveDelegate.NetCoreApp.cs | 124 +++++++++++++++- .../SqlClient/EnclaveDelegate.NetStandard.cs | 24 +++- .../Data/SqlClient/EnclaveDelegate.cs | 78 ----------- .../Microsoft/Data/SqlClient/SqlCommand.cs | 24 +++- .../Microsoft/Data/SqlClient/SqlConnection.cs | 12 ++ .../Data/SqlClient/SqlConnectionString.cs | 35 +++++ .../SqlClient/SqlConnectionStringBuilder.cs | 47 +++++++ .../src/Microsoft/Data/SqlClient/SqlUtil.cs | 20 +++ .../src/Microsoft/Data/SqlClient/TdsEnums.cs | 23 +++ .../src/Microsoft/Data/SqlClient/TdsParser.cs | 52 +++++++ .../netcore/src/Resources/SR.Designer.cs | 38 ++++- .../netcore/src/Resources/SR.resx | 14 +- .../src/Microsoft/Data/SqlClient/SqlUtil.cs | 2 +- 15 files changed, 552 insertions(+), 92 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.NetCoreApp.cs index 9caeac95c3..a5a8df9df8 100644 --- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.NetCoreApp.cs @@ -100,6 +100,25 @@ public enum SqlConnectionColumnEncryptionSetting /// Enabled = 1, } + /// + /// To add include file for docs + /// + public enum SqlConnectionAttestationProtocol + { + /// + /// To add include file for docs + /// + NotSpecified = 0, + /// + /// To add include file for docs + /// + AAS = 1, + /// + /// To add include file for docs + /// + HGS = 3 + } + /// public partial class SqlColumnEncryptionCertificateStoreProvider : Microsoft.Data.SqlClient.SqlColumnEncryptionKeyStoreProvider { diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs index 34ed70bb42..1857d76064 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs @@ -214,6 +214,136 @@ internal static string ColumnEncryptionSettingToString(SqlConnectionColumnEncryp } } + #region <> + + /// + /// Attestation Protocol. + /// + const string AttestationProtocolHGS = "HGS"; + const string AttestationProtocolAAS = "AAS"; + + /// + /// Convert a string value to the corresponding SqlConnectionAttestationProtocol + /// + /// + /// + /// + internal static bool TryConvertToAttestationProtocol(string value, out SqlConnectionAttestationProtocol result) + { + bool isSuccess = false; + + if (StringComparer.InvariantCultureIgnoreCase.Equals(value, AttestationProtocolHGS)) + { + result = SqlConnectionAttestationProtocol.HGS; + isSuccess = true; + } + else if (StringComparer.InvariantCultureIgnoreCase.Equals(value, AttestationProtocolAAS)) + { + result = SqlConnectionAttestationProtocol.AAS; + isSuccess = true; + } + else + { + result = DbConnectionStringDefaults.AttestationProtocol; + } + + return isSuccess; + } + + internal static bool IsValidAttestationProtocol(SqlConnectionAttestationProtocol value) + { + Debug.Assert(Enum.GetNames(typeof(SqlConnectionAttestationProtocol)).Length == 3, "SqlConnectionAttestationProtocol enum has changed, update needed"); + return value == SqlConnectionAttestationProtocol.NotSpecified + || value == SqlConnectionAttestationProtocol.HGS + || value == SqlConnectionAttestationProtocol.AAS; + + } + + internal static string AttestationProtocolToString(SqlConnectionAttestationProtocol value) + { + Debug.Assert(IsValidAttestationProtocol(value), "value is not a valid attestation protocol"); + + switch (value) + { + case SqlConnectionAttestationProtocol.HGS: + return AttestationProtocolHGS; + case SqlConnectionAttestationProtocol.AAS: + return AttestationProtocolAAS; + default: + return null; + } + } + + internal static SqlConnectionAttestationProtocol ConvertToAttestationProtocol(string keyword, object value) + { + if (null == value) + { + return DbConnectionStringDefaults.AttestationProtocol; + } + + string sValue = (value as string); + SqlConnectionAttestationProtocol result; + + if (null != sValue) + { + if (TryConvertToAttestationProtocol(sValue, out result)) + { + return result; + } + + // try again after remove leading & trailing whitespaces. + sValue = sValue.Trim(); + if (TryConvertToAttestationProtocol(sValue, out result)) + { + return result; + } + + // string values must be valid + throw ADP.InvalidConnectionOptionValue(keyword); + } + else + { + // the value is not string, try other options + SqlConnectionAttestationProtocol eValue; + + if (value is SqlConnectionAttestationProtocol) + { + eValue = (SqlConnectionAttestationProtocol)value; + } + else if (value.GetType().IsEnum) + { + // explicitly block scenarios in which user tries to use wrong enum types, like: + // builder["SqlConnectionAttestationProtocol"] = EnvironmentVariableTarget.Process; + // workaround: explicitly cast non-SqlConnectionAttestationProtocol enums to int + throw ADP.ConvertFailed(value.GetType(), typeof(SqlConnectionAttestationProtocol), null); + } + else + { + try + { + // Enum.ToObject allows only integral and enum values (enums are blocked above), rasing ArgumentException for the rest + eValue = (SqlConnectionAttestationProtocol)Enum.ToObject(typeof(SqlConnectionAttestationProtocol), value); + } + catch (ArgumentException e) + { + // to be consistent with the messages we send in case of wrong type usage, replace + // the error with our exception, and keep the original one as inner one for troubleshooting + throw ADP.ConvertFailed(value.GetType(), typeof(SqlConnectionAttestationProtocol), e); + } + } + + if (IsValidAttestationProtocol(eValue)) + { + return eValue; + } + else + { + throw ADP.InvalidEnumerationValue(typeof(SqlConnectionAttestationProtocol), (int)eValue); + } + } + } + + #endregion internal static bool IsValidApplicationIntentValue(ApplicationIntent value) { @@ -524,6 +654,7 @@ internal static partial class DbConnectionStringDefaults internal static readonly SqlAuthenticationMethod Authentication = SqlAuthenticationMethod.NotSpecified; internal const SqlConnectionColumnEncryptionSetting ColumnEncryptionSetting = SqlConnectionColumnEncryptionSetting.Disabled; internal const string EnclaveAttestationUrl = ""; + internal static readonly SqlConnectionAttestationProtocol AttestationProtocol = SqlConnectionAttestationProtocol.NotSpecified; } @@ -559,6 +690,7 @@ internal static partial class DbConnectionStringKeywords internal const string Authentication = "Authentication"; internal const string ColumnEncryptionSetting = "Column Encryption Setting"; internal const string EnclaveAttestationUrl = "Enclave Attestation Url"; + internal const string AttestationProtocol = "Attestation Protocol"; // common keywords (OleDb, OracleClient, SqlClient) internal const string DataSource = "Data Source"; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetCoreApp.cs index 0947c1ad3b..9a5cbf62a1 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetCoreApp.cs @@ -1,7 +1,8 @@ // 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.Security.Cryptography; namespace Microsoft.Data.SqlClient @@ -13,6 +14,8 @@ internal partial class EnclaveDelegate { private static readonly string GetSerializedAttestationParametersName = "GetSerializedAttestationParameters"; + private static Dictionary EnclaveProviders = new Dictionary(); + internal byte[] GetSerializedAttestationParameters(SqlEnclaveAttestationParameters sqlEnclaveAttestationParameters, string enclaveType) { byte[] attestationProtocolBytes = null; @@ -56,18 +59,19 @@ internal byte[] GetSerializedAttestationParameters(SqlEnclaveAttestationParamete /// /// Create a new enclave session /// + /// attestation protocol /// enclave type /// servername /// attestation url for attestation service endpoint /// attestation info from SQL Server /// attestation parameters - internal void CreateEnclaveSession(string enclaveType, string serverName, string attestationUrl, + internal void CreateEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, string serverName, string attestationUrl, byte[] attestationInfo, SqlEnclaveAttestationParameters attestationParameters) { lock (_lock) { - SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = GetEnclaveProvider(enclaveType); + SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = GetEnclaveProvider(attestationProtocol, enclaveType); long counter; SqlEnclaveSession sqlEnclaveSession = null; sqlColumnEncryptionEnclaveProvider.GetEnclaveSession(serverName, attestationUrl, out sqlEnclaveSession, out counter); @@ -82,5 +86,119 @@ internal void CreateEnclaveSession(string enclaveType, string serverName, string if (sqlEnclaveSession == null) throw SQL.NullEnclaveSessionReturnedFromProvider(enclaveType, attestationUrl); } } + + /// + /// Generate the byte package that needs to be sent to the enclave + /// + /// attestation protocol + /// Keys to be sent to enclave + /// + /// enclave type + /// server name + /// url for attestation endpoint + /// + internal EnclavePackage GenerateEnclavePackage(SqlConnectionAttestationProtocol attestationProtocol, Dictionary keysTobeSentToEnclave, string queryText, string enclaveType, string serverName, string enclaveAttestationUrl) + { + + SqlEnclaveSession sqlEnclaveSession = null; + long counter; + try + { + GetEnclaveSession(attestationProtocol, enclaveType, serverName, enclaveAttestationUrl, out sqlEnclaveSession, out counter, throwIfNull: true); + } + catch (Exception e) + { + throw new RetriableEnclaveQueryExecutionException(e.Message, e); + } + + List decryptedKeysToBeSentToEnclave = GetDecryptedKeysToBeSentToEnclave(keysTobeSentToEnclave, serverName); + byte[] queryStringHashBytes = ComputeQueryStringHash(queryText); + byte[] keyBytePackage = GenerateBytePackageForKeys(counter, queryStringHashBytes, decryptedKeysToBeSentToEnclave); + byte[] sessionKey = sqlEnclaveSession.GetSessionKey(); + byte[] encryptedBytePackage = EncryptBytePackage(keyBytePackage, sessionKey, serverName); + byte[] enclaveSessionHandle = BitConverter.GetBytes(sqlEnclaveSession.SessionId); + byte[] byteArrayToBeSentToEnclave = CombineByteArrays(new[] { enclaveSessionHandle, encryptedBytePackage }); + return new EnclavePackage(byteArrayToBeSentToEnclave, sqlEnclaveSession); + } + + internal void InvalidateEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, string serverName, string EnclaveAttestationUrl, SqlEnclaveSession enclaveSession) + { + SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = GetEnclaveProvider(attestationProtocol, enclaveType); + sqlColumnEncryptionEnclaveProvider.InvalidateEnclaveSession(serverName, EnclaveAttestationUrl, enclaveSession); + } + + + internal SqlEnclaveAttestationParameters GetAttestationParameters(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, string serverName, string enclaveAttestationUrl) + { + SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = GetEnclaveProvider(attestationProtocol, enclaveType); + return sqlColumnEncryptionEnclaveProvider.GetAttestationParameters(); + } + + private SqlColumnEncryptionEnclaveProvider GetEnclaveProvider(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType) + { + SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = null; + + if (!EnclaveProviders.TryGetValue(attestationProtocol, out sqlColumnEncryptionEnclaveProvider)) + { + switch (attestationProtocol) + { + case SqlConnectionAttestationProtocol.AAS: + AzureAttestationEnclaveProvider azureAttestationEnclaveProvider = new AzureAttestationEnclaveProvider(); + EnclaveProviders[attestationProtocol] = (SqlColumnEncryptionEnclaveProvider)azureAttestationEnclaveProvider; + sqlColumnEncryptionEnclaveProvider = EnclaveProviders[attestationProtocol]; + break; + + case SqlConnectionAttestationProtocol.HGS: + HostGuardianServiceEnclaveProvider hostGuardianServiceEnclaveProvider = new HostGuardianServiceEnclaveProvider(); + EnclaveProviders[attestationProtocol] = (SqlColumnEncryptionEnclaveProvider)hostGuardianServiceEnclaveProvider; + sqlColumnEncryptionEnclaveProvider = EnclaveProviders[attestationProtocol]; + break; + + default: + break; + } + } + + if (sqlColumnEncryptionEnclaveProvider == null) + { + throw SQL.EnclaveProviderNotFound(enclaveType, ConvertAttestationProtocolToString(attestationProtocol)); + } + + return sqlColumnEncryptionEnclaveProvider; + } + + private string ConvertAttestationProtocolToString(SqlConnectionAttestationProtocol attestationProtocol) + { + switch (attestationProtocol) + { + case SqlConnectionAttestationProtocol.AAS: + return "AAS"; + + case SqlConnectionAttestationProtocol.HGS: + return "HGS"; + + default: + return "NotSpecified"; + } + } + + internal void GetEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, string serverName, string enclaveAttestationUrl, out SqlEnclaveSession sqlEnclaveSession) + { + long counter; + GetEnclaveSession(attestationProtocol, enclaveType, serverName, enclaveAttestationUrl, out sqlEnclaveSession, out counter, throwIfNull: false); + } + + // kz to move to netcoreapp + private void GetEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, string serverName, string enclaveAttestationUrl, out SqlEnclaveSession sqlEnclaveSession, out long counter, bool throwIfNull) + { + SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = GetEnclaveProvider(attestationProtocol, enclaveType); + sqlColumnEncryptionEnclaveProvider.GetEnclaveSession(serverName, enclaveAttestationUrl, out sqlEnclaveSession, out counter); + + if (throwIfNull) + { + if (sqlEnclaveSession == null) + throw SQL.NullEnclaveSessionDuringQueryExecution(enclaveType, enclaveAttestationUrl); + } + } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetStandard.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetStandard.cs index f7c56f0155..91f536637d 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetStandard.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetStandard.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; namespace Microsoft.Data.SqlClient { @@ -20,15 +21,36 @@ internal byte[] GetSerializedAttestationParameters( /// /// Create a new enclave session /// + /// attestation protocol /// enclave type /// servername /// attestation url for attestation service endpoint /// attestation info from SQL Server /// attestation parameters - internal void CreateEnclaveSession(string enclaveType, string serverName, string attestationUrl, + internal void CreateEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, string serverName, string attestationUrl, byte[] attestationInfo, SqlEnclaveAttestationParameters attestationParameters) { throw new PlatformNotSupportedException(); } + + internal void GetEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, string serverName, string enclaveAttestationUrl, out SqlEnclaveSession sqlEnclaveSession) + { + throw new PlatformNotSupportedException(); + } + + internal EnclavePackage GenerateEnclavePackage(SqlConnectionAttestationProtocol attestationProtocol, Dictionary keysTobeSentToEnclave, string queryText, string enclaveType, string serverName, string enclaveAttestationUrl) + { + throw new PlatformNotSupportedException(); + } + + internal void InvalidateEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, string serverName, string EnclaveAttestationUrl, SqlEnclaveSession enclaveSession) + { + throw new PlatformNotSupportedException(); + } + + internal SqlEnclaveAttestationParameters GetAttestationParameters(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, string serverName, string enclaveAttestationUrl) + { + throw new PlatformNotSupportedException(); + } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs index 89fd92c0de..3d3ef1cab0 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs @@ -28,70 +28,6 @@ internal partial class EnclaveDelegate private EnclaveDelegate() { } - /// - /// Generate the byte package that needs to be sent to the enclave - /// - /// Keys to be sent to enclave - /// - /// enclave type - /// server name - /// url for attestation endpoint - /// - internal EnclavePackage GenerateEnclavePackage(Dictionary keysTobeSentToEnclave, string queryText, string enclaveType, string serverName, string enclaveAttestationUrl) - { - - SqlEnclaveSession sqlEnclaveSession = null; - long counter; - try - { - GetEnclaveSession(enclaveType, serverName, enclaveAttestationUrl, out sqlEnclaveSession, out counter, throwIfNull: true); - } - catch (Exception e) - { - throw new RetriableEnclaveQueryExecutionException(e.Message, e); - } - - List decryptedKeysToBeSentToEnclave = GetDecryptedKeysToBeSentToEnclave(keysTobeSentToEnclave, serverName); - byte[] queryStringHashBytes = ComputeQueryStringHash(queryText); - byte[] keyBytePackage = GenerateBytePackageForKeys(counter, queryStringHashBytes, decryptedKeysToBeSentToEnclave); - byte[] sessionKey = sqlEnclaveSession.GetSessionKey(); - byte[] encryptedBytePackage = EncryptBytePackage(keyBytePackage, sessionKey, serverName); - byte[] enclaveSessionHandle = BitConverter.GetBytes(sqlEnclaveSession.SessionId); - byte[] byteArrayToBeSentToEnclave = CombineByteArrays(new[] { enclaveSessionHandle, encryptedBytePackage }); - return new EnclavePackage(byteArrayToBeSentToEnclave, sqlEnclaveSession); - } - - internal void InvalidateEnclaveSession(string enclaveType, string serverName, string EnclaveAttestationUrl, SqlEnclaveSession enclaveSession) - { - SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = GetEnclaveProvider(enclaveType); - sqlColumnEncryptionEnclaveProvider.InvalidateEnclaveSession(serverName, EnclaveAttestationUrl, enclaveSession); - } - - internal void GetEnclaveSession(string enclaveType, string serverName, string enclaveAttestationUrl, out SqlEnclaveSession sqlEnclaveSession) - { - long counter; - GetEnclaveSession(enclaveType, serverName, enclaveAttestationUrl, out sqlEnclaveSession, out counter, throwIfNull: false); - } - - private void GetEnclaveSession(string enclaveType, string serverName, string enclaveAttestationUrl, out SqlEnclaveSession sqlEnclaveSession, out long counter, bool throwIfNull) - { - SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = GetEnclaveProvider(enclaveType); - sqlColumnEncryptionEnclaveProvider.GetEnclaveSession(serverName, enclaveAttestationUrl, out sqlEnclaveSession, out counter); - - if (throwIfNull) - { - if (sqlEnclaveSession == null) - throw SQL.NullEnclaveSessionDuringQueryExecution(enclaveType, enclaveAttestationUrl); - } - } - - internal SqlEnclaveAttestationParameters GetAttestationParameters(string enclaveType, string serverName, string enclaveAttestationUrl) - { - SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = GetEnclaveProvider(enclaveType); - return sqlColumnEncryptionEnclaveProvider.GetAttestationParameters(); - } - - private byte[] GetUintBytes(string enclaveType, int intValue, string variableName) { try @@ -106,20 +42,6 @@ private byte[] GetUintBytes(string enclaveType, int intValue, string variableNam } } - private SqlColumnEncryptionEnclaveProvider GetEnclaveProvider(string enclaveType) - { - if (SqlConnection.sqlColumnEncryptionEnclaveProviderConfigurationManager == null) - throw SQL.EnclaveProvidersNotConfiguredForEnclaveBasedQuery(); - - var sqlColumnEncryptionEnclaveProvider = - SqlConnection.sqlColumnEncryptionEnclaveProviderConfigurationManager.GetSqlColumnEncryptionEnclaveProvider( - enclaveType); - - if (sqlColumnEncryptionEnclaveProvider == null) - throw SQL.EnclaveProviderNotFound(enclaveType); - return sqlColumnEncryptionEnclaveProvider; - } - /// /// Decrypt the keys that need to be sent to the enclave /// 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 1b5b34a1a5..17eb88d565 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 @@ -2041,7 +2041,7 @@ private void CreateLocalCompletionTask(CommandBehavior behavior, object stateObj if (ShouldUseEnclaveBasedWorkflow && this.enclavePackage != null) { - EnclaveDelegate.Instance.InvalidateEnclaveSession(this._activeConnection.Parser.EnclaveType, + EnclaveDelegate.Instance.InvalidateEnclaveSession(this._activeConnection.AttestationProtocol, this._activeConnection.Parser.EnclaveType, this._activeConnection.DataSource, this._activeConnection.EnclaveAttestationUrl, this.enclavePackage.EnclaveSession); } @@ -3349,14 +3349,15 @@ private SqlDataReader TryFetchInputParameterEncryptionInfo(int timeout, if (ShouldUseEnclaveBasedWorkflow) { + SqlConnectionAttestationProtocol attestationProtocol = this._activeConnection.AttestationProtocol; string enclaveType = this._activeConnection.Parser.EnclaveType; string dataSource = this._activeConnection.DataSource; string enclaveAttestationUrl = this._activeConnection.EnclaveAttestationUrl; SqlEnclaveSession sqlEnclaveSession = null; - EnclaveDelegate.Instance.GetEnclaveSession(enclaveType, dataSource, enclaveAttestationUrl, out sqlEnclaveSession); + EnclaveDelegate.Instance.GetEnclaveSession(attestationProtocol, enclaveType, dataSource, enclaveAttestationUrl, out sqlEnclaveSession); if (sqlEnclaveSession == null) { - enclaveAttestationParameters = EnclaveDelegate.Instance.GetAttestationParameters(enclaveType, dataSource, enclaveAttestationUrl); + enclaveAttestationParameters = EnclaveDelegate.Instance.GetAttestationParameters(attestationProtocol, enclaveType, dataSource, enclaveAttestationUrl); serializedAttestatationParameters = EnclaveDelegate.Instance.GetSerializedAttestationParameters(enclaveAttestationParameters, enclaveType); } } @@ -3858,11 +3859,12 @@ private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDi byte[] attestationInfo = new byte[attestationInfoLength]; ds.GetBytes((int)DescribeParameterEncryptionResultSet3.AttestationInfo, 0, attestationInfo, 0, attestationInfoLength); + SqlConnectionAttestationProtocol attestationProtocol = this._activeConnection.AttestationProtocol; string enclaveType = this._activeConnection.Parser.EnclaveType; string dataSource = this._activeConnection.DataSource; string enclaveAttestationUrl = this._activeConnection.EnclaveAttestationUrl; - EnclaveDelegate.Instance.CreateEnclaveSession(enclaveType, dataSource, enclaveAttestationUrl, attestationInfo, enclaveAttestationParameters); + EnclaveDelegate.Instance.CreateEnclaveSession(attestationProtocol, enclaveType, dataSource, enclaveAttestationUrl, attestationInfo, enclaveAttestationParameters); enclaveAttestationParameters = null; attestationInfoRead = true; } @@ -3975,7 +3977,8 @@ internal SqlDataReader RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior if (ShouldUseEnclaveBasedWorkflow && this.enclavePackage != null) { - EnclaveDelegate.Instance.InvalidateEnclaveSession(this._activeConnection.Parser.EnclaveType, this._activeConnection.DataSource, this._activeConnection.EnclaveAttestationUrl, this.enclavePackage.EnclaveSession); + EnclaveDelegate.Instance.InvalidateEnclaveSession(this._activeConnection.AttestationProtocol, this._activeConnection.Parser.EnclaveType, + this._activeConnection.DataSource, this._activeConnection.EnclaveAttestationUrl, this.enclavePackage.EnclaveSession); } return RunExecuteReader(cmdBehavior, runBehavior, returnStream, null, TdsParserStaticMethods.GetRemainingTimeout(timeout, firstAttemptStart), out task, out usedCache, isAsync, inRetry: true, method: method); @@ -4017,7 +4020,8 @@ internal SqlDataReader RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior if (ShouldUseEnclaveBasedWorkflow && this.enclavePackage != null) { - EnclaveDelegate.Instance.InvalidateEnclaveSession(this._activeConnection.Parser.EnclaveType, this._activeConnection.DataSource, this._activeConnection.EnclaveAttestationUrl, this.enclavePackage.EnclaveSession); + EnclaveDelegate.Instance.InvalidateEnclaveSession(this._activeConnection.AttestationProtocol, this._activeConnection.Parser.EnclaveType, + this._activeConnection.DataSource, this._activeConnection.EnclaveAttestationUrl, this.enclavePackage.EnclaveSession); } return RunExecuteReader(cmdBehavior, runBehavior, returnStream, null, TdsParserStaticMethods.GetRemainingTimeout(timeout, firstAttemptStart), out task, out usedCache, isAsync, inRetry: true, method: method); @@ -4114,9 +4118,15 @@ private void GenerateEnclavePackage() if (string.IsNullOrWhiteSpace(enclaveType)) throw SQL.EnclaveTypeNullForEnclaveBasedQuery(); + SqlConnectionAttestationProtocol attestationProtocol = this._activeConnection.AttestationProtocol; + if (attestationProtocol == SqlConnectionAttestationProtocol.NotSpecified) + { + throw SQL.AttestationProtocolNotSpecifiedForGeneratingEnclavePackage(); + } + try { - this.enclavePackage = EnclaveDelegate.Instance.GenerateEnclavePackage(keysToBeSentToEnclave, + this.enclavePackage = EnclaveDelegate.Instance.GenerateEnclavePackage(attestationProtocol, keysToBeSentToEnclave, this.CommandText, enclaveType, this._activeConnection.DataSource, this._activeConnection.EnclaveAttestationUrl); } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs index 9c3c12e353..418489e5ff 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -293,6 +293,18 @@ public static void RegisterColumnEncryptionKeyStoreProviders(IDictionary internal string EnclaveAttestationUrl => ((SqlConnectionString)ConnectionOptions).EnclaveAttestationUrl; + /// + /// Get attestation protocol + /// + internal SqlConnectionAttestationProtocol AttestationProtocol + { + get + { + SqlConnectionString opt = (SqlConnectionString)ConnectionOptions; + return opt.AttestationProtocol; + } + } + // This method will be called once connection string is set or changed. private void CacheConnectionStringProperties() { diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionString.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionString.cs index ee88cd3817..9cf93665dc 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionString.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionString.cs @@ -48,6 +48,7 @@ internal static partial class DEFAULT internal static readonly SqlAuthenticationMethod Authentication = SqlAuthenticationMethod.NotSpecified; internal const SqlConnectionColumnEncryptionSetting ColumnEncryptionSetting = SqlConnectionColumnEncryptionSetting.Disabled; internal const string EnclaveAttestationUrl = ""; + internal static readonly SqlConnectionAttestationProtocol AttestationProtocol = SqlConnectionAttestationProtocol.NotSpecified; } // SqlConnection ConnectionString Options @@ -63,6 +64,7 @@ internal static class KEY #endif internal const string ColumnEncryptionSetting = "column encryption setting"; internal const string EnclaveAttestationUrl = "enclave attestation url"; + internal const string AttestationProtocol = "attestation protocol"; internal const string Connect_Timeout = "connect timeout"; internal const string Connection_Reset = "connection reset"; internal const string Context_Connection = "context connection"; @@ -186,6 +188,7 @@ internal static class TRANSACTIONBINDING private readonly SqlAuthenticationMethod _authType; private readonly SqlConnectionColumnEncryptionSetting _columnEncryptionSetting; private readonly string _enclaveAttestationUrl; + private readonly SqlConnectionAttestationProtocol _attestationProtocol; private readonly int _connectTimeout; private readonly int _loadBalanceTimeout; @@ -261,6 +264,7 @@ internal SqlConnectionString(string connectionString) : base(connectionString, G _authType = ConvertValueToAuthenticationType(); _columnEncryptionSetting = ConvertValueToColumnEncryptionSetting(); _enclaveAttestationUrl = ConvertValueToString(KEY.EnclaveAttestationUrl, DEFAULT.EnclaveAttestationUrl); + _attestationProtocol = ConvertValueToAttestationProtocol(); // Temporary string - this value is stored internally as an enum. string typeSystemVersionString = ConvertValueToString(KEY.Type_System_Version, null); @@ -475,6 +479,7 @@ internal SqlConnectionString(SqlConnectionString connectionOptions, string dataS _authType = connectionOptions._authType; _columnEncryptionSetting = connectionOptions._columnEncryptionSetting; _enclaveAttestationUrl = connectionOptions._enclaveAttestationUrl; + _attestationProtocol = connectionOptions._attestationProtocol; ValidateValueLength(_dataSource, TdsEnums.MAXLEN_SERVERNAME, KEY.Data_Source); } @@ -495,6 +500,7 @@ internal SqlConnectionString(SqlConnectionString connectionOptions, string dataS internal SqlAuthenticationMethod Authentication { get { return _authType; } } internal SqlConnectionColumnEncryptionSetting ColumnEncryptionSetting { get { return _columnEncryptionSetting; } } internal string EnclaveAttestationUrl { get { return _enclaveAttestationUrl; } } + internal SqlConnectionAttestationProtocol AttestationProtocol { get { return _attestationProtocol; } } internal bool PersistSecurityInfo { get { return _persistSecurityInfo; } } internal bool Pooling { get { return _pooling; } } internal bool Replication { get { return _replication; } } @@ -568,6 +574,7 @@ internal static Dictionary GetParseSynonyms() { KEY.Type_System_Version, KEY.Type_System_Version }, { KEY.ColumnEncryptionSetting, KEY.ColumnEncryptionSetting }, { KEY.EnclaveAttestationUrl, KEY.EnclaveAttestationUrl }, + { KEY.AttestationProtocol, KEY.AttestationProtocol}, { KEY.User_ID, KEY.User_ID }, { KEY.User_Instance, KEY.User_Instance }, { KEY.Workstation_Id, KEY.Workstation_Id }, @@ -708,5 +715,33 @@ internal SqlConnectionColumnEncryptionSetting ConvertValueToColumnEncryptionSett throw ADP.InvalidConnectionOptionValue(KEY.ColumnEncryptionSetting, e); } } + + /// + /// Convert the value to SqlConnectionAttestationProtocol + /// + /// + internal SqlConnectionAttestationProtocol ConvertValueToAttestationProtocol() + { + object value = base.Parsetable[KEY.AttestationProtocol]; + + string valStr = value as string; + if (valStr == null) + { + return DEFAULT.AttestationProtocol; + } + + try + { + return DbConnectionStringBuilderUtil.ConvertToAttestationProtocol(KEY.AttestationProtocol, valStr); + } + catch (FormatException e) + { + throw ADP.InvalidConnectionOptionValue(KEY.AttestationProtocol, e); + } + catch (OverflowException e) + { + throw ADP.InvalidConnectionOptionValue(KEY.AttestationProtocol, e); + } + } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs index 4f052fac9e..6a7d4cf7b4 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs @@ -67,6 +67,7 @@ private enum Keywords ColumnEncryptionSetting, EnclaveAttestationUrl, + AttestationProtocol, // keep the count value last KeywordsCount @@ -113,6 +114,7 @@ private enum Keywords private SqlAuthenticationMethod _authentication = DbConnectionStringDefaults.Authentication; private SqlConnectionColumnEncryptionSetting _columnEncryptionSetting = DbConnectionStringDefaults.ColumnEncryptionSetting; private string _enclaveAttestationUrl = DbConnectionStringDefaults.EnclaveAttestationUrl; + private SqlConnectionAttestationProtocol _attestationProtocol = DbConnectionStringDefaults.AttestationProtocol; private static string[] CreateValidKeywords() { @@ -153,6 +155,7 @@ private static string[] CreateValidKeywords() validKeywords[(int)Keywords.Authentication] = DbConnectionStringKeywords.Authentication; validKeywords[(int)Keywords.ColumnEncryptionSetting] = DbConnectionStringKeywords.ColumnEncryptionSetting; validKeywords[(int)Keywords.EnclaveAttestationUrl] = DbConnectionStringKeywords.EnclaveAttestationUrl; + validKeywords[(int)Keywords.AttestationProtocol] = DbConnectionStringKeywords.AttestationProtocol; return validKeywords; } @@ -195,6 +198,7 @@ private static Dictionary CreateKeywordsDictionary() hash.Add(DbConnectionStringKeywords.Authentication, Keywords.Authentication); hash.Add(DbConnectionStringKeywords.ColumnEncryptionSetting, Keywords.ColumnEncryptionSetting); hash.Add(DbConnectionStringKeywords.EnclaveAttestationUrl, Keywords.EnclaveAttestationUrl); + hash.Add(DbConnectionStringKeywords.AttestationProtocol, Keywords.AttestationProtocol); hash.Add(DbConnectionStringSynonyms.APP, Keywords.ApplicationName); hash.Add(DbConnectionStringSynonyms.EXTENDEDPROPERTIES, Keywords.AttachDBFilename); @@ -313,6 +317,9 @@ public override object this[string keyword] case Keywords.EnclaveAttestationUrl: EnclaveAttestationUrl = ConvertToString(value); break; + case Keywords.AttestationProtocol: + AttestationProtocol = ConvertToAttestationProtocol(keyword, value); + break; #if netcoreapp case Keywords.PoolBlockingPeriod: PoolBlockingPeriod = ConvertToPoolBlockingPeriod(keyword, value); break; #endif @@ -475,6 +482,25 @@ public string EnclaveAttestationUrl } } + /// + /// + /// + /// + public SqlConnectionAttestationProtocol AttestationProtocol + { + get { return _attestationProtocol; } + set + { + if (!DbConnectionStringBuilderUtil.IsValidAttestationProtocol(value)) + { + throw ADP.InvalidEnumerationValue(typeof(SqlConnectionAttestationProtocol), (int)value); + } + + SetAttestationProtocolValue(value); + _attestationProtocol = value; + } + } + /// public bool TrustServerCertificate { @@ -850,6 +876,16 @@ private static SqlConnectionColumnEncryptionSetting ConvertToColumnEncryptionSet return DbConnectionStringBuilderUtil.ConvertToColumnEncryptionSetting(keyword, value); } + /// + /// Convert to SqlConnectionAttestationProtocol + /// + /// + /// + private static SqlConnectionAttestationProtocol ConvertToAttestationProtocol(string keyword, object value) + { + return DbConnectionStringBuilderUtil.ConvertToAttestationProtocol(keyword, value); + } + private object GetAt(Keywords index) { switch (index) @@ -922,6 +958,8 @@ private object GetAt(Keywords index) return ColumnEncryptionSetting; case Keywords.EnclaveAttestationUrl: return EnclaveAttestationUrl; + case Keywords.AttestationProtocol: + return AttestationProtocol; default: Debug.Fail("unexpected keyword"); @@ -1064,6 +1102,9 @@ private void Reset(Keywords index) case Keywords.EnclaveAttestationUrl: _enclaveAttestationUrl = DbConnectionStringDefaults.EnclaveAttestationUrl; break; + case Keywords.AttestationProtocol: + _attestationProtocol = DbConnectionStringDefaults.AttestationProtocol; + break; default: Debug.Fail("unexpected keyword"); throw UnsupportedKeyword(s_validKeywords[(int)index]); @@ -1094,6 +1135,12 @@ private void SetColumnEncryptionSettingValue(SqlConnectionColumnEncryptionSettin base[DbConnectionStringKeywords.ColumnEncryptionSetting] = DbConnectionStringBuilderUtil.ColumnEncryptionSettingToString(value); } + private void SetAttestationProtocolValue(SqlConnectionAttestationProtocol value) + { + Debug.Assert(DbConnectionStringBuilderUtil.IsValidAttestationProtocol(value), "Invalid value for SqlConnectionAttestationProtocol"); + base[DbConnectionStringKeywords.AttestationProtocol] = DbConnectionStringBuilderUtil.AttestationProtocolToString(value); + } + private void SetAuthenticationValue(SqlAuthenticationMethod value) { Debug.Assert(DbConnectionStringBuilderUtil.IsValidAuthenticationTypeValue(value), "Invalid value for AuthenticationType"); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs index b5a1293310..58c0a89c4f 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs @@ -1672,6 +1672,26 @@ internal static Exception NullEnclavePackageForEnclaveBasedQuery(string enclaveT return ADP.Argument(System.SRHelper.GetString(SR.TCE_NullEnclavePackageForEnclaveBasedQuery, enclaveType, enclaveAttestationUrl)); } + internal static Exception EnclaveProviderNotFound(string enclaveType, string attestationProtocol) + { + return ADP.InvalidOperation(System.SRHelper.GetString(SR.TCE_EnclaveProviderNotFound, enclaveType, attestationProtocol)); + } + + internal static Exception EnclaveTypeNotSupported(string enclaveType) + { + return ADP.InvalidOperation(System.SRHelper.GetString(SR.TCE_EnclaveTypeNotSupported, enclaveType)); + } + + internal static Exception AttestationProtocolNotSupportEnclaveType(string attestationProtocolStr, string enclaveType) + { + return ADP.InvalidOperation(System.SRHelper.GetString(SR.TCE_AttestationProtocolNotSupportEnclaveType, attestationProtocolStr, enclaveType)); + } + + internal static Exception AttestationProtocolNotSpecifiedForGeneratingEnclavePackage() + { + return ADP.InvalidOperation(System.SRHelper.GetString(SR.TCE_AttestationProtocolNotSpecifiedForGeneratingEnclavePackage)); + } + #endregion Always Encrypted - Client side query processing errors #region Always Encrypted - SQL connection related error messages 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 6ccd9c9eae..902bc0f094 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 @@ -981,6 +981,8 @@ internal static string GetSniContextEnumName(SniContext sniContext) internal const int AES_256_CBC = 1; internal const int AEAD_AES_256_CBC_HMAC_SHA256 = 2; + internal const string ENCLAVE_TYPE_VBS = "VBS"; + internal const string ENCLAVE_TYPE_SGX = "SGX"; // TCE Param names for exec handling internal const string TCE_PARAM_CIPHERTEXT = "cipherText"; @@ -1045,6 +1047,27 @@ internal enum ParsingErrorState DataClassificationInvalidInformationTypeIndex = 27 } + /// + /// + /// + public enum SqlConnectionAttestationProtocol + { + /// + /// If the attestation protocol is not specified. Use this as default value. + /// + NotSpecified = 0, + + /// + /// Attestation portocol for Azure Attestation Service + /// + AAS = 1, + + /// + /// Attestation protocol for Host Guardian Service + /// + HGS = 3 + } + /// public enum SqlConnectionColumnEncryptionSetting { 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 b871e6f420..1fec3e066f 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 @@ -3002,9 +3002,61 @@ private bool TryProcessFeatureExtAck(TdsParserStateObject stateObj) throw SQL.EnclaveTypeNotReturned(); } + // Check if enclave attestation url was specified and the attestation protocol supports the enclave type. + SqlConnectionAttestationProtocol attestationProtocol = _connHandler.ConnectionOptions.AttestationProtocol; + if (this.Connection.RoutingInfo == null + && (!string.IsNullOrWhiteSpace(_connHandler.ConnectionOptions.EnclaveAttestationUrl)) + && (!string.IsNullOrWhiteSpace(EnclaveType)) + && (!IsValidAttestationProtocol(attestationProtocol, EnclaveType))) + { + throw SQL.AttestationProtocolNotSupportEnclaveType(ConvertAttestationProtocolToString(attestationProtocol), EnclaveType); + } + + return true; + } + + private bool IsValidAttestationProtocol(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType) + { + switch (enclaveType) + { + case TdsEnums.ENCLAVE_TYPE_VBS: + if (attestationProtocol != SqlConnectionAttestationProtocol.AAS + && attestationProtocol != SqlConnectionAttestationProtocol.HGS) + { + return false; + } + break; + + case TdsEnums.ENCLAVE_TYPE_SGX: + if (attestationProtocol != SqlConnectionAttestationProtocol.AAS) + { + return false; + } + break; + + default: + // if we reach here, the enclave type is not supported + throw SQL.EnclaveTypeNotSupported(enclaveType); + } + return true; } + private string ConvertAttestationProtocolToString(SqlConnectionAttestationProtocol attestationProtocol) + { + switch (attestationProtocol) + { + case SqlConnectionAttestationProtocol.AAS: + return "AAS"; + + case SqlConnectionAttestationProtocol.HGS: + return "HGS"; + + default: + return "NotSpecified"; + } + } + private bool TryReadByteString(TdsParserStateObject stateObj, out string value) { value = string.Empty; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs index cc57d64639..e0656bf9ef 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs @@ -4334,7 +4334,43 @@ internal static string TCE_EnclaveProviderNotFound { return ResourceManager.GetString("TCE_EnclaveProviderNotFound", resourceCulture); } } - + + /// + /// Looks up a localized string similar to Specifies an attestation protocol for its corresponding enclave attestation service. + /// + internal static string TCE_DbConnectionString_AttestationProtocol { + get { + return ResourceManager.GetString("TCE_DbConnectionString_AttestationProtocol", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The enclave type '{0}' returned from the server is not supported. + /// + internal static string TCE_EnclaveTypeNotSupported { + get { + return ResourceManager.GetString("TCE_EnclaveTypeNotSupported", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to initialize connection. The attestation protocol does not support the enclave type '{0}'. + /// + internal static string TCE_AttestationProtocolNotSupportEnclaveType { + get { + return ResourceManager.GetString("TCE_AttestationProtocolNotSupportEnclaveType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Error occured when generating enclave package. Attestation Protocol has not been specified in the connection string, but the query requires enclave computations. + /// + internal static string TCE_AttestationProtocolNotSpecifiedForGeneratingEnclavePackage { + get { + return ResourceManager.GetString("TCE_AttestationProtocolNotSpecifiedForGeneratingEnclavePackage", resourceCulture); + } + } + /// /// Looks up a localized string similar to Executing a query requires enclave computations, but the application configuration is missing the enclave provider section.. /// diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx index ea5834018a..b779db2554 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx @@ -1654,7 +1654,7 @@ You have specified the enclave attestation URL in the connection string, but the SQL Server instance in use does not support enclave based computations. - No enclave provider found for enclave type '{0}'. Please specify the provider in the application configuration. + No enclave provider found for enclave type '{0}' and attestation protocol '{1}'. Please specify the correct attestation protocol in the connection string. Executing a query requires enclave computations, but the application configuration is missing the enclave provider section. @@ -1836,4 +1836,16 @@ Failed to build a chain of trust between the enclave host's health report and the HGS root certificate for attestation URL '{0}' with status: '{1}'. Verify the attestation URL matches the URL configured on the SQL Server machine. If both the client and SQL Server use the same attestation service, contact Customer Support Services. + + Specifies an attestation protocol for its corresponding enclave attestation service. + + + The enclave type '{0}' returned from the server is not supported. + + + Failed to initialize connection. The attestation protocol does not support the enclave type '{0}'. + + + Error occured when generating enclave package. Attestation Protocol has not been specified in the connection string, but the query requires enclave computations. + \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs index 7bdf736858..b0d7d94442 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs @@ -1737,7 +1737,7 @@ static internal Exception EnclaveTypeNotReturned() static internal Exception EnclaveTypeNotSupported(string enclaveType) { - return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_SequentialAccessNotSupportedOnEncryptedColumn, enclaveType)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_EnclaveTypeNotSupported, enclaveType)); } static internal Exception AttestationProtocolNotSupportEnclaveType(string attestationProtocolStr, string enclaveType) From 335e6f0c47a98c82b4d07180d138b391a39b188b Mon Sep 17 00:00:00 2001 From: Javad Rahnama Date: Wed, 16 Oct 2019 16:43:23 -0700 Subject: [PATCH 22/63] setting up the test for Attestation Protocol in SqlConnection --- .../ConnectionStringBuilderShould.cs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs index 4cbc734230..914f092b5a 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs @@ -280,6 +280,19 @@ public void TestSqlConnectionStringBuilderShouldSerialize(SqlConnectionColumnEnc // Query the property to check if the above add was effective. Assert.False(connectionStringBuilder.ShouldSerialize(@"Enclave Attestation Url")); + + string protocol = "HGS"; + //Use the Add function to update the column Encryption Setting in the dictionary. + connectionStringBuilder.Add(@"Attestation Protocol", protocol); + + // Query the ShouldSerialize method to check if the above add was effective. + Assert.True(connectionStringBuilder.ShouldSerialize(@"Attestation Protocol")); + + // Use the Remove function to Remove the Column Encryption Setting from the dictionary. + connectionStringBuilder.Remove(@"Attestation Protocol"); + + // Query the property to check if the above add was effective. + Assert.False(connectionStringBuilder.ShouldSerialize(@"Attestation Protocol")); } /// @@ -300,6 +313,25 @@ private void VerifyEnclaveAttestationUrlSetting(SqlConnectionStringBuilder conne } } + /// + /// Verifies expected Attestation Protocol value for SqlConnectionColumnEncryptionSetting. + /// + /// + /// + private void VerifyAttestationProtocol(SqlConnectionStringBuilder connectionStringBuilder, string expectedAttestationProtocol) + { + string connectionString = connectionStringBuilder.ToString(); + using (SqlConnection sqlConnection = new SqlConnection(connectionString)) + { + string currentAttestationProtocol = (string)typeof(SqlConnection) + .GetProperty(@"Attestation Protocol", BindingFlags.Instance | BindingFlags.NonPublic) + .GetValue(sqlConnection); + + Assert.Equal(expectedAttestationProtocol, currentAttestationProtocol); + + } + } + /// /// Verify the expected setting value for SqlConnectionColumnEncryptionSetting. /// From f3f1759859a4e458e845e66925e951fb94595140 Mon Sep 17 00:00:00 2001 From: Karina Zhou Date: Wed, 16 Oct 2019 16:59:07 -0700 Subject: [PATCH 23/63] Fix error message and add dependencies --- .../src/Microsoft/Data/SqlClient/SqlConnectionString.cs | 7 ++----- .../netcore/src/Resources/SR.Designer.cs | 2 +- src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx | 2 +- tools/specs/Microsoft.Data.SqlClient.nuspec | 2 ++ 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionString.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionString.cs index 9cf93665dc..f642e84a64 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionString.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionString.cs @@ -722,17 +722,14 @@ internal SqlConnectionColumnEncryptionSetting ConvertValueToColumnEncryptionSett /// internal SqlConnectionAttestationProtocol ConvertValueToAttestationProtocol() { - object value = base.Parsetable[KEY.AttestationProtocol]; - - string valStr = value as string; - if (valStr == null) + if (!TryGetParsetableValue(KEY.AttestationProtocol, out string value)) { return DEFAULT.AttestationProtocol; } try { - return DbConnectionStringBuilderUtil.ConvertToAttestationProtocol(KEY.AttestationProtocol, valStr); + return DbConnectionStringBuilderUtil.ConvertToAttestationProtocol(KEY.AttestationProtocol, value); } catch (FormatException e) { diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs index e0656bf9ef..3f38515c5e 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs @@ -4354,7 +4354,7 @@ internal static string TCE_EnclaveTypeNotSupported { } /// - /// Looks up a localized string similar to Failed to initialize connection. The attestation protocol does not support the enclave type '{0}'. + /// Looks up a localized string similar to Failed to initialize connection. The attestation protocol '{0}' does not support the enclave type '{1}'. /// internal static string TCE_AttestationProtocolNotSupportEnclaveType { get { diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx index b779db2554..cd79f44b12 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx @@ -1843,7 +1843,7 @@ The enclave type '{0}' returned from the server is not supported. - Failed to initialize connection. The attestation protocol does not support the enclave type '{0}'. + Failed to initialize connection. The attestation protocol '{0}' does not support the enclave type '{1}'. Error occured when generating enclave package. Attestation Protocol has not been specified in the connection string, but the query requires enclave computations. diff --git a/tools/specs/Microsoft.Data.SqlClient.nuspec b/tools/specs/Microsoft.Data.SqlClient.nuspec index decbb9f59f..09ed140a16 100644 --- a/tools/specs/Microsoft.Data.SqlClient.nuspec +++ b/tools/specs/Microsoft.Data.SqlClient.nuspec @@ -41,6 +41,8 @@ When using NuGet 3.x this package requires at least version 3.4. + + From 0a4f33408cd4d170c6aeb46be6c7540fe3f3c3d9 Mon Sep 17 00:00:00 2001 From: v-jizho2 Date: Wed, 16 Oct 2019 17:05:14 -0700 Subject: [PATCH 24/63] Add enclave support in .NET Core (#270) * make attestation stuff internal * Add AE enclave support in .NET Core * Fix error message and add dependencies --- .../Microsoft.Data.SqlClient.NetCoreApp.cs | 19 +++ .../Data/Common/DbConnectionStringCommon.cs | 132 ++++++++++++++++++ ...ncryptedEnclaveProviderUtils.NetCoreApp.cs | 1 - .../SqlClient/EnclaveDelegate.NetCoreApp.cs | 124 +++++++++++++++- .../SqlClient/EnclaveDelegate.NetStandard.cs | 24 +++- .../Data/SqlClient/EnclaveDelegate.cs | 78 ----------- .../EnclaveProviderBase.NetCoreApp.cs | 1 + .../Microsoft/Data/SqlClient/SqlCommand.cs | 24 +++- .../Microsoft/Data/SqlClient/SqlConnection.cs | 12 ++ .../Data/SqlClient/SqlConnectionString.cs | 32 +++++ .../SqlClient/SqlConnectionStringBuilder.cs | 47 +++++++ .../src/Microsoft/Data/SqlClient/SqlUtil.cs | 20 +++ .../src/Microsoft/Data/SqlClient/TdsEnums.cs | 23 +++ .../src/Microsoft/Data/SqlClient/TdsParser.cs | 52 +++++++ .../netcore/src/Resources/SR.Designer.cs | 38 ++++- .../netcore/src/Resources/SR.resx | 14 +- .../AzureAttestationBasedEnclaveProvider.cs | 1 - .../src/Microsoft/Data/SqlClient/SqlUtil.cs | 2 +- tools/specs/Microsoft.Data.SqlClient.nuspec | 2 + 19 files changed, 552 insertions(+), 94 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.NetCoreApp.cs index 9caeac95c3..a5a8df9df8 100644 --- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.NetCoreApp.cs @@ -100,6 +100,25 @@ public enum SqlConnectionColumnEncryptionSetting /// Enabled = 1, } + /// + /// To add include file for docs + /// + public enum SqlConnectionAttestationProtocol + { + /// + /// To add include file for docs + /// + NotSpecified = 0, + /// + /// To add include file for docs + /// + AAS = 1, + /// + /// To add include file for docs + /// + HGS = 3 + } + /// public partial class SqlColumnEncryptionCertificateStoreProvider : Microsoft.Data.SqlClient.SqlColumnEncryptionKeyStoreProvider { diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs index 34ed70bb42..1857d76064 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs @@ -214,6 +214,136 @@ internal static string ColumnEncryptionSettingToString(SqlConnectionColumnEncryp } } + #region <> + + /// + /// Attestation Protocol. + /// + const string AttestationProtocolHGS = "HGS"; + const string AttestationProtocolAAS = "AAS"; + + /// + /// Convert a string value to the corresponding SqlConnectionAttestationProtocol + /// + /// + /// + /// + internal static bool TryConvertToAttestationProtocol(string value, out SqlConnectionAttestationProtocol result) + { + bool isSuccess = false; + + if (StringComparer.InvariantCultureIgnoreCase.Equals(value, AttestationProtocolHGS)) + { + result = SqlConnectionAttestationProtocol.HGS; + isSuccess = true; + } + else if (StringComparer.InvariantCultureIgnoreCase.Equals(value, AttestationProtocolAAS)) + { + result = SqlConnectionAttestationProtocol.AAS; + isSuccess = true; + } + else + { + result = DbConnectionStringDefaults.AttestationProtocol; + } + + return isSuccess; + } + + internal static bool IsValidAttestationProtocol(SqlConnectionAttestationProtocol value) + { + Debug.Assert(Enum.GetNames(typeof(SqlConnectionAttestationProtocol)).Length == 3, "SqlConnectionAttestationProtocol enum has changed, update needed"); + return value == SqlConnectionAttestationProtocol.NotSpecified + || value == SqlConnectionAttestationProtocol.HGS + || value == SqlConnectionAttestationProtocol.AAS; + + } + + internal static string AttestationProtocolToString(SqlConnectionAttestationProtocol value) + { + Debug.Assert(IsValidAttestationProtocol(value), "value is not a valid attestation protocol"); + + switch (value) + { + case SqlConnectionAttestationProtocol.HGS: + return AttestationProtocolHGS; + case SqlConnectionAttestationProtocol.AAS: + return AttestationProtocolAAS; + default: + return null; + } + } + + internal static SqlConnectionAttestationProtocol ConvertToAttestationProtocol(string keyword, object value) + { + if (null == value) + { + return DbConnectionStringDefaults.AttestationProtocol; + } + + string sValue = (value as string); + SqlConnectionAttestationProtocol result; + + if (null != sValue) + { + if (TryConvertToAttestationProtocol(sValue, out result)) + { + return result; + } + + // try again after remove leading & trailing whitespaces. + sValue = sValue.Trim(); + if (TryConvertToAttestationProtocol(sValue, out result)) + { + return result; + } + + // string values must be valid + throw ADP.InvalidConnectionOptionValue(keyword); + } + else + { + // the value is not string, try other options + SqlConnectionAttestationProtocol eValue; + + if (value is SqlConnectionAttestationProtocol) + { + eValue = (SqlConnectionAttestationProtocol)value; + } + else if (value.GetType().IsEnum) + { + // explicitly block scenarios in which user tries to use wrong enum types, like: + // builder["SqlConnectionAttestationProtocol"] = EnvironmentVariableTarget.Process; + // workaround: explicitly cast non-SqlConnectionAttestationProtocol enums to int + throw ADP.ConvertFailed(value.GetType(), typeof(SqlConnectionAttestationProtocol), null); + } + else + { + try + { + // Enum.ToObject allows only integral and enum values (enums are blocked above), rasing ArgumentException for the rest + eValue = (SqlConnectionAttestationProtocol)Enum.ToObject(typeof(SqlConnectionAttestationProtocol), value); + } + catch (ArgumentException e) + { + // to be consistent with the messages we send in case of wrong type usage, replace + // the error with our exception, and keep the original one as inner one for troubleshooting + throw ADP.ConvertFailed(value.GetType(), typeof(SqlConnectionAttestationProtocol), e); + } + } + + if (IsValidAttestationProtocol(eValue)) + { + return eValue; + } + else + { + throw ADP.InvalidEnumerationValue(typeof(SqlConnectionAttestationProtocol), (int)eValue); + } + } + } + + #endregion internal static bool IsValidApplicationIntentValue(ApplicationIntent value) { @@ -524,6 +654,7 @@ internal static partial class DbConnectionStringDefaults internal static readonly SqlAuthenticationMethod Authentication = SqlAuthenticationMethod.NotSpecified; internal const SqlConnectionColumnEncryptionSetting ColumnEncryptionSetting = SqlConnectionColumnEncryptionSetting.Disabled; internal const string EnclaveAttestationUrl = ""; + internal static readonly SqlConnectionAttestationProtocol AttestationProtocol = SqlConnectionAttestationProtocol.NotSpecified; } @@ -559,6 +690,7 @@ internal static partial class DbConnectionStringKeywords internal const string Authentication = "Authentication"; internal const string ColumnEncryptionSetting = "Column Encryption Setting"; internal const string EnclaveAttestationUrl = "Enclave Attestation Url"; + internal const string AttestationProtocol = "Attestation Protocol"; // common keywords (OleDb, OracleClient, SqlClient) internal const string DataSource = "Data Source"; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.NetCoreApp.cs index 53b3d14b81..066d33be25 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.NetCoreApp.cs @@ -17,7 +17,6 @@ public EnclavePublicKey(byte[] payload) } } - // Class to hold the Enclave's Diffie-Hellman public key and signature internal class EnclaveDiffieHellmanInfo { public int Size { get; private set; } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetCoreApp.cs index 0947c1ad3b..9a5cbf62a1 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetCoreApp.cs @@ -1,7 +1,8 @@ // 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.Security.Cryptography; namespace Microsoft.Data.SqlClient @@ -13,6 +14,8 @@ internal partial class EnclaveDelegate { private static readonly string GetSerializedAttestationParametersName = "GetSerializedAttestationParameters"; + private static Dictionary EnclaveProviders = new Dictionary(); + internal byte[] GetSerializedAttestationParameters(SqlEnclaveAttestationParameters sqlEnclaveAttestationParameters, string enclaveType) { byte[] attestationProtocolBytes = null; @@ -56,18 +59,19 @@ internal byte[] GetSerializedAttestationParameters(SqlEnclaveAttestationParamete /// /// Create a new enclave session /// + /// attestation protocol /// enclave type /// servername /// attestation url for attestation service endpoint /// attestation info from SQL Server /// attestation parameters - internal void CreateEnclaveSession(string enclaveType, string serverName, string attestationUrl, + internal void CreateEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, string serverName, string attestationUrl, byte[] attestationInfo, SqlEnclaveAttestationParameters attestationParameters) { lock (_lock) { - SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = GetEnclaveProvider(enclaveType); + SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = GetEnclaveProvider(attestationProtocol, enclaveType); long counter; SqlEnclaveSession sqlEnclaveSession = null; sqlColumnEncryptionEnclaveProvider.GetEnclaveSession(serverName, attestationUrl, out sqlEnclaveSession, out counter); @@ -82,5 +86,119 @@ internal void CreateEnclaveSession(string enclaveType, string serverName, string if (sqlEnclaveSession == null) throw SQL.NullEnclaveSessionReturnedFromProvider(enclaveType, attestationUrl); } } + + /// + /// Generate the byte package that needs to be sent to the enclave + /// + /// attestation protocol + /// Keys to be sent to enclave + /// + /// enclave type + /// server name + /// url for attestation endpoint + /// + internal EnclavePackage GenerateEnclavePackage(SqlConnectionAttestationProtocol attestationProtocol, Dictionary keysTobeSentToEnclave, string queryText, string enclaveType, string serverName, string enclaveAttestationUrl) + { + + SqlEnclaveSession sqlEnclaveSession = null; + long counter; + try + { + GetEnclaveSession(attestationProtocol, enclaveType, serverName, enclaveAttestationUrl, out sqlEnclaveSession, out counter, throwIfNull: true); + } + catch (Exception e) + { + throw new RetriableEnclaveQueryExecutionException(e.Message, e); + } + + List decryptedKeysToBeSentToEnclave = GetDecryptedKeysToBeSentToEnclave(keysTobeSentToEnclave, serverName); + byte[] queryStringHashBytes = ComputeQueryStringHash(queryText); + byte[] keyBytePackage = GenerateBytePackageForKeys(counter, queryStringHashBytes, decryptedKeysToBeSentToEnclave); + byte[] sessionKey = sqlEnclaveSession.GetSessionKey(); + byte[] encryptedBytePackage = EncryptBytePackage(keyBytePackage, sessionKey, serverName); + byte[] enclaveSessionHandle = BitConverter.GetBytes(sqlEnclaveSession.SessionId); + byte[] byteArrayToBeSentToEnclave = CombineByteArrays(new[] { enclaveSessionHandle, encryptedBytePackage }); + return new EnclavePackage(byteArrayToBeSentToEnclave, sqlEnclaveSession); + } + + internal void InvalidateEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, string serverName, string EnclaveAttestationUrl, SqlEnclaveSession enclaveSession) + { + SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = GetEnclaveProvider(attestationProtocol, enclaveType); + sqlColumnEncryptionEnclaveProvider.InvalidateEnclaveSession(serverName, EnclaveAttestationUrl, enclaveSession); + } + + + internal SqlEnclaveAttestationParameters GetAttestationParameters(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, string serverName, string enclaveAttestationUrl) + { + SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = GetEnclaveProvider(attestationProtocol, enclaveType); + return sqlColumnEncryptionEnclaveProvider.GetAttestationParameters(); + } + + private SqlColumnEncryptionEnclaveProvider GetEnclaveProvider(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType) + { + SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = null; + + if (!EnclaveProviders.TryGetValue(attestationProtocol, out sqlColumnEncryptionEnclaveProvider)) + { + switch (attestationProtocol) + { + case SqlConnectionAttestationProtocol.AAS: + AzureAttestationEnclaveProvider azureAttestationEnclaveProvider = new AzureAttestationEnclaveProvider(); + EnclaveProviders[attestationProtocol] = (SqlColumnEncryptionEnclaveProvider)azureAttestationEnclaveProvider; + sqlColumnEncryptionEnclaveProvider = EnclaveProviders[attestationProtocol]; + break; + + case SqlConnectionAttestationProtocol.HGS: + HostGuardianServiceEnclaveProvider hostGuardianServiceEnclaveProvider = new HostGuardianServiceEnclaveProvider(); + EnclaveProviders[attestationProtocol] = (SqlColumnEncryptionEnclaveProvider)hostGuardianServiceEnclaveProvider; + sqlColumnEncryptionEnclaveProvider = EnclaveProviders[attestationProtocol]; + break; + + default: + break; + } + } + + if (sqlColumnEncryptionEnclaveProvider == null) + { + throw SQL.EnclaveProviderNotFound(enclaveType, ConvertAttestationProtocolToString(attestationProtocol)); + } + + return sqlColumnEncryptionEnclaveProvider; + } + + private string ConvertAttestationProtocolToString(SqlConnectionAttestationProtocol attestationProtocol) + { + switch (attestationProtocol) + { + case SqlConnectionAttestationProtocol.AAS: + return "AAS"; + + case SqlConnectionAttestationProtocol.HGS: + return "HGS"; + + default: + return "NotSpecified"; + } + } + + internal void GetEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, string serverName, string enclaveAttestationUrl, out SqlEnclaveSession sqlEnclaveSession) + { + long counter; + GetEnclaveSession(attestationProtocol, enclaveType, serverName, enclaveAttestationUrl, out sqlEnclaveSession, out counter, throwIfNull: false); + } + + // kz to move to netcoreapp + private void GetEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, string serverName, string enclaveAttestationUrl, out SqlEnclaveSession sqlEnclaveSession, out long counter, bool throwIfNull) + { + SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = GetEnclaveProvider(attestationProtocol, enclaveType); + sqlColumnEncryptionEnclaveProvider.GetEnclaveSession(serverName, enclaveAttestationUrl, out sqlEnclaveSession, out counter); + + if (throwIfNull) + { + if (sqlEnclaveSession == null) + throw SQL.NullEnclaveSessionDuringQueryExecution(enclaveType, enclaveAttestationUrl); + } + } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetStandard.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetStandard.cs index f7c56f0155..91f536637d 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetStandard.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetStandard.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; namespace Microsoft.Data.SqlClient { @@ -20,15 +21,36 @@ internal byte[] GetSerializedAttestationParameters( /// /// Create a new enclave session /// + /// attestation protocol /// enclave type /// servername /// attestation url for attestation service endpoint /// attestation info from SQL Server /// attestation parameters - internal void CreateEnclaveSession(string enclaveType, string serverName, string attestationUrl, + internal void CreateEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, string serverName, string attestationUrl, byte[] attestationInfo, SqlEnclaveAttestationParameters attestationParameters) { throw new PlatformNotSupportedException(); } + + internal void GetEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, string serverName, string enclaveAttestationUrl, out SqlEnclaveSession sqlEnclaveSession) + { + throw new PlatformNotSupportedException(); + } + + internal EnclavePackage GenerateEnclavePackage(SqlConnectionAttestationProtocol attestationProtocol, Dictionary keysTobeSentToEnclave, string queryText, string enclaveType, string serverName, string enclaveAttestationUrl) + { + throw new PlatformNotSupportedException(); + } + + internal void InvalidateEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, string serverName, string EnclaveAttestationUrl, SqlEnclaveSession enclaveSession) + { + throw new PlatformNotSupportedException(); + } + + internal SqlEnclaveAttestationParameters GetAttestationParameters(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, string serverName, string enclaveAttestationUrl) + { + throw new PlatformNotSupportedException(); + } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs index 89fd92c0de..3d3ef1cab0 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs @@ -28,70 +28,6 @@ internal partial class EnclaveDelegate private EnclaveDelegate() { } - /// - /// Generate the byte package that needs to be sent to the enclave - /// - /// Keys to be sent to enclave - /// - /// enclave type - /// server name - /// url for attestation endpoint - /// - internal EnclavePackage GenerateEnclavePackage(Dictionary keysTobeSentToEnclave, string queryText, string enclaveType, string serverName, string enclaveAttestationUrl) - { - - SqlEnclaveSession sqlEnclaveSession = null; - long counter; - try - { - GetEnclaveSession(enclaveType, serverName, enclaveAttestationUrl, out sqlEnclaveSession, out counter, throwIfNull: true); - } - catch (Exception e) - { - throw new RetriableEnclaveQueryExecutionException(e.Message, e); - } - - List decryptedKeysToBeSentToEnclave = GetDecryptedKeysToBeSentToEnclave(keysTobeSentToEnclave, serverName); - byte[] queryStringHashBytes = ComputeQueryStringHash(queryText); - byte[] keyBytePackage = GenerateBytePackageForKeys(counter, queryStringHashBytes, decryptedKeysToBeSentToEnclave); - byte[] sessionKey = sqlEnclaveSession.GetSessionKey(); - byte[] encryptedBytePackage = EncryptBytePackage(keyBytePackage, sessionKey, serverName); - byte[] enclaveSessionHandle = BitConverter.GetBytes(sqlEnclaveSession.SessionId); - byte[] byteArrayToBeSentToEnclave = CombineByteArrays(new[] { enclaveSessionHandle, encryptedBytePackage }); - return new EnclavePackage(byteArrayToBeSentToEnclave, sqlEnclaveSession); - } - - internal void InvalidateEnclaveSession(string enclaveType, string serverName, string EnclaveAttestationUrl, SqlEnclaveSession enclaveSession) - { - SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = GetEnclaveProvider(enclaveType); - sqlColumnEncryptionEnclaveProvider.InvalidateEnclaveSession(serverName, EnclaveAttestationUrl, enclaveSession); - } - - internal void GetEnclaveSession(string enclaveType, string serverName, string enclaveAttestationUrl, out SqlEnclaveSession sqlEnclaveSession) - { - long counter; - GetEnclaveSession(enclaveType, serverName, enclaveAttestationUrl, out sqlEnclaveSession, out counter, throwIfNull: false); - } - - private void GetEnclaveSession(string enclaveType, string serverName, string enclaveAttestationUrl, out SqlEnclaveSession sqlEnclaveSession, out long counter, bool throwIfNull) - { - SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = GetEnclaveProvider(enclaveType); - sqlColumnEncryptionEnclaveProvider.GetEnclaveSession(serverName, enclaveAttestationUrl, out sqlEnclaveSession, out counter); - - if (throwIfNull) - { - if (sqlEnclaveSession == null) - throw SQL.NullEnclaveSessionDuringQueryExecution(enclaveType, enclaveAttestationUrl); - } - } - - internal SqlEnclaveAttestationParameters GetAttestationParameters(string enclaveType, string serverName, string enclaveAttestationUrl) - { - SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = GetEnclaveProvider(enclaveType); - return sqlColumnEncryptionEnclaveProvider.GetAttestationParameters(); - } - - private byte[] GetUintBytes(string enclaveType, int intValue, string variableName) { try @@ -106,20 +42,6 @@ private byte[] GetUintBytes(string enclaveType, int intValue, string variableNam } } - private SqlColumnEncryptionEnclaveProvider GetEnclaveProvider(string enclaveType) - { - if (SqlConnection.sqlColumnEncryptionEnclaveProviderConfigurationManager == null) - throw SQL.EnclaveProvidersNotConfiguredForEnclaveBasedQuery(); - - var sqlColumnEncryptionEnclaveProvider = - SqlConnection.sqlColumnEncryptionEnclaveProviderConfigurationManager.GetSqlColumnEncryptionEnclaveProvider( - enclaveType); - - if (sqlColumnEncryptionEnclaveProvider == null) - throw SQL.EnclaveProviderNotFound(enclaveType); - return sqlColumnEncryptionEnclaveProvider; - } - /// /// Decrypt the keys that need to be sent to the enclave /// diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveProviderBase.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveProviderBase.NetCoreApp.cs index f252670cbc..456224a068 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveProviderBase.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveProviderBase.NetCoreApp.cs @@ -66,6 +66,7 @@ namespace Microsoft.Data.SqlClient { // Base class for Enclave provider + internal abstract class EnclaveProviderBase : SqlColumnEncryptionEnclaveProvider { #region Constants 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 1b5b34a1a5..17eb88d565 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 @@ -2041,7 +2041,7 @@ private void CreateLocalCompletionTask(CommandBehavior behavior, object stateObj if (ShouldUseEnclaveBasedWorkflow && this.enclavePackage != null) { - EnclaveDelegate.Instance.InvalidateEnclaveSession(this._activeConnection.Parser.EnclaveType, + EnclaveDelegate.Instance.InvalidateEnclaveSession(this._activeConnection.AttestationProtocol, this._activeConnection.Parser.EnclaveType, this._activeConnection.DataSource, this._activeConnection.EnclaveAttestationUrl, this.enclavePackage.EnclaveSession); } @@ -3349,14 +3349,15 @@ private SqlDataReader TryFetchInputParameterEncryptionInfo(int timeout, if (ShouldUseEnclaveBasedWorkflow) { + SqlConnectionAttestationProtocol attestationProtocol = this._activeConnection.AttestationProtocol; string enclaveType = this._activeConnection.Parser.EnclaveType; string dataSource = this._activeConnection.DataSource; string enclaveAttestationUrl = this._activeConnection.EnclaveAttestationUrl; SqlEnclaveSession sqlEnclaveSession = null; - EnclaveDelegate.Instance.GetEnclaveSession(enclaveType, dataSource, enclaveAttestationUrl, out sqlEnclaveSession); + EnclaveDelegate.Instance.GetEnclaveSession(attestationProtocol, enclaveType, dataSource, enclaveAttestationUrl, out sqlEnclaveSession); if (sqlEnclaveSession == null) { - enclaveAttestationParameters = EnclaveDelegate.Instance.GetAttestationParameters(enclaveType, dataSource, enclaveAttestationUrl); + enclaveAttestationParameters = EnclaveDelegate.Instance.GetAttestationParameters(attestationProtocol, enclaveType, dataSource, enclaveAttestationUrl); serializedAttestatationParameters = EnclaveDelegate.Instance.GetSerializedAttestationParameters(enclaveAttestationParameters, enclaveType); } } @@ -3858,11 +3859,12 @@ private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDi byte[] attestationInfo = new byte[attestationInfoLength]; ds.GetBytes((int)DescribeParameterEncryptionResultSet3.AttestationInfo, 0, attestationInfo, 0, attestationInfoLength); + SqlConnectionAttestationProtocol attestationProtocol = this._activeConnection.AttestationProtocol; string enclaveType = this._activeConnection.Parser.EnclaveType; string dataSource = this._activeConnection.DataSource; string enclaveAttestationUrl = this._activeConnection.EnclaveAttestationUrl; - EnclaveDelegate.Instance.CreateEnclaveSession(enclaveType, dataSource, enclaveAttestationUrl, attestationInfo, enclaveAttestationParameters); + EnclaveDelegate.Instance.CreateEnclaveSession(attestationProtocol, enclaveType, dataSource, enclaveAttestationUrl, attestationInfo, enclaveAttestationParameters); enclaveAttestationParameters = null; attestationInfoRead = true; } @@ -3975,7 +3977,8 @@ internal SqlDataReader RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior if (ShouldUseEnclaveBasedWorkflow && this.enclavePackage != null) { - EnclaveDelegate.Instance.InvalidateEnclaveSession(this._activeConnection.Parser.EnclaveType, this._activeConnection.DataSource, this._activeConnection.EnclaveAttestationUrl, this.enclavePackage.EnclaveSession); + EnclaveDelegate.Instance.InvalidateEnclaveSession(this._activeConnection.AttestationProtocol, this._activeConnection.Parser.EnclaveType, + this._activeConnection.DataSource, this._activeConnection.EnclaveAttestationUrl, this.enclavePackage.EnclaveSession); } return RunExecuteReader(cmdBehavior, runBehavior, returnStream, null, TdsParserStaticMethods.GetRemainingTimeout(timeout, firstAttemptStart), out task, out usedCache, isAsync, inRetry: true, method: method); @@ -4017,7 +4020,8 @@ internal SqlDataReader RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior if (ShouldUseEnclaveBasedWorkflow && this.enclavePackage != null) { - EnclaveDelegate.Instance.InvalidateEnclaveSession(this._activeConnection.Parser.EnclaveType, this._activeConnection.DataSource, this._activeConnection.EnclaveAttestationUrl, this.enclavePackage.EnclaveSession); + EnclaveDelegate.Instance.InvalidateEnclaveSession(this._activeConnection.AttestationProtocol, this._activeConnection.Parser.EnclaveType, + this._activeConnection.DataSource, this._activeConnection.EnclaveAttestationUrl, this.enclavePackage.EnclaveSession); } return RunExecuteReader(cmdBehavior, runBehavior, returnStream, null, TdsParserStaticMethods.GetRemainingTimeout(timeout, firstAttemptStart), out task, out usedCache, isAsync, inRetry: true, method: method); @@ -4114,9 +4118,15 @@ private void GenerateEnclavePackage() if (string.IsNullOrWhiteSpace(enclaveType)) throw SQL.EnclaveTypeNullForEnclaveBasedQuery(); + SqlConnectionAttestationProtocol attestationProtocol = this._activeConnection.AttestationProtocol; + if (attestationProtocol == SqlConnectionAttestationProtocol.NotSpecified) + { + throw SQL.AttestationProtocolNotSpecifiedForGeneratingEnclavePackage(); + } + try { - this.enclavePackage = EnclaveDelegate.Instance.GenerateEnclavePackage(keysToBeSentToEnclave, + this.enclavePackage = EnclaveDelegate.Instance.GenerateEnclavePackage(attestationProtocol, keysToBeSentToEnclave, this.CommandText, enclaveType, this._activeConnection.DataSource, this._activeConnection.EnclaveAttestationUrl); } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs index 51ccc824b6..20e32310f4 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -276,6 +276,18 @@ public static void RegisterColumnEncryptionKeyStoreProviders(IDictionary internal string EnclaveAttestationUrl => ((SqlConnectionString)ConnectionOptions).EnclaveAttestationUrl; + /// + /// Get attestation protocol + /// + internal SqlConnectionAttestationProtocol AttestationProtocol + { + get + { + SqlConnectionString opt = (SqlConnectionString)ConnectionOptions; + return opt.AttestationProtocol; + } + } + // This method will be called once connection string is set or changed. private void CacheConnectionStringProperties() { diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionString.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionString.cs index ee88cd3817..f642e84a64 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionString.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionString.cs @@ -48,6 +48,7 @@ internal static partial class DEFAULT internal static readonly SqlAuthenticationMethod Authentication = SqlAuthenticationMethod.NotSpecified; internal const SqlConnectionColumnEncryptionSetting ColumnEncryptionSetting = SqlConnectionColumnEncryptionSetting.Disabled; internal const string EnclaveAttestationUrl = ""; + internal static readonly SqlConnectionAttestationProtocol AttestationProtocol = SqlConnectionAttestationProtocol.NotSpecified; } // SqlConnection ConnectionString Options @@ -63,6 +64,7 @@ internal static class KEY #endif internal const string ColumnEncryptionSetting = "column encryption setting"; internal const string EnclaveAttestationUrl = "enclave attestation url"; + internal const string AttestationProtocol = "attestation protocol"; internal const string Connect_Timeout = "connect timeout"; internal const string Connection_Reset = "connection reset"; internal const string Context_Connection = "context connection"; @@ -186,6 +188,7 @@ internal static class TRANSACTIONBINDING private readonly SqlAuthenticationMethod _authType; private readonly SqlConnectionColumnEncryptionSetting _columnEncryptionSetting; private readonly string _enclaveAttestationUrl; + private readonly SqlConnectionAttestationProtocol _attestationProtocol; private readonly int _connectTimeout; private readonly int _loadBalanceTimeout; @@ -261,6 +264,7 @@ internal SqlConnectionString(string connectionString) : base(connectionString, G _authType = ConvertValueToAuthenticationType(); _columnEncryptionSetting = ConvertValueToColumnEncryptionSetting(); _enclaveAttestationUrl = ConvertValueToString(KEY.EnclaveAttestationUrl, DEFAULT.EnclaveAttestationUrl); + _attestationProtocol = ConvertValueToAttestationProtocol(); // Temporary string - this value is stored internally as an enum. string typeSystemVersionString = ConvertValueToString(KEY.Type_System_Version, null); @@ -475,6 +479,7 @@ internal SqlConnectionString(SqlConnectionString connectionOptions, string dataS _authType = connectionOptions._authType; _columnEncryptionSetting = connectionOptions._columnEncryptionSetting; _enclaveAttestationUrl = connectionOptions._enclaveAttestationUrl; + _attestationProtocol = connectionOptions._attestationProtocol; ValidateValueLength(_dataSource, TdsEnums.MAXLEN_SERVERNAME, KEY.Data_Source); } @@ -495,6 +500,7 @@ internal SqlConnectionString(SqlConnectionString connectionOptions, string dataS internal SqlAuthenticationMethod Authentication { get { return _authType; } } internal SqlConnectionColumnEncryptionSetting ColumnEncryptionSetting { get { return _columnEncryptionSetting; } } internal string EnclaveAttestationUrl { get { return _enclaveAttestationUrl; } } + internal SqlConnectionAttestationProtocol AttestationProtocol { get { return _attestationProtocol; } } internal bool PersistSecurityInfo { get { return _persistSecurityInfo; } } internal bool Pooling { get { return _pooling; } } internal bool Replication { get { return _replication; } } @@ -568,6 +574,7 @@ internal static Dictionary GetParseSynonyms() { KEY.Type_System_Version, KEY.Type_System_Version }, { KEY.ColumnEncryptionSetting, KEY.ColumnEncryptionSetting }, { KEY.EnclaveAttestationUrl, KEY.EnclaveAttestationUrl }, + { KEY.AttestationProtocol, KEY.AttestationProtocol}, { KEY.User_ID, KEY.User_ID }, { KEY.User_Instance, KEY.User_Instance }, { KEY.Workstation_Id, KEY.Workstation_Id }, @@ -708,5 +715,30 @@ internal SqlConnectionColumnEncryptionSetting ConvertValueToColumnEncryptionSett throw ADP.InvalidConnectionOptionValue(KEY.ColumnEncryptionSetting, e); } } + + /// + /// Convert the value to SqlConnectionAttestationProtocol + /// + /// + internal SqlConnectionAttestationProtocol ConvertValueToAttestationProtocol() + { + if (!TryGetParsetableValue(KEY.AttestationProtocol, out string value)) + { + return DEFAULT.AttestationProtocol; + } + + try + { + return DbConnectionStringBuilderUtil.ConvertToAttestationProtocol(KEY.AttestationProtocol, value); + } + catch (FormatException e) + { + throw ADP.InvalidConnectionOptionValue(KEY.AttestationProtocol, e); + } + catch (OverflowException e) + { + throw ADP.InvalidConnectionOptionValue(KEY.AttestationProtocol, e); + } + } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs index 4f052fac9e..6a7d4cf7b4 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs @@ -67,6 +67,7 @@ private enum Keywords ColumnEncryptionSetting, EnclaveAttestationUrl, + AttestationProtocol, // keep the count value last KeywordsCount @@ -113,6 +114,7 @@ private enum Keywords private SqlAuthenticationMethod _authentication = DbConnectionStringDefaults.Authentication; private SqlConnectionColumnEncryptionSetting _columnEncryptionSetting = DbConnectionStringDefaults.ColumnEncryptionSetting; private string _enclaveAttestationUrl = DbConnectionStringDefaults.EnclaveAttestationUrl; + private SqlConnectionAttestationProtocol _attestationProtocol = DbConnectionStringDefaults.AttestationProtocol; private static string[] CreateValidKeywords() { @@ -153,6 +155,7 @@ private static string[] CreateValidKeywords() validKeywords[(int)Keywords.Authentication] = DbConnectionStringKeywords.Authentication; validKeywords[(int)Keywords.ColumnEncryptionSetting] = DbConnectionStringKeywords.ColumnEncryptionSetting; validKeywords[(int)Keywords.EnclaveAttestationUrl] = DbConnectionStringKeywords.EnclaveAttestationUrl; + validKeywords[(int)Keywords.AttestationProtocol] = DbConnectionStringKeywords.AttestationProtocol; return validKeywords; } @@ -195,6 +198,7 @@ private static Dictionary CreateKeywordsDictionary() hash.Add(DbConnectionStringKeywords.Authentication, Keywords.Authentication); hash.Add(DbConnectionStringKeywords.ColumnEncryptionSetting, Keywords.ColumnEncryptionSetting); hash.Add(DbConnectionStringKeywords.EnclaveAttestationUrl, Keywords.EnclaveAttestationUrl); + hash.Add(DbConnectionStringKeywords.AttestationProtocol, Keywords.AttestationProtocol); hash.Add(DbConnectionStringSynonyms.APP, Keywords.ApplicationName); hash.Add(DbConnectionStringSynonyms.EXTENDEDPROPERTIES, Keywords.AttachDBFilename); @@ -313,6 +317,9 @@ public override object this[string keyword] case Keywords.EnclaveAttestationUrl: EnclaveAttestationUrl = ConvertToString(value); break; + case Keywords.AttestationProtocol: + AttestationProtocol = ConvertToAttestationProtocol(keyword, value); + break; #if netcoreapp case Keywords.PoolBlockingPeriod: PoolBlockingPeriod = ConvertToPoolBlockingPeriod(keyword, value); break; #endif @@ -475,6 +482,25 @@ public string EnclaveAttestationUrl } } + /// + /// + /// + /// + public SqlConnectionAttestationProtocol AttestationProtocol + { + get { return _attestationProtocol; } + set + { + if (!DbConnectionStringBuilderUtil.IsValidAttestationProtocol(value)) + { + throw ADP.InvalidEnumerationValue(typeof(SqlConnectionAttestationProtocol), (int)value); + } + + SetAttestationProtocolValue(value); + _attestationProtocol = value; + } + } + /// public bool TrustServerCertificate { @@ -850,6 +876,16 @@ private static SqlConnectionColumnEncryptionSetting ConvertToColumnEncryptionSet return DbConnectionStringBuilderUtil.ConvertToColumnEncryptionSetting(keyword, value); } + /// + /// Convert to SqlConnectionAttestationProtocol + /// + /// + /// + private static SqlConnectionAttestationProtocol ConvertToAttestationProtocol(string keyword, object value) + { + return DbConnectionStringBuilderUtil.ConvertToAttestationProtocol(keyword, value); + } + private object GetAt(Keywords index) { switch (index) @@ -922,6 +958,8 @@ private object GetAt(Keywords index) return ColumnEncryptionSetting; case Keywords.EnclaveAttestationUrl: return EnclaveAttestationUrl; + case Keywords.AttestationProtocol: + return AttestationProtocol; default: Debug.Fail("unexpected keyword"); @@ -1064,6 +1102,9 @@ private void Reset(Keywords index) case Keywords.EnclaveAttestationUrl: _enclaveAttestationUrl = DbConnectionStringDefaults.EnclaveAttestationUrl; break; + case Keywords.AttestationProtocol: + _attestationProtocol = DbConnectionStringDefaults.AttestationProtocol; + break; default: Debug.Fail("unexpected keyword"); throw UnsupportedKeyword(s_validKeywords[(int)index]); @@ -1094,6 +1135,12 @@ private void SetColumnEncryptionSettingValue(SqlConnectionColumnEncryptionSettin base[DbConnectionStringKeywords.ColumnEncryptionSetting] = DbConnectionStringBuilderUtil.ColumnEncryptionSettingToString(value); } + private void SetAttestationProtocolValue(SqlConnectionAttestationProtocol value) + { + Debug.Assert(DbConnectionStringBuilderUtil.IsValidAttestationProtocol(value), "Invalid value for SqlConnectionAttestationProtocol"); + base[DbConnectionStringKeywords.AttestationProtocol] = DbConnectionStringBuilderUtil.AttestationProtocolToString(value); + } + private void SetAuthenticationValue(SqlAuthenticationMethod value) { Debug.Assert(DbConnectionStringBuilderUtil.IsValidAuthenticationTypeValue(value), "Invalid value for AuthenticationType"); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs index b5a1293310..58c0a89c4f 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs @@ -1672,6 +1672,26 @@ internal static Exception NullEnclavePackageForEnclaveBasedQuery(string enclaveT return ADP.Argument(System.SRHelper.GetString(SR.TCE_NullEnclavePackageForEnclaveBasedQuery, enclaveType, enclaveAttestationUrl)); } + internal static Exception EnclaveProviderNotFound(string enclaveType, string attestationProtocol) + { + return ADP.InvalidOperation(System.SRHelper.GetString(SR.TCE_EnclaveProviderNotFound, enclaveType, attestationProtocol)); + } + + internal static Exception EnclaveTypeNotSupported(string enclaveType) + { + return ADP.InvalidOperation(System.SRHelper.GetString(SR.TCE_EnclaveTypeNotSupported, enclaveType)); + } + + internal static Exception AttestationProtocolNotSupportEnclaveType(string attestationProtocolStr, string enclaveType) + { + return ADP.InvalidOperation(System.SRHelper.GetString(SR.TCE_AttestationProtocolNotSupportEnclaveType, attestationProtocolStr, enclaveType)); + } + + internal static Exception AttestationProtocolNotSpecifiedForGeneratingEnclavePackage() + { + return ADP.InvalidOperation(System.SRHelper.GetString(SR.TCE_AttestationProtocolNotSpecifiedForGeneratingEnclavePackage)); + } + #endregion Always Encrypted - Client side query processing errors #region Always Encrypted - SQL connection related error messages 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 6ccd9c9eae..902bc0f094 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 @@ -981,6 +981,8 @@ internal static string GetSniContextEnumName(SniContext sniContext) internal const int AES_256_CBC = 1; internal const int AEAD_AES_256_CBC_HMAC_SHA256 = 2; + internal const string ENCLAVE_TYPE_VBS = "VBS"; + internal const string ENCLAVE_TYPE_SGX = "SGX"; // TCE Param names for exec handling internal const string TCE_PARAM_CIPHERTEXT = "cipherText"; @@ -1045,6 +1047,27 @@ internal enum ParsingErrorState DataClassificationInvalidInformationTypeIndex = 27 } + /// + /// + /// + public enum SqlConnectionAttestationProtocol + { + /// + /// If the attestation protocol is not specified. Use this as default value. + /// + NotSpecified = 0, + + /// + /// Attestation portocol for Azure Attestation Service + /// + AAS = 1, + + /// + /// Attestation protocol for Host Guardian Service + /// + HGS = 3 + } + /// public enum SqlConnectionColumnEncryptionSetting { 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 b871e6f420..1fec3e066f 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 @@ -3002,9 +3002,61 @@ private bool TryProcessFeatureExtAck(TdsParserStateObject stateObj) throw SQL.EnclaveTypeNotReturned(); } + // Check if enclave attestation url was specified and the attestation protocol supports the enclave type. + SqlConnectionAttestationProtocol attestationProtocol = _connHandler.ConnectionOptions.AttestationProtocol; + if (this.Connection.RoutingInfo == null + && (!string.IsNullOrWhiteSpace(_connHandler.ConnectionOptions.EnclaveAttestationUrl)) + && (!string.IsNullOrWhiteSpace(EnclaveType)) + && (!IsValidAttestationProtocol(attestationProtocol, EnclaveType))) + { + throw SQL.AttestationProtocolNotSupportEnclaveType(ConvertAttestationProtocolToString(attestationProtocol), EnclaveType); + } + + return true; + } + + private bool IsValidAttestationProtocol(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType) + { + switch (enclaveType) + { + case TdsEnums.ENCLAVE_TYPE_VBS: + if (attestationProtocol != SqlConnectionAttestationProtocol.AAS + && attestationProtocol != SqlConnectionAttestationProtocol.HGS) + { + return false; + } + break; + + case TdsEnums.ENCLAVE_TYPE_SGX: + if (attestationProtocol != SqlConnectionAttestationProtocol.AAS) + { + return false; + } + break; + + default: + // if we reach here, the enclave type is not supported + throw SQL.EnclaveTypeNotSupported(enclaveType); + } + return true; } + private string ConvertAttestationProtocolToString(SqlConnectionAttestationProtocol attestationProtocol) + { + switch (attestationProtocol) + { + case SqlConnectionAttestationProtocol.AAS: + return "AAS"; + + case SqlConnectionAttestationProtocol.HGS: + return "HGS"; + + default: + return "NotSpecified"; + } + } + private bool TryReadByteString(TdsParserStateObject stateObj, out string value) { value = string.Empty; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs index cc57d64639..3f38515c5e 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs @@ -4334,7 +4334,43 @@ internal static string TCE_EnclaveProviderNotFound { return ResourceManager.GetString("TCE_EnclaveProviderNotFound", resourceCulture); } } - + + /// + /// Looks up a localized string similar to Specifies an attestation protocol for its corresponding enclave attestation service. + /// + internal static string TCE_DbConnectionString_AttestationProtocol { + get { + return ResourceManager.GetString("TCE_DbConnectionString_AttestationProtocol", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The enclave type '{0}' returned from the server is not supported. + /// + internal static string TCE_EnclaveTypeNotSupported { + get { + return ResourceManager.GetString("TCE_EnclaveTypeNotSupported", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to initialize connection. The attestation protocol '{0}' does not support the enclave type '{1}'. + /// + internal static string TCE_AttestationProtocolNotSupportEnclaveType { + get { + return ResourceManager.GetString("TCE_AttestationProtocolNotSupportEnclaveType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Error occured when generating enclave package. Attestation Protocol has not been specified in the connection string, but the query requires enclave computations. + /// + internal static string TCE_AttestationProtocolNotSpecifiedForGeneratingEnclavePackage { + get { + return ResourceManager.GetString("TCE_AttestationProtocolNotSpecifiedForGeneratingEnclavePackage", resourceCulture); + } + } + /// /// Looks up a localized string similar to Executing a query requires enclave computations, but the application configuration is missing the enclave provider section.. /// diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx index ea5834018a..cd79f44b12 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx @@ -1654,7 +1654,7 @@ You have specified the enclave attestation URL in the connection string, but the SQL Server instance in use does not support enclave based computations. - No enclave provider found for enclave type '{0}'. Please specify the provider in the application configuration. + No enclave provider found for enclave type '{0}' and attestation protocol '{1}'. Please specify the correct attestation protocol in the connection string. Executing a query requires enclave computations, but the application configuration is missing the enclave provider section. @@ -1836,4 +1836,16 @@ Failed to build a chain of trust between the enclave host's health report and the HGS root certificate for attestation URL '{0}' with status: '{1}'. Verify the attestation URL matches the URL configured on the SQL Server machine. If both the client and SQL Server use the same attestation service, contact Customer Support Services. + + Specifies an attestation protocol for its corresponding enclave attestation service. + + + The enclave type '{0}' returned from the server is not supported. + + + Failed to initialize connection. The attestation protocol '{0}' does not support the enclave type '{1}'. + + + Error occured when generating enclave package. Attestation Protocol has not been specified in the connection string, but the query requires enclave computations. + \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs index 9bd1949c26..47f1dbeaf1 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs @@ -44,7 +44,6 @@ namespace Microsoft.Data.SqlClient { - // Implementation of an Enclave provider (both for Sgx and Vsm) with Azure Attestation internal class AzureAttestationEnclaveProvider : EnclaveProviderBase { diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs index 7bdf736858..b0d7d94442 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs @@ -1737,7 +1737,7 @@ static internal Exception EnclaveTypeNotReturned() static internal Exception EnclaveTypeNotSupported(string enclaveType) { - return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_SequentialAccessNotSupportedOnEncryptedColumn, enclaveType)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_EnclaveTypeNotSupported, enclaveType)); } static internal Exception AttestationProtocolNotSupportEnclaveType(string attestationProtocolStr, string enclaveType) diff --git a/tools/specs/Microsoft.Data.SqlClient.nuspec b/tools/specs/Microsoft.Data.SqlClient.nuspec index decbb9f59f..09ed140a16 100644 --- a/tools/specs/Microsoft.Data.SqlClient.nuspec +++ b/tools/specs/Microsoft.Data.SqlClient.nuspec @@ -41,6 +41,8 @@ When using NuGet 3.x this package requires at least version 3.4. + + From 0e7d693c489b7b5389f12b18c48bc02d30746d60 Mon Sep 17 00:00:00 2001 From: Javad Rahnama Date: Wed, 16 Oct 2019 17:20:58 -0700 Subject: [PATCH 25/63] testing sqlconnection --- .../ConnectionStringBuilderShould.cs | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs index 914f092b5a..88de3095f6 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs @@ -193,6 +193,22 @@ public void TestSqlConnectionStringBuilderTryGetValue(SqlConnectionColumnEncrypt tryGetValueResult = connectionStringBuilder.TryGetValue(@"Enclave Attestation Url", out outputValue); Assert.True(tryGetValueResult); Assert.Equal("www.foo.com", (string)outputValue); + + // connectionStringBuilder should not have the key ColumnEncryptionSetting. The key is with spaces. + tryGetValueResult = connectionStringBuilder.TryGetValue(@"AttestationProtocol", out outputValue); + Assert.False(tryGetValueResult); + Assert.Null(outputValue); + + // Get the value for the key without setting it. + tryGetValueResult = connectionStringBuilder.TryGetValue(@"Attestation Protocol", out outputValue); + Assert.True(tryGetValueResult); + //Assert.Equal(string.Empty, (string)outputValue); + + // set the value for the protocol without setting it. + connectionStringBuilder.AttestationProtocol = SqlConnectionAttestationProtocol.HGS; + tryGetValueResult = connectionStringBuilder.TryGetValue(@"Attestation Protocol", out outputValue); + Assert.True(tryGetValueResult); + // Assert.Equal("HGS", (string)outputValue); } [Theory] @@ -207,12 +223,22 @@ public void TestSqlConnectionStringBuilderAdd(SqlConnectionColumnEncryptionSetti // Query the property to check if the above add was effective. Assert.Equal(sqlConnectionColumnEncryptionSetting, connectionStringBuilder.ColumnEncryptionSetting); - // Use the Add function to update the Column Encryption Setting in the dictionary. + + //define value for Attestation Url and Attestation Protocol string url = "www.foo.com"; + SqlConnectionAttestationProtocol protocol = SqlConnectionAttestationProtocol.HGS; + + // Use the Add function to update the Column Encryption Setting in the dictionary. connectionStringBuilder.Add(@"Enclave Attestation Url", url); // Query the property to check if the above add was effective. Assert.Equal(url, connectionStringBuilder.EnclaveAttestationUrl); + + // Use the Add function to update the Column Encryption Setting in the dictionary. + connectionStringBuilder.Add(@"Attestation Protocol", protocol); + + // Query the property to check if the above add was effective. + Assert.Equal(protocol, connectionStringBuilder.AttestationProtocol); } [Theory] From fcc62be717a5a297a96e4815965ff7126a2201bb Mon Sep 17 00:00:00 2001 From: Javad Rahnama Date: Wed, 16 Oct 2019 17:27:02 -0700 Subject: [PATCH 26/63] continue testing connectionstring --- .../AlwaysEncryptedTests/ConnectionStringBuilderShould.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs index 88de3095f6..9fd4df2e9d 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs @@ -202,13 +202,13 @@ public void TestSqlConnectionStringBuilderTryGetValue(SqlConnectionColumnEncrypt // Get the value for the key without setting it. tryGetValueResult = connectionStringBuilder.TryGetValue(@"Attestation Protocol", out outputValue); Assert.True(tryGetValueResult); - //Assert.Equal(string.Empty, (string)outputValue); + Assert.Equal(string.Empty, outputValue.ToString()); // set the value for the protocol without setting it. connectionStringBuilder.AttestationProtocol = SqlConnectionAttestationProtocol.HGS; tryGetValueResult = connectionStringBuilder.TryGetValue(@"Attestation Protocol", out outputValue); Assert.True(tryGetValueResult); - // Assert.Equal("HGS", (string)outputValue); + Assert.Equal("HGS",outputValue.ToString()); } [Theory] From 4da88e68c96db01ec78e9ce69418d7f06303f7d9 Mon Sep 17 00:00:00 2001 From: Karina Zhou Date: Thu, 17 Oct 2019 13:48:01 -0700 Subject: [PATCH 27/63] Port fix for AAS claim comparison --- .../AzureAttestationBasedEnclaveProvider.NetCoreApp.cs | 2 +- .../Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.NetCoreApp.cs index e2df559455..affbab8080 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.NetCoreApp.cs @@ -508,7 +508,7 @@ private void ValidateClaim(Dictionary claims, string claimName, throw new AlwaysEncryptedAttestationException(SR.InvalidArgumentToBase64UrlDecoder); } - bool hasValidClaim = String.Equals(encodedActualData, claimData, StringComparison.InvariantCultureIgnoreCase); + bool hasValidClaim = String.Equals(encodedActualData, claimData, StringComparison.Ordinal); if (!hasValidClaim) { throw new AlwaysEncryptedAttestationException(String.Format(SR.InvalidClaimInAttestationToken, claimName, claimData)); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs index 47f1dbeaf1..53753beacb 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs @@ -497,7 +497,7 @@ private void ValidateClaim(Dictionary claims, string claimName, throw new AlwaysEncryptedAttestationException(Strings.InvalidArgumentToBase64UrlDecoder); } - bool hasValidClaim = String.Equals(encodedActualData, claimData, StringComparison.InvariantCultureIgnoreCase); + bool hasValidClaim = String.Equals(encodedActualData, claimData, StringComparison.Ordinal); if (!hasValidClaim) { throw new AlwaysEncryptedAttestationException(String.Format(Strings.InvalidClaimInAttestationToken, claimName, claimData)); From 3a10fdc761508b1c2d1fef5c21f024257238121a Mon Sep 17 00:00:00 2001 From: Karina Zhou Date: Thu, 17 Oct 2019 14:59:47 -0700 Subject: [PATCH 28/63] Add Enclave setup check --- .../ManualTests/DataCommon/DataTestUtility.cs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs index 88eae0d979..125c0a05f8 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs @@ -18,6 +18,7 @@ public static class DataTestUtility public static readonly string NpConnStr = null; public static readonly string TcpConnStr = null; public static readonly string AADPasswordConnStr = null; + public static readonly string TcpEnclaveConnStr = null; public const string UdtTestDbName = "UdtTestDb"; private static readonly Assembly s_systemDotData = typeof(Microsoft.Data.SqlClient.SqlConnection).GetTypeInfo().Assembly; private static readonly Type s_tdsParserStateObjectFactory = s_systemDotData?.GetType("Microsoft.Data.SqlClient.TdsParserStateObjectFactory"); @@ -35,6 +36,7 @@ static DataTestUtility() NpConnStr = Environment.GetEnvironmentVariable("TEST_NP_CONN_STR"); TcpConnStr = Environment.GetEnvironmentVariable("TEST_TCP_CONN_STR"); AADPasswordConnStr = Environment.GetEnvironmentVariable("AAD_PASSWORD_CONN_STR"); + TcpEnclaveConnStr = Environment.GetEnvironmentVariable("TEST_TCP_ENCLAVE_CONN_STR"); } public static bool IsDatabasePresent(string name) @@ -100,6 +102,33 @@ public static bool IsUTF8Supported() return retval; } + public static bool IsEnclaveEnabled() + { + if (AreConnStringsSetup()) + { + using (SqlConnection connection = new SqlConnection(DataTestUtility.TcpConnStr)) + using (SqlCommand command = new SqlCommand()) + { + command.Connection = connection; + command.CommandText = @"SELECT [value_in_use] FROM sys.configurations WHERE [name] = 'column encryption enclave type';"; + connection.Open(); + + using (SqlDataReader reader = command.ExecuteReader()) + { + while (reader.Read()) + { + if (reader.GetInt32(0) > 0) + { + return true; + } + } + } + } + } + + return false; + } + // the name length will be no more then (16 + prefix.Length + escapeLeft.Length + escapeRight.Length) // some providers does not support names (Oracle supports up to 30) From 11c896f7079b597d922295202f5020dcb24c323e Mon Sep 17 00:00:00 2001 From: v-jizho2 Date: Thu, 17 Oct 2019 15:06:49 -0700 Subject: [PATCH 29/63] Add enclave setup check (#272) * Port fix for AAS claim comparison * Add Enclave setup check --- ...estationBasedEnclaveProvider.NetCoreApp.cs | 2 +- .../AzureAttestationBasedEnclaveProvider.cs | 2 +- .../ManualTests/DataCommon/DataTestUtility.cs | 29 +++++++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.NetCoreApp.cs index e2df559455..affbab8080 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.NetCoreApp.cs @@ -508,7 +508,7 @@ private void ValidateClaim(Dictionary claims, string claimName, throw new AlwaysEncryptedAttestationException(SR.InvalidArgumentToBase64UrlDecoder); } - bool hasValidClaim = String.Equals(encodedActualData, claimData, StringComparison.InvariantCultureIgnoreCase); + bool hasValidClaim = String.Equals(encodedActualData, claimData, StringComparison.Ordinal); if (!hasValidClaim) { throw new AlwaysEncryptedAttestationException(String.Format(SR.InvalidClaimInAttestationToken, claimName, claimData)); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs index 47f1dbeaf1..53753beacb 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs @@ -497,7 +497,7 @@ private void ValidateClaim(Dictionary claims, string claimName, throw new AlwaysEncryptedAttestationException(Strings.InvalidArgumentToBase64UrlDecoder); } - bool hasValidClaim = String.Equals(encodedActualData, claimData, StringComparison.InvariantCultureIgnoreCase); + bool hasValidClaim = String.Equals(encodedActualData, claimData, StringComparison.Ordinal); if (!hasValidClaim) { throw new AlwaysEncryptedAttestationException(String.Format(Strings.InvalidClaimInAttestationToken, claimName, claimData)); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs index 88eae0d979..125c0a05f8 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs @@ -18,6 +18,7 @@ public static class DataTestUtility public static readonly string NpConnStr = null; public static readonly string TcpConnStr = null; public static readonly string AADPasswordConnStr = null; + public static readonly string TcpEnclaveConnStr = null; public const string UdtTestDbName = "UdtTestDb"; private static readonly Assembly s_systemDotData = typeof(Microsoft.Data.SqlClient.SqlConnection).GetTypeInfo().Assembly; private static readonly Type s_tdsParserStateObjectFactory = s_systemDotData?.GetType("Microsoft.Data.SqlClient.TdsParserStateObjectFactory"); @@ -35,6 +36,7 @@ static DataTestUtility() NpConnStr = Environment.GetEnvironmentVariable("TEST_NP_CONN_STR"); TcpConnStr = Environment.GetEnvironmentVariable("TEST_TCP_CONN_STR"); AADPasswordConnStr = Environment.GetEnvironmentVariable("AAD_PASSWORD_CONN_STR"); + TcpEnclaveConnStr = Environment.GetEnvironmentVariable("TEST_TCP_ENCLAVE_CONN_STR"); } public static bool IsDatabasePresent(string name) @@ -100,6 +102,33 @@ public static bool IsUTF8Supported() return retval; } + public static bool IsEnclaveEnabled() + { + if (AreConnStringsSetup()) + { + using (SqlConnection connection = new SqlConnection(DataTestUtility.TcpConnStr)) + using (SqlCommand command = new SqlCommand()) + { + command.Connection = connection; + command.CommandText = @"SELECT [value_in_use] FROM sys.configurations WHERE [name] = 'column encryption enclave type';"; + connection.Open(); + + using (SqlDataReader reader = command.ExecuteReader()) + { + while (reader.Read()) + { + if (reader.GetInt32(0) > 0) + { + return true; + } + } + } + } + } + + return false; + } + // the name length will be no more then (16 + prefix.Length + escapeLeft.Length + escapeRight.Length) // some providers does not support names (Oracle supports up to 30) From 6129bf74abcccb91c60d8286830776ce48f97b57 Mon Sep 17 00:00:00 2001 From: Javad Date: Fri, 18 Oct 2019 09:32:33 -0700 Subject: [PATCH 30/63] Test SqlConnectionStringBuilder Tests updated for Attestation Protocol. Remove an extra empty line in DataTestUtility. --- .../ConnectionStringBuilderShould.cs | 114 ++++++++++++++++-- .../ManualTests/DataCommon/DataTestUtility.cs | 1 - 2 files changed, 107 insertions(+), 8 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs index 9fd4df2e9d..ee9910ac05 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs @@ -24,6 +24,7 @@ public void TestSqlConnectionStringBuilder() // Create a connection object with the above builder and verify the expected value. VerifyColumnEncryptionSetting(connectionStringBuilder, false); + VerifyAttestationProtocol(connectionStringBuilder, SqlConnectionAttestationProtocol.NotSpecified); } [Fact] @@ -50,25 +51,87 @@ public void TestSqlConnectionStringBuilderEnclaveAttestationUrl() Assert.True(string.IsNullOrEmpty(connectionStringBuilder2.DataSource)); } + [Fact] + public void TestSqlConnectionStringAttestationProtocol() + { + SqlConnectionStringBuilder connectionStringBuilder = new SqlConnectionStringBuilder(); + Assert.Equal(SqlConnectionAttestationProtocol.NotSpecified, connectionStringBuilder.AttestationProtocol); + connectionStringBuilder.DataSource = @"localhost"; + + // Create a connection object with the above builder and verify the expected value. + VerifyAttestationProtocol(connectionStringBuilder, SqlConnectionAttestationProtocol.NotSpecified); + + SqlConnectionStringBuilder connectionStringBuilder2 = new SqlConnectionStringBuilder(); + connectionStringBuilder.AttestationProtocol = SqlConnectionAttestationProtocol.AAS; + Assert.Equal(SqlConnectionAttestationProtocol.AAS, connectionStringBuilder2.AttestationProtocol); + connectionStringBuilder2.DataSource = @"localhost"; + + // Create a connection object with the above builder and verify the expected value. + VerifyAttestationProtocol(connectionStringBuilder2, SqlConnectionAttestationProtocol.AAS); + + connectionStringBuilder2.Clear(); + + Assert.Equal(SqlConnectionAttestationProtocol.NotSpecified, connectionStringBuilder2.AttestationProtocol); + Assert.True(string.IsNullOrEmpty(connectionStringBuilder2.DataSource)); + + SqlConnectionStringBuilder connectionStringBuilder3 = new SqlConnectionStringBuilder(); + connectionStringBuilder.AttestationProtocol = SqlConnectionAttestationProtocol.HGS; + Assert.Equal(SqlConnectionAttestationProtocol.HGS, connectionStringBuilder3.AttestationProtocol); + connectionStringBuilder3.DataSource = @"localhost"; + + // Create a connection object with the above builder and verify the expected value. + VerifyAttestationProtocol(connectionStringBuilder3, SqlConnectionAttestationProtocol.HGS); + + connectionStringBuilder3.Clear(); + + Assert.Equal(SqlConnectionAttestationProtocol.NotSpecified, connectionStringBuilder3.AttestationProtocol); + Assert.True(string.IsNullOrEmpty(connectionStringBuilder3.DataSource)); + } + [Fact] public void TestSqlConnectionStringBuilderEquivalentTo_EnclaveAttestationUrl() { string enclaveAttUrl1 = "www.foo.com"; string enclaveAttUrl2 = "www.foo1.com"; + SqlConnectionStringBuilder connectionStringBuilder1 = new SqlConnectionStringBuilder(); SqlConnectionStringBuilder connectionStringBuilder2 = new SqlConnectionStringBuilder(); // Modify the default value and set the same value on the both the builder objects above. connectionStringBuilder1.EnclaveAttestationUrl = enclaveAttUrl1; + connectionStringBuilder2.EnclaveAttestationUrl = enclaveAttUrl1; // Use the EquivalentTo function to compare both the builder objects and make sure the result is expected. Assert.True(connectionStringBuilder1.EquivalentTo(connectionStringBuilder2)); connectionStringBuilder2.EnclaveAttestationUrl = enclaveAttUrl2; + + Assert.True(!connectionStringBuilder1.EquivalentTo(connectionStringBuilder2)); + } + + [Fact] + public void TestSqlConnectionStringBuilderEquivilantTo_AttestationProtocol() + { + SqlConnectionAttestationProtocol protocol1 = SqlConnectionAttestationProtocol.AAS; + SqlConnectionAttestationProtocol protocol2 = SqlConnectionAttestationProtocol.HGS; + + SqlConnectionStringBuilder connectionStringBuilder1 = new SqlConnectionStringBuilder(); + SqlConnectionStringBuilder connectionStringBuilder2 = new SqlConnectionStringBuilder(); + + // Modify the default value and set the same value on the both the builder objects above. + connectionStringBuilder1.AttestationProtocol = protocol1; + connectionStringBuilder2.AttestationProtocol = protocol1; + + // Use the EquivalentTo function to compare both the builder objects and make sure the result is expected. + Assert.True(connectionStringBuilder1.EquivalentTo(connectionStringBuilder2)); + + connectionStringBuilder2.AttestationProtocol = protocol2; + Assert.True(!connectionStringBuilder1.EquivalentTo(connectionStringBuilder2)); } + [Theory] [MemberData(nameof(SqlConnectionColumnEncryptionSettings))] public void TestSqlConnectionStringBuilderColumnEncryptionSetting(SqlConnectionColumnEncryptionSetting sqlConnectionColumnEncryptionSetting) @@ -82,6 +145,23 @@ public void TestSqlConnectionStringBuilderColumnEncryptionSetting(SqlConnectionC VerifyColumnEncryptionSetting(connectionStringBuilder, sqlConnectionColumnEncryptionSetting == SqlConnectionColumnEncryptionSetting.Enabled); } + [Theory] + [InlineData(SqlConnectionAttestationProtocol.AAS)] + [InlineData(SqlConnectionAttestationProtocol.HGS)] + public void TestSqlConnectionStringBuilderAttestationProtocol(SqlConnectionAttestationProtocol protocol) + { + SqlConnectionStringBuilder connectionStringBuilder = new SqlConnectionStringBuilder(); + + // Modify value. + if (protocol != SqlConnectionAttestationProtocol.NotSpecified) + { + connectionStringBuilder.AttestationProtocol = protocol; + } + + //Create a connection object with the above builder and verify the expected value. + VerifyAttestationProtocol(connectionStringBuilder, protocol); + } + [Theory] [MemberData(nameof(SqlConnectionColumnEncryptionSettings))] public void TestSqlConnectionStringBuilderClear(SqlConnectionColumnEncryptionSetting sqlConnectionColumnEncryptionSetting) @@ -90,11 +170,14 @@ public void TestSqlConnectionStringBuilderClear(SqlConnectionColumnEncryptionSet // Modify the default value. connectionStringBuilder.ColumnEncryptionSetting = sqlConnectionColumnEncryptionSetting; + connectionStringBuilder.AttestationProtocol = SqlConnectionAttestationProtocol.AAS; connectionStringBuilder.DataSource = @"localhost"; connectionStringBuilder.Clear(); Assert.Equal(SqlConnectionColumnEncryptionSetting.Disabled, connectionStringBuilder.ColumnEncryptionSetting); + + Assert.True(connectionStringBuilder.AttestationProtocol == SqlConnectionAttestationProtocol.NotSpecified); Assert.True(string.IsNullOrEmpty(connectionStringBuilder.DataSource)); } @@ -153,6 +236,11 @@ public void TestSqlConnectionStringBuilderContainsKey(SqlConnectionColumnEncrypt // set a value and check for the key again, it should exist. connectionStringBuilder.EnclaveAttestationUrl = "www.foo.com"; Assert.True(connectionStringBuilder.ContainsKey(@"Enclave Attestation Url")); + + //Aslo check attestation protocol + + // Key is "Attestation Protocol" with spaces. So lookup for AttestationProtocol should return false. + Assert.False(connectionStringBuilder.ContainsKey(@"AttestationProtocol")); } [Theory] @@ -202,13 +290,13 @@ public void TestSqlConnectionStringBuilderTryGetValue(SqlConnectionColumnEncrypt // Get the value for the key without setting it. tryGetValueResult = connectionStringBuilder.TryGetValue(@"Attestation Protocol", out outputValue); Assert.True(tryGetValueResult); - Assert.Equal(string.Empty, outputValue.ToString()); + Assert.Equal(SqlConnectionAttestationProtocol.NotSpecified, outputValue); // set the value for the protocol without setting it. connectionStringBuilder.AttestationProtocol = SqlConnectionAttestationProtocol.HGS; tryGetValueResult = connectionStringBuilder.TryGetValue(@"Attestation Protocol", out outputValue); Assert.True(tryGetValueResult); - Assert.Equal("HGS",outputValue.ToString()); + Assert.Equal(SqlConnectionAttestationProtocol.HGS, outputValue); } [Theory] @@ -223,7 +311,6 @@ public void TestSqlConnectionStringBuilderAdd(SqlConnectionColumnEncryptionSetti // Query the property to check if the above add was effective. Assert.Equal(sqlConnectionColumnEncryptionSetting, connectionStringBuilder.ColumnEncryptionSetting); - //define value for Attestation Url and Attestation Protocol string url = "www.foo.com"; SqlConnectionAttestationProtocol protocol = SqlConnectionAttestationProtocol.HGS; @@ -271,9 +358,22 @@ public void TestSqlConnectionStringBuilderRemove(SqlConnectionColumnEncryptionSe // Use the Remove function to remove the Column Encryption Setting from the dictionary. connectionStringBuilder.Remove(@"Enclave Attestation Url"); - // Query the property to check if the above add was effective. + // Query the property to check if the above remove was effective. connectionStringBuilder.TryGetValue(@"Enclave Attestation Url", out outputValue); Assert.Equal(string.Empty, outputValue); + + // Use Add function to update the Attestation Protocol in the dictionary. + SqlConnectionAttestationProtocol protocol = SqlConnectionAttestationProtocol.AAS; + connectionStringBuilder.Add(@"Attestation Protocol", protocol); + + // Query the property ti check if the above Add was effective. + Assert.Equal(protocol, connectionStringBuilder.AttestationProtocol); + + // Use Remove function to remove the Attestation Protocol. + connectionStringBuilder.Remove(@"Attestation Protocol"); + + // Query the property to check if above Remove was effective. + Assert.Equal(SqlConnectionAttestationProtocol.NotSpecified, connectionStringBuilder.AttestationProtocol); } [Theory] @@ -344,13 +444,13 @@ private void VerifyEnclaveAttestationUrlSetting(SqlConnectionStringBuilder conne /// /// /// - private void VerifyAttestationProtocol(SqlConnectionStringBuilder connectionStringBuilder, string expectedAttestationProtocol) + private void VerifyAttestationProtocol(SqlConnectionStringBuilder connectionStringBuilder, SqlConnectionAttestationProtocol expectedAttestationProtocol) { string connectionString = connectionStringBuilder.ToString(); using (SqlConnection sqlConnection = new SqlConnection(connectionString)) { - string currentAttestationProtocol = (string)typeof(SqlConnection) - .GetProperty(@"Attestation Protocol", BindingFlags.Instance | BindingFlags.NonPublic) + SqlConnectionAttestationProtocol currentAttestationProtocol = (SqlConnectionAttestationProtocol)typeof(SqlConnection) + .GetProperty(@"AttestationProtocol", BindingFlags.Instance | BindingFlags.NonPublic) .GetValue(sqlConnection); Assert.Equal(expectedAttestationProtocol, currentAttestationProtocol); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs index 125c0a05f8..be420cbb76 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs @@ -129,7 +129,6 @@ public static bool IsEnclaveEnabled() return false; } - // the name length will be no more then (16 + prefix.Length + escapeLeft.Length + escapeRight.Length) // some providers does not support names (Oracle supports up to 30) public static string GetUniqueName(string prefix) From 43338400ee544bc8dcaa5e853242d3e98d96379a Mon Sep 17 00:00:00 2001 From: Javad Date: Fri, 18 Oct 2019 16:45:28 -0700 Subject: [PATCH 31/63] adding more test for AE v2 --- .../AlwaysEncryptedTests/ConnectionStringBuilderShould.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs index ee9910ac05..9be71de582 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs @@ -62,7 +62,7 @@ public void TestSqlConnectionStringAttestationProtocol() VerifyAttestationProtocol(connectionStringBuilder, SqlConnectionAttestationProtocol.NotSpecified); SqlConnectionStringBuilder connectionStringBuilder2 = new SqlConnectionStringBuilder(); - connectionStringBuilder.AttestationProtocol = SqlConnectionAttestationProtocol.AAS; + connectionStringBuilder2.AttestationProtocol = SqlConnectionAttestationProtocol.AAS; Assert.Equal(SqlConnectionAttestationProtocol.AAS, connectionStringBuilder2.AttestationProtocol); connectionStringBuilder2.DataSource = @"localhost"; @@ -75,7 +75,7 @@ public void TestSqlConnectionStringAttestationProtocol() Assert.True(string.IsNullOrEmpty(connectionStringBuilder2.DataSource)); SqlConnectionStringBuilder connectionStringBuilder3 = new SqlConnectionStringBuilder(); - connectionStringBuilder.AttestationProtocol = SqlConnectionAttestationProtocol.HGS; + connectionStringBuilder3.AttestationProtocol = SqlConnectionAttestationProtocol.HGS; Assert.Equal(SqlConnectionAttestationProtocol.HGS, connectionStringBuilder3.AttestationProtocol); connectionStringBuilder3.DataSource = @"localhost"; @@ -84,7 +84,7 @@ public void TestSqlConnectionStringAttestationProtocol() connectionStringBuilder3.Clear(); - Assert.Equal(SqlConnectionAttestationProtocol.NotSpecified, connectionStringBuilder3.AttestationProtocol); + //Assert.Equal(SqlConnectionAttestationProtocol.NotSpecified, connectionStringBuilder3.AttestationProtocol); Assert.True(string.IsNullOrEmpty(connectionStringBuilder3.DataSource)); } From 987afab23d914826225dade6a8df8a3e8f7aed63 Mon Sep 17 00:00:00 2001 From: Javad Rahnama Date: Wed, 23 Oct 2019 15:45:16 -0700 Subject: [PATCH 32/63] ConnectionString for AEV2 --- .../netfx/src/Microsoft.Data.SqlClient.csproj | 37 +++++++---- .../netfx/src/Resources/Strings.Designer.cs | 64 +++++++++---------- .../netfx/src/Resources/Strings.resx | 1 - .../ManualTests/AlwaysEncrypted/ApiShould.cs | 51 ++++++++------- .../TestFixtures/SQLSetupStrategy.cs | 48 +++++++++----- .../TestFixtures/SQLSetupStrategyCspExt.cs | 10 ++- .../ManualTests/DataCommon/DataTestUtility.cs | 38 +++-------- .../tests/ManualTests/config.json | 1 + 8 files changed, 132 insertions(+), 118 deletions(-) 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 224e5bd306..976bac41c5 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -158,10 +158,16 @@ - - + + Component + + + Component + - + + Component + @@ -170,7 +176,9 @@ - + + Component + @@ -252,7 +260,9 @@ - + + Component + @@ -311,16 +321,17 @@ - + True True - $(ResxFileName).resx + Strings.resx - + System ResXFileCodeGenerator - $(ResxFileName).Designer.cs + Strings.Designer.cs + Designer @@ -339,10 +350,12 @@ True - + + + - 1.1.0 + 1.0.19235.1 3.0.8 @@ -365,4 +378,4 @@ - + \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs index 64a216d4eb..42908c4ba7 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs @@ -7777,7 +7777,7 @@ internal static string OleDb_PropertyBadOption { /// Looks up a localized string similar to Failed to initialize the '{0}' property for one of the following reasons: /// The value data type was not the data type of the property or was not null. For example, the property was DBPROP_MEMORYUSAGE, which has a data type of Int32, and the data type was Int64. /// The value was not a valid value. For example, the property was DBPROP_MEMORYUSAGE and the value was negative. - /// The value was a valid value for the property and the provider supports the property as a settable property, but the provider does not su [rest of string was truncated]";. + /// The value was a valid value for the property and the provider supports the property as a settable property, but the provider does not suppo [rest of string was truncated]";. /// internal static string OleDb_PropertyBadValue { get { @@ -11664,6 +11664,24 @@ internal static string TCE_AttestationInfoNotReturnedFromSQLServer { } } + /// + /// Looks up a localized string similar to Error occured when generating enclave package. Attestation Protocol has not been specified in the connection string, but the query requires enclave computations.. + /// + internal static string TCE_AttestationProtocolNotSpecifiedForGeneratingEnclavePackage { + get { + return ResourceManager.GetString("TCE_AttestationProtocolNotSpecifiedForGeneratingEnclavePackage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to initialize connection. The attestation protocol '{0}' does not support the enclave type '{1}'.. + /// + internal static string TCE_AttestationProtocolNotSupportEnclaveType { + get { + return ResourceManager.GetString("TCE_AttestationProtocolNotSupportEnclaveType", resourceCulture); + } + } + /// /// Looks up a localized string similar to {0} should be identical on all commands ({1}, {2}, {3}, {4}) when doing batch updates.. /// @@ -11772,6 +11790,15 @@ internal static string TCE_ColumnMasterKeySignatureVerificationFailed { } } + /// + /// Looks up a localized string similar to Specifies an attestation protocol for its corresponding enclave attestation service.. + /// + internal static string TCE_DbConnectionString_AttestationProtocol { + get { + return ResourceManager.GetString("TCE_DbConnectionString_AttestationProtocol", resourceCulture); + } + } + /// /// Looks up a localized string similar to Default column encryption setting for all the commands on the connection.. /// @@ -11789,15 +11816,6 @@ internal static string TCE_DbConnectionString_EnclaveAttestationUrl { return ResourceManager.GetString("TCE_DbConnectionString_EnclaveAttestationUrl", resourceCulture); } } - - /// - /// Looks up a localized string similar to Specifies an attestation protocol for its corresponding enclave attestation service. - /// - internal static string TCE_DbConnectionString_AttestationProtocol { - get { - return ResourceManager.GetString("TCE_DbConnectionString_AttestationProtocol", resourceCulture); - } - } /// /// Looks up a localized string similar to Decryption failed. The last 10 bytes of the encrypted column encryption key are: '{0}'. The first 10 bytes of ciphertext are: '{1}'.. @@ -11953,22 +11971,13 @@ internal static string TCE_EnclaveComputationsNotSupported { } /// - /// Looks up a localized string similar to No enclave provider found for enclave type '{0}'. Please specify the provider in the application configuration. . + /// Looks up a localized string similar to No enclave provider found for enclave type '{0}' and attestation protocol '{1}'. Please specify the correct attestation protocol in the connection string. . /// internal static string TCE_EnclaveProviderNotFound { get { return ResourceManager.GetString("TCE_EnclaveProviderNotFound", resourceCulture); } } - - /// - /// Looks up a localized string similar to Error occured when generating enclave package. Attestation Protocol has not been specified in the connection string, but the query requires enclave computations. - /// - internal static string TCE_AttestationProtocolNotSpecifiedForGeneratingEnclavePackage { - get { - return ResourceManager.GetString("TCE_AttestationProtocolNotSpecifiedForGeneratingEnclavePackage", resourceCulture); - } - } /// /// Looks up a localized string similar to Executing a query requires enclave computations, but the application configuration is missing the enclave provider section.. @@ -11987,25 +11996,16 @@ internal static string TCE_EnclaveTypeNotReturned { return ResourceManager.GetString("TCE_EnclaveTypeNotReturned", resourceCulture); } } - + /// - /// Looks up a localized string similar to The enclave type '{0}' returned from the server is not supported. + /// Looks up a localized string similar to The enclave type '{0}' returned from the server is not supported.. /// internal static string TCE_EnclaveTypeNotSupported { get { return ResourceManager.GetString("TCE_EnclaveTypeNotSupported", resourceCulture); } } - - /// - /// Looks up a localized string similar to Failed to initialize connection. The attestation protocol does not support the enclave type '{0}'. - /// - internal static string TCE_AttestationProtocolNotSupportEnclaveType { - get { - return ResourceManager.GetString("TCE_AttestationProtocolNotSupportEnclaveType", resourceCulture); - } - } - + /// /// Looks up a localized string similar to Internal Error. Enclave type received from SQL Server is null or empty when executing a query requiring enclave computations.. /// diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx index 29a611f125..4d75d55da3 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx @@ -4519,4 +4519,3 @@ Error occured when generating enclave package. Attestation Protocol has not been specified in the connection string, but the query requires enclave computations. - diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs index 7276b88dd8..3faa0e349e 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs @@ -32,11 +32,14 @@ public ApiShould(SQLSetupStrategyCertStoreProvider fixture) } [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [InlineData(true)] - [InlineData(false)] - public void TestSqlTransactionCommitRollbackWithTransparentInsert(bool isCommitted) + [InlineData(false, "Data Source = WIN-K4TQB0TR8JQ; Initial Catalog = Northwind; Column Encryption Setting = Enabled; Attestation Protocol = HGS; Enclave Attestation Url= http://WIN-K4TQB0TR8JQ/Attestation;User ID=sa; Password=Moonshine4me; Integrated Security= False;")] + [InlineData(true, "Data Source = WIN-K4TQB0TR8JQ; Initial Catalog = Northwind; Column Encryption Setting = Enabled; Attestation Protocol = HGS; Enclave Attestation Url= http://WIN-K4TQB0TR8JQ/Attestation;User ID=sa; Password=Moonshine4me; Integrated Security= False;")] + [InlineData(true, "Data Source=tcp:localhost;Database=Northwind;Integrated Security=true")] + + public void TestSqlTransactionCommitRollbackWithTransparentInsert(bool isCommitted, string value) { - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionString)) + + using (SqlConnection sqlConnection = new SqlConnection(value)) { sqlConnection.Open(); @@ -72,7 +75,7 @@ public void TestSqlTransactionCommitRollbackWithTransparentInsert(bool isCommitt [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] public void TestSqlTransactionRollbackToSavePoint() { - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionString)) + using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport)) { sqlConnection.Open(); @@ -126,7 +129,7 @@ public void SqlParameterProperties() const int decimalColumnScale = 4; const int timeColumnScale = 5; - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionString)) + using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport)) { try { @@ -392,7 +395,7 @@ public void TestSqlDataAdapterFillDataTable() InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values); - var encryptionEnabledConnectionString = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) + var encryptionEnabledConnectionString = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport) { ColumnEncryptionSetting = SqlConnectionColumnEncryptionSetting.Enabled }.ConnectionString; @@ -457,7 +460,7 @@ public void TestSqlDataAdapterFillSchema(SchemaType schemaType) // Insert a bunch of rows in to the table. int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values); - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionString)) + using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport)) { sqlConnection.Open(); SqlDataAdapter adapter = CreateSqlDataAdapter(sqlConnection); @@ -507,7 +510,7 @@ public void TestExecuteNonQuery(bool isAsync) Assert.Equal(numberOfRows, rowsAffected); rowsAffected = -1; - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionString)) + using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport)) { sqlConnection.Open(); @@ -550,7 +553,7 @@ public void TestExecuteScalar(bool isAsync) // Insert a bunch of rows in to the table. int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values); - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionString)) + using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport)) { sqlConnection.Open(); @@ -589,7 +592,7 @@ public void TestSqlDataAdapterBatchUpdate(int numberofRows) DataTable dataTable = CreateDataTable(tableName: tableName, numberofRows: numberofRows); - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionString)) + using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport)) { sqlConnection.Open(); @@ -637,7 +640,7 @@ public void TestExecuteReader() int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values); Assert.True(numberOfRows == rowsAffected, "Two values failed"); - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionString)) + using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport)) { sqlConnection.Open(); @@ -695,7 +698,7 @@ public void TestExecuteReaderWithCommandBehavior(CommandBehavior commandBehavior int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values); Assert.Equal(rowsAffected, numberOfRows); - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionString)) + using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport)) { sqlConnection.Open(); @@ -811,7 +814,7 @@ public void TestPrepareWithExecuteNonQuery() Assert.True(rowsAffected == numberOfRows, "number of rows affected is unexpected."); - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionString)) + using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport)) { sqlConnection.Open(); using (SqlCommand sqlCommand = @@ -853,7 +856,7 @@ public void TestAsyncWriteDelayWithExecuteNonQueryAsync() // Insert a bunch of rows in to the table. int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values); - using (SqlConnection sqlconnection = new SqlConnection(DataTestUtility.TCPConnectionString)) + using (SqlConnection sqlconnection = new SqlConnection(DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport)) { sqlconnection.Open(); @@ -908,7 +911,7 @@ public void TestAsyncWriteDelayWithExecuteReaderAsync() Assert.True(rowsAffected == numberOfRows, "number of rows affected is unexpected."); - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionString)) + using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport)) { sqlConnection.Open(); @@ -972,7 +975,7 @@ public void TestPrepareWithExecuteNonQueryAsync() Assert.True(rowsAffected == numberOfRows, "number of rows affected is unexpected."); - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionString)) + using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport)) { sqlConnection.Open(); using (SqlCommand sqlCommand = new SqlCommand( @@ -1024,7 +1027,7 @@ public void TestPrepareWithExecuteReaderAsync(CommandBehavior commandBehavior) Assert.True(rowsAffected == numberOfRows, "number of rows affected is unexpected."); - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionString)) + using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport)) { sqlConnection.Open(); @@ -1088,7 +1091,7 @@ public void TestSqlDataReaderAPIs(SqlCommandColumnEncryptionSetting value) Assert.True(rowsAffected == numberOfRows, "number of rows affected is unexpected."); - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionString)) + using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport)) { sqlConnection.Open(); @@ -1283,7 +1286,7 @@ public void TestSqlDataReaderAPIsWithSequentialAccess(object value) Assert.Equal(rowsAffected, numberOfRows); - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionString)) + using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport)) { sqlConnection.Open(); using (SqlCommand sqlCommand = new SqlCommand($"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", @@ -1707,7 +1710,7 @@ public void TestSqlCommandSequentialAccessCodePaths(object value) Assert.True(rowsAffected == numberOfRows, "number of rows affected is unexpected."); - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionString)) + using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport)) { sqlConnection.Open(); // Test SqlDataReader.GetStream() on encrypted column, throw an exception. @@ -1851,7 +1854,7 @@ private int InsertRows(string tableName, int numberofRows, IList values) { int rowsAffected = 0; - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionString)) + using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport)) { sqlConnection.Open(); @@ -1877,7 +1880,7 @@ private int InsertRows(string tableName, int numberofRows, IList values) /// private void DropHelperProcedures(string[] procNames) { - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionString)) + using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport)) { sqlConnection.Open(); foreach (string name in procNames) @@ -2009,7 +2012,7 @@ private void TestDataAdapterFillResults(DataTable dataTable, IList value public void Dispose() { - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionString)) + using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport)) { sqlConnection.Open(); 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 baad3b528f..1f96596f96 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategy.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategy.cs @@ -23,29 +23,40 @@ public class SQLSetupStrategy : IDisposable protected List databaseObjects = new List(); + protected Dictionary ConnectionStrings = new Dictionary(); public SQLSetupStrategy() { certificate = CertificateUtility.CreateCertificate(); + ConnectionStrings = DataTestUtility.connStrings; } protected SQLSetupStrategy(string customKeyPath) => keyPath = customKeyPath; internal virtual void SetupDatabase() { - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionString)) + foreach (var value in ConnectionStrings.Values) { - sqlConnection.Open(); - databaseObjects.ForEach(o => o.Create(sqlConnection)); - } - - // Insert data for TrustedMasterKeyPaths tests. - SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString); - builder.ConnectTimeout = 10000; - Customer customer = new Customer(45, "Microsoft", "Corporation"); - using (SqlConnection sqlConn = new SqlConnection(builder.ToString())) - { - sqlConn.Open(); - DatabaseHelper.InsertCustomerData(sqlConn, TrustedMasterKeyPathsTestTable.Name, customer); + using (SqlConnection sqlConnection = new SqlConnection(value)) + { + sqlConnection.Open(); + databaseObjects.ForEach(o => o.Create(sqlConnection)); + } + + //using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport)) + //{ + // sqlConnection.Open(); + // databaseObjects.ForEach(o => o.Create(sqlConnection)); + //} + + // Insert data for TrustedMasterKeyPaths tests. + SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(value); + builder.ConnectTimeout = 10000; + Customer customer = new Customer(45, "Microsoft", "Corporation"); + using (SqlConnection sqlConn = new SqlConnection(builder.ToString())) + { + sqlConn.Open(); + DatabaseHelper.InsertCustomerData(sqlConn, TrustedMasterKeyPathsTestTable.Name, customer); + } } } @@ -89,11 +100,16 @@ protected List CreateTables(IList columnEncryptionKe public void Dispose() { databaseObjects.Reverse(); - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionString)) + foreach (var value in ConnectionStrings.Values) { - sqlConnection.Open(); - databaseObjects.ForEach(o => o.Drop(sqlConnection)); + using (SqlConnection sqlConnection = new SqlConnection(value)) + { + sqlConnection.Open(); + databaseObjects.ForEach(o => o.Drop(sqlConnection)); + } } + + } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategyCspExt.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategyCspExt.cs index 930e9b226b..2e040ae0a7 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategyCspExt.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategyCspExt.cs @@ -29,11 +29,15 @@ internal override void SetupDatabase() Table table = CreateTable(columnEncryptionKeys); databaseObjects.Add(table); - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionString)) + foreach(var value in DataTestUtility.connStrings.Values) { - sqlConnection.Open(); - databaseObjects.ForEach(o => o.Create(sqlConnection)); + using (SqlConnection sqlConnection = new SqlConnection(value)) + { + sqlConnection.Open(); + databaseObjects.ForEach(o => o.Create(sqlConnection)); + } } + } private Table CreateTable(IList columnEncryptionKeys) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs index 5ec73215fd..6386dc61c3 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs @@ -18,13 +18,14 @@ public static class DataTestUtility { public static readonly string NPConnectionString = null; public static readonly string TCPConnectionString = null; + public static readonly string TCPConnectionStringWithAEV2HGSVBSSupport = null; public static readonly string AADAccessToken = null; public static readonly string AADPasswordConnectionString = null; public static readonly string AKVBaseUrl = null; public static readonly string AKVUrl = null; public static readonly string AKVClientId = null; public static readonly string AKVClientSecret = null; - + public static Dictionary connStrings = new Dictionary(); public static readonly bool SupportsIntegratedSecurity = false; public static readonly bool SupportsLocalDb = false; public static readonly bool SupportsFileStream = false; @@ -47,6 +48,7 @@ private class Config { public string TCPConnectionString = null; public string NPConnectionString = null; + public string TCPConnectionStringWithAEV2HGSVBSSupport = ""; public string AADAccessToken = null; public string AADPasswordConnectionString = null; public string AzureKeyVaultURL = null; @@ -66,12 +68,15 @@ static DataTestUtility() NPConnectionString = c.NPConnectionString; TCPConnectionString = c.TCPConnectionString; + TCPConnectionStringWithAEV2HGSVBSSupport = c.TCPConnectionStringWithAEV2HGSVBSSupport; + connStrings.Add("TCPConnectionString", c.TCPConnectionString); + connStrings.Add("TCPConnectionStringWithAEV2HGSVBSSupport", c.TCPConnectionStringWithAEV2HGSVBSSupport); AADAccessToken = c.AADAccessToken; AADPasswordConnectionString = c.AADPasswordConnectionString; SupportsLocalDb = c.SupportsLocalDb; SupportsIntegratedSecurity = c.SupportsIntegratedSecurity; SupportsFileStream = c.SupportsFileStream; - + string url = c.AzureKeyVaultURL; Uri AKVBaseUri = null; if (!string.IsNullOrEmpty(url) && Uri.TryCreate(url, UriKind.Absolute, out AKVBaseUri)) @@ -80,7 +85,7 @@ static DataTestUtility() AKVBaseUrl = AKVBaseUri.AbsoluteUri; AKVUrl = (new Uri(AKVBaseUri, $"/keys/{AKVKeyName}")).AbsoluteUri; } - + AKVClientId = c.AzureKeyVaultClientId; AKVClientSecret = c.AzureKeyVaultClientSecret; } @@ -154,33 +159,6 @@ public static bool IsUTF8Supported() return retval; } - public static bool IsEnclaveEnabled() - { - if (AreConnStringsSetup()) - { - using (SqlConnection connection = new SqlConnection(DataTestUtility.TcpConnStr)) - using (SqlCommand command = new SqlCommand()) - { - command.Connection = connection; - command.CommandText = @"SELECT [value_in_use] FROM sys.configurations WHERE [name] = 'column encryption enclave type';"; - connection.Open(); - - using (SqlDataReader reader = command.ExecuteReader()) - { - while (reader.Read()) - { - if (reader.GetInt32(0) > 0) - { - return true; - } - } - } - } - } - - return false; - } - // the name length will be no more then (16 + prefix.Length + escapeLeft.Length + escapeRight.Length) // some providers does not support names (Oracle supports up to 30) public static string GetUniqueName(string prefix) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/config.json b/src/Microsoft.Data.SqlClient/tests/ManualTests/config.json index 20f66028c2..1bb115b297 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/config.json +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/config.json @@ -1,6 +1,7 @@ { "TCPConnectionString": "Data Source=tcp:localhost;Database=Northwind;Integrated Security=true", "NPConnectionString": "Data Source=np:localhost;Database=Northwind;Integrated Security=true;", + "TCPConnectionStringWithAEV2HGSVBSSupport": "Data Source = WIN-K4TQB0TR8JQ; Initial Catalog = Northwind; Column Encryption Setting = Enabled; Attestation Protocol = HGS; Enclave Attestation Url= http://WIN-K4TQB0TR8JQ/Attestation;User ID=sa; Password=Moonshine4me; Integrated Security= False;", "AADAccessToken": "", "AADPasswordConnectionString": "", "AzureKeyVaultURL": "", From 2637744da486d834e59c78e46f70d98ef164a935 Mon Sep 17 00:00:00 2001 From: Javad Rahnama Date: Fri, 25 Oct 2019 14:19:13 -0700 Subject: [PATCH 33/63] AEV2 Tests --- .../TestFixtures/Setup/ColumnMasterKey.cs | 32 ++++++++++++++++--- .../ManualTests/DataCommon/DataTestUtility.cs | 13 +++++--- .../tests/ManualTests/config.json | 1 + 3 files changed, 37 insertions(+), 9 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ColumnMasterKey.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ColumnMasterKey.cs index 34ad27241e..749168e8bb 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ColumnMasterKey.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ColumnMasterKey.cs @@ -2,6 +2,8 @@ // 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; + namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted.Setup { public abstract class ColumnMasterKey : DbObject @@ -13,19 +15,39 @@ protected ColumnMasterKey(string name) : base(name) protected string KeyStoreProviderName { get; set; } public abstract string KeyPath { get; } + //public const byte[] Signature =Configuration() + + public override void Create(SqlConnection sqlConnection) { - string sql = - $@"CREATE COLUMN MASTER KEY [{Name}] + string sql = string.Empty; + var connStrings = sqlConnection.ConnectionString; + if (connStrings.Contains("HGS") ||connStrings.Contains("AAS")) + { + sql = + $@"CREATE COLUMN MASTER KEY [{Name}] + WITH ( + KEY_STORE_PROVIDER_NAME = N'{KeyStoreProviderName}', + KEY_PATH = N'{KeyPath}', + ENCLAVE_COMPUTATIONS (SIGNATURE = {DataTestUtility.CertificateSignature}) + );"; + } + else + { + sql = + $@"CREATE COLUMN MASTER KEY [{Name}] WITH ( KEY_STORE_PROVIDER_NAME = N'{KeyStoreProviderName}', KEY_PATH = N'{KeyPath}' );"; - + } using (SqlCommand command = sqlConnection.CreateCommand()) { - command.CommandText = sql; - command.ExecuteNonQuery(); + if (!string.IsNullOrEmpty(sql)) + { + command.CommandText = sql; + command.ExecuteNonQuery(); + } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs index 6386dc61c3..965013af88 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs @@ -8,6 +8,7 @@ using System.Globalization; using System.IO; using System.Reflection; +using System.Text; using System.Threading.Tasks; using Newtonsoft.Json; using Xunit; @@ -26,6 +27,7 @@ public static class DataTestUtility public static readonly string AKVClientId = null; public static readonly string AKVClientSecret = null; public static Dictionary connStrings = new Dictionary(); + public static string CertificateSignature; public static readonly bool SupportsIntegratedSecurity = false; public static readonly bool SupportsLocalDb = false; public static readonly bool SupportsFileStream = false; @@ -48,7 +50,8 @@ private class Config { public string TCPConnectionString = null; public string NPConnectionString = null; - public string TCPConnectionStringWithAEV2HGSVBSSupport = ""; + public string TCPConnectionStringWithAEV2HGSVBSSupport = null; + public string CertificateSignature = null; public string AADAccessToken = null; public string AADPasswordConnectionString = null; public string AzureKeyVaultURL = null; @@ -69,8 +72,7 @@ static DataTestUtility() NPConnectionString = c.NPConnectionString; TCPConnectionString = c.TCPConnectionString; TCPConnectionStringWithAEV2HGSVBSSupport = c.TCPConnectionStringWithAEV2HGSVBSSupport; - connStrings.Add("TCPConnectionString", c.TCPConnectionString); - connStrings.Add("TCPConnectionStringWithAEV2HGSVBSSupport", c.TCPConnectionStringWithAEV2HGSVBSSupport); + CertificateSignature = c.CertificateSignature; AADAccessToken = c.AADAccessToken; AADPasswordConnectionString = c.AADPasswordConnectionString; SupportsLocalDb = c.SupportsLocalDb; @@ -89,6 +91,8 @@ static DataTestUtility() AKVClientId = c.AzureKeyVaultClientId; AKVClientSecret = c.AzureKeyVaultClientSecret; } + connStrings.Add("TCPConnectionString", TCPConnectionString); + connStrings.Add("TCPConnectionStringWithAEV2HGSVBSSupport", TCPConnectionStringWithAEV2HGSVBSSupport); } public static bool IsDatabasePresent(string name) @@ -115,7 +119,8 @@ public static bool IsDatabasePresent(string name) public static bool AreConnStringsSetup() { - return !string.IsNullOrEmpty(NPConnectionString) && !string.IsNullOrEmpty(TCPConnectionString); + return (connStrings.Values.Count > 0); + //return !string.IsNullOrEmpty(NPConnectionString) && !string.IsNullOrEmpty(TCPConnectionString); } public static bool IsAADPasswordConnStrSetup() diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/config.json b/src/Microsoft.Data.SqlClient/tests/ManualTests/config.json index 1bb115b297..c214f64ecc 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/config.json +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/config.json @@ -2,6 +2,7 @@ "TCPConnectionString": "Data Source=tcp:localhost;Database=Northwind;Integrated Security=true", "NPConnectionString": "Data Source=np:localhost;Database=Northwind;Integrated Security=true;", "TCPConnectionStringWithAEV2HGSVBSSupport": "Data Source = WIN-K4TQB0TR8JQ; Initial Catalog = Northwind; Column Encryption Setting = Enabled; Attestation Protocol = HGS; Enclave Attestation Url= http://WIN-K4TQB0TR8JQ/Attestation;User ID=sa; Password=Moonshine4me; Integrated Security= False;", + "CertificateSignature": "0x96CDFA34612108680E8B85C6AD2320776EC5CBC10A62A58408BD084DF58D98B696F0FCC7B1660F32253A59F33A16C739919A0978F7956B61FDD2B603A8D72139E33783691B993DD18837EEA12FAA139F3F10C7F7FCCDA12CCCAA7010B1D2DAEA1EE2D9BE6DB49AF8FDA9FAE907F4C47AE3AB85CEB8E1FF7B5285360419BD3FC9C80B605F23EC6FA50DA69F94F120B7E7250B8393B8BC15D4E952428981693EA67033594B85FA7503193F49083E1BD29C4BF52D02382AB06B9DAD9D9A2448C9B4CECC3734E5B07353C36A9868DB6C5FB7D92A1C2E499819678D07746C1DFA1C19AD14BA0CB18997AFD8E2F301ABCF69793B131D16772D32C337D70602FA02C25F", "AADAccessToken": "", "AADPasswordConnectionString": "", "AzureKeyVaultURL": "", From ffdb7010a0e87bd7f80069581bd803ec76ecf2a9 Mon Sep 17 00:00:00 2001 From: Javad Rahnama Date: Fri, 25 Oct 2019 15:54:29 -0700 Subject: [PATCH 34/63] Fixing some of the tests --- .../ManualTests/AlwaysEncrypted/ApiShould.cs | 44 +++++++++---------- .../ManualTests/DataCommon/DataTestUtility.cs | 34 ++++++++++++-- 2 files changed, 51 insertions(+), 27 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs index 3faa0e349e..3e0891ae49 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs @@ -32,14 +32,10 @@ public ApiShould(SQLSetupStrategyCertStoreProvider fixture) } [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [InlineData(false, "Data Source = WIN-K4TQB0TR8JQ; Initial Catalog = Northwind; Column Encryption Setting = Enabled; Attestation Protocol = HGS; Enclave Attestation Url= http://WIN-K4TQB0TR8JQ/Attestation;User ID=sa; Password=Moonshine4me; Integrated Security= False;")] - [InlineData(true, "Data Source = WIN-K4TQB0TR8JQ; Initial Catalog = Northwind; Column Encryption Setting = Enabled; Attestation Protocol = HGS; Enclave Attestation Url= http://WIN-K4TQB0TR8JQ/Attestation;User ID=sa; Password=Moonshine4me; Integrated Security= False;")] - [InlineData(true, "Data Source=tcp:localhost;Database=Northwind;Integrated Security=true")] - - public void TestSqlTransactionCommitRollbackWithTransparentInsert(bool isCommitted, string value) + [ClassData(typeof(ConnectionStringProviderWithBooleanVariable))] + public void TestSqlTransactionCommitRollbackWithTransparentInsert(string connection, bool isPermitted) { - - using (SqlConnection sqlConnection = new SqlConnection(value)) + using (SqlConnection sqlConnection = new SqlConnection(connection)) { sqlConnection.Open(); @@ -50,7 +46,7 @@ public void TestSqlTransactionCommitRollbackWithTransparentInsert(bool isCommitt { InsertCustomerRecord(sqlConnection, sqlTransaction, customer); - if (isCommitted) + if (isPermitted) { sqlTransaction.Commit(); } @@ -61,7 +57,7 @@ public void TestSqlTransactionCommitRollbackWithTransparentInsert(bool isCommitt } // Data should be available on select if committed else, data should not be available. - if (isCommitted) + if (isPermitted) { VerifyRecordPresent(sqlConnection, customer); } @@ -72,10 +68,11 @@ public void TestSqlTransactionCommitRollbackWithTransparentInsert(bool isCommitt } } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - public void TestSqlTransactionRollbackToSavePoint() + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ClassData(typeof(ConnectionStringProvider))] + public void TestSqlTransactionRollbackToSavePoint(string connection) { - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport)) + using (SqlConnection sqlConnection = new SqlConnection(connection)) { sqlConnection.Open(); @@ -115,8 +112,9 @@ public void TestSqlTransactionRollbackToSavePoint() } } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - public void SqlParameterProperties() + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ClassData(typeof(ConnectionStringProvider))] + public void SqlParameterProperties(string connection) { string tableName = fixture.SqlParameterPropertiesTable.Name; const string firstColumnName = @"firstColumn"; @@ -129,7 +127,7 @@ public void SqlParameterProperties() const int decimalColumnScale = 4; const int timeColumnScale = 5; - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport)) + using (SqlConnection sqlConnection = new SqlConnection(connection)) { try { @@ -385,8 +383,9 @@ private void InsertCustomerRecord(SqlConnection sqlConnection, SqlTransaction sq } } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - public void TestSqlDataAdapterFillDataTable() + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ClassData(typeof(ConnectionStringProvider))] + public void TestSqlDataAdapterFillDataTable(string connection) { const string DummyParamName = "@dummyParam"; int numberOfRows = 100; @@ -395,7 +394,7 @@ public void TestSqlDataAdapterFillDataTable() InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values); - var encryptionEnabledConnectionString = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport) + var encryptionEnabledConnectionString = new SqlConnectionStringBuilder(connection) { ColumnEncryptionSetting = SqlConnectionColumnEncryptionSetting.Enabled }.ConnectionString; @@ -496,9 +495,8 @@ private void ValidateSchema(DataColumnCollection dataColumns) } [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [InlineData(false)] - [InlineData(true)] - public void TestExecuteNonQuery(bool isAsync) + [ClassData(typeof(ConnectionStringProviderWithBooleanVariable))] + public void TestExecuteNonQuery(string connection, bool isAsync) { Parallel.For(0, 10, i => { @@ -510,7 +508,7 @@ public void TestExecuteNonQuery(bool isAsync) Assert.Equal(numberOfRows, rowsAffected); rowsAffected = -1; - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport)) + using (SqlConnection sqlConnection = new SqlConnection(connection)) { sqlConnection.Open(); @@ -1885,7 +1883,7 @@ private void DropHelperProcedures(string[] procNames) sqlConnection.Open(); foreach (string name in procNames) { - string procedureName = name.Trim( new Char[] { '[', ']'}); + string procedureName = name.Trim(new Char[] { '[', ']' }); using (SqlCommand cmd = new SqlCommand(string.Format("IF EXISTS (SELECT * FROM sys.procedures WHERE name = '{0}') \n DROP PROCEDURE {0}", procedureName), sqlConnection)) { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs index 965013af88..bdddcd83f2 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs @@ -3,10 +3,12 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections; using System.Collections.Generic; using System.Data; using System.Globalization; using System.IO; +using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; @@ -91,8 +93,11 @@ static DataTestUtility() AKVClientId = c.AzureKeyVaultClientId; AKVClientSecret = c.AzureKeyVaultClientSecret; } - connStrings.Add("TCPConnectionString", TCPConnectionString); - connStrings.Add("TCPConnectionStringWithAEV2HGSVBSSupport", TCPConnectionStringWithAEV2HGSVBSSupport); + if (!string.IsNullOrEmpty(TCPConnectionString)) + { + connStrings.Add("TCPConnectionString", TCPConnectionString); + connStrings.Add("TCPConnectionStringWithAEV2HGSVBSSupport", TCPConnectionStringWithAEV2HGSVBSSupport); + } } public static bool IsDatabasePresent(string name) @@ -119,8 +124,7 @@ public static bool IsDatabasePresent(string name) public static bool AreConnStringsSetup() { - return (connStrings.Values.Count > 0); - //return !string.IsNullOrEmpty(NPConnectionString) && !string.IsNullOrEmpty(TCPConnectionString); + return connStrings.TryGetValue("TCPConnectionString", out _); } public static bool IsAADPasswordConnStrSetup() @@ -431,4 +435,26 @@ public static DataTable RunQuery(string connectionString, string sql) return result; } } + public class ConnectionStringProviderWithBooleanVariable : IEnumerable + { + public IEnumerator GetEnumerator() + { + yield return new object[] { DataTestUtility.TCPConnectionString, true }; + yield return new object[] { DataTestUtility.TCPConnectionString, false }; + yield return new object[] { DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport, true }; + yield return new object[] { DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport, false }; + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + public class ConnectionStringProvider : IEnumerable + { + public IEnumerator GetEnumerator() + { + yield return new object[] { DataTestUtility.TCPConnectionString }; + yield return new object[] { DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport }; + + } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } } From 80222d541f5a6a033186efe220813e4f7d111ae5 Mon Sep 17 00:00:00 2001 From: Javad Rahnama Date: Mon, 28 Oct 2019 11:23:22 -0700 Subject: [PATCH 35/63] Adding ClassData for testing AEV2 --- .../tests/ManualTests/AlwaysEncrypted/ApiShould.cs | 10 +++++----- .../AlwaysEncrypted/TestFixtures/Setup/ApiTestTable.cs | 10 +++++++--- .../TestFixtures/Setup/ColumnMasterKey.cs | 3 ++- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs index 3e0891ae49..530377a662 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs @@ -539,9 +539,9 @@ public void TestExecuteNonQuery(string connection, bool isAsync) } [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [InlineData(false)] - [InlineData(true)] - public void TestExecuteScalar(bool isAsync) + [ClassData(typeof(ConnectionStringProviderWithBooleanVariable))] + + public void TestExecuteScalar(string connection,bool isAsync) { Parallel.For(0, 10, i => { @@ -551,7 +551,7 @@ public void TestExecuteScalar(bool isAsync) // Insert a bunch of rows in to the table. int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values); - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport)) + using (SqlConnection sqlConnection = new SqlConnection(connection)) { sqlConnection.Open(); @@ -573,7 +573,7 @@ public void TestExecuteScalar(bool isAsync) } else { - customerId = (int)sqlCommand.ExecuteScalar(); + customerId = (int)sqlCommand?.ExecuteScalar(); } Assert.Equal((int)values[0], customerId); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ApiTestTable.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ApiTestTable.cs index 687b0b6ff1..2c39c59cc0 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ApiTestTable.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ApiTestTable.cs @@ -18,13 +18,17 @@ public ApiTestTable(string tableName, ColumnEncryptionKey columnEncryptionKey1, public override void Create(SqlConnection sqlConnection) { - string sql = + string encryptionType = sqlConnection.ConnectionString.Contains("HGS") ? "RANDOMIZED" : "DETERMINISTIC"; + + string sql = $@"CREATE TABLE [dbo].[{Name}] ( - [CustomerId] [int] ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{columnEncryptionKey1.Name}], ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = '{ColumnEncryptionAlgorithmName}'), + [CustomerId] [int] ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{columnEncryptionKey1.Name}], ENCRYPTION_TYPE = {encryptionType}, ALGORITHM = '{ColumnEncryptionAlgorithmName}'), [FirstName] [nvarchar](50) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{columnEncryptionKey2.Name}], ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = '{ColumnEncryptionAlgorithmName}'), [LastName] [nvarchar](50) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{columnEncryptionKey2.Name}], ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = '{ColumnEncryptionAlgorithmName}') - )"; + )"; + + using (SqlCommand command = sqlConnection.CreateCommand()) { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ColumnMasterKey.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ColumnMasterKey.cs index 749168e8bb..199fd637f3 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ColumnMasterKey.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ColumnMasterKey.cs @@ -8,6 +8,7 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted.Setup { public abstract class ColumnMasterKey : DbObject { + protected ColumnMasterKey(string name) : base(name) { } @@ -22,7 +23,7 @@ public override void Create(SqlConnection sqlConnection) { string sql = string.Empty; var connStrings = sqlConnection.ConnectionString; - if (connStrings.Contains("HGS") ||connStrings.Contains("AAS")) + if (connStrings.Contains("HGS") || connStrings.Contains("AAS")) { sql = $@"CREATE COLUMN MASTER KEY [{Name}] From 54fd39922e185bccba9f6f3c0f23bf6af4ce61c8 Mon Sep 17 00:00:00 2001 From: Javad Rahnama Date: Mon, 28 Oct 2019 14:03:40 -0700 Subject: [PATCH 36/63] config file updated --- .../ManualTests/AlwaysEncrypted/ApiShould.cs | 30 +++++++++---------- .../TestFixtures/Setup/ApiTestTable.cs | 5 +--- .../TestFixtures/Setup/ColumnMasterKey.cs | 9 ++++-- .../ManualTests/DataCommon/DataTestUtility.cs | 16 +++++----- .../tests/ManualTests/config.json | 4 +-- 5 files changed, 33 insertions(+), 31 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs index 530377a662..32a222109d 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs @@ -459,7 +459,7 @@ public void TestSqlDataAdapterFillSchema(SchemaType schemaType) // Insert a bunch of rows in to the table. int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values); - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport)) + using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringHGSVBS)) { sqlConnection.Open(); SqlDataAdapter adapter = CreateSqlDataAdapter(sqlConnection); @@ -590,7 +590,7 @@ public void TestSqlDataAdapterBatchUpdate(int numberofRows) DataTable dataTable = CreateDataTable(tableName: tableName, numberofRows: numberofRows); - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport)) + using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringHGSVBS)) { sqlConnection.Open(); @@ -638,7 +638,7 @@ public void TestExecuteReader() int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values); Assert.True(numberOfRows == rowsAffected, "Two values failed"); - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport)) + using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringHGSVBS)) { sqlConnection.Open(); @@ -696,7 +696,7 @@ public void TestExecuteReaderWithCommandBehavior(CommandBehavior commandBehavior int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values); Assert.Equal(rowsAffected, numberOfRows); - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport)) + using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringHGSVBS)) { sqlConnection.Open(); @@ -812,7 +812,7 @@ public void TestPrepareWithExecuteNonQuery() Assert.True(rowsAffected == numberOfRows, "number of rows affected is unexpected."); - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport)) + using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringHGSVBS)) { sqlConnection.Open(); using (SqlCommand sqlCommand = @@ -854,7 +854,7 @@ public void TestAsyncWriteDelayWithExecuteNonQueryAsync() // Insert a bunch of rows in to the table. int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values); - using (SqlConnection sqlconnection = new SqlConnection(DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport)) + using (SqlConnection sqlconnection = new SqlConnection(DataTestUtility.TCPConnectionStringHGSVBS)) { sqlconnection.Open(); @@ -909,7 +909,7 @@ public void TestAsyncWriteDelayWithExecuteReaderAsync() Assert.True(rowsAffected == numberOfRows, "number of rows affected is unexpected."); - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport)) + using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringHGSVBS)) { sqlConnection.Open(); @@ -973,7 +973,7 @@ public void TestPrepareWithExecuteNonQueryAsync() Assert.True(rowsAffected == numberOfRows, "number of rows affected is unexpected."); - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport)) + using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringHGSVBS)) { sqlConnection.Open(); using (SqlCommand sqlCommand = new SqlCommand( @@ -1025,7 +1025,7 @@ public void TestPrepareWithExecuteReaderAsync(CommandBehavior commandBehavior) Assert.True(rowsAffected == numberOfRows, "number of rows affected is unexpected."); - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport)) + using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringHGSVBS)) { sqlConnection.Open(); @@ -1089,7 +1089,7 @@ public void TestSqlDataReaderAPIs(SqlCommandColumnEncryptionSetting value) Assert.True(rowsAffected == numberOfRows, "number of rows affected is unexpected."); - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport)) + using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringHGSVBS)) { sqlConnection.Open(); @@ -1284,7 +1284,7 @@ public void TestSqlDataReaderAPIsWithSequentialAccess(object value) Assert.Equal(rowsAffected, numberOfRows); - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport)) + using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringHGSVBS)) { sqlConnection.Open(); using (SqlCommand sqlCommand = new SqlCommand($"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", @@ -1708,7 +1708,7 @@ public void TestSqlCommandSequentialAccessCodePaths(object value) Assert.True(rowsAffected == numberOfRows, "number of rows affected is unexpected."); - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport)) + using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringHGSVBS)) { sqlConnection.Open(); // Test SqlDataReader.GetStream() on encrypted column, throw an exception. @@ -1852,7 +1852,7 @@ private int InsertRows(string tableName, int numberofRows, IList values) { int rowsAffected = 0; - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport)) + using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringHGSVBS)) { sqlConnection.Open(); @@ -1878,7 +1878,7 @@ private int InsertRows(string tableName, int numberofRows, IList values) /// private void DropHelperProcedures(string[] procNames) { - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport)) + using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringHGSVBS)) { sqlConnection.Open(); foreach (string name in procNames) @@ -2010,7 +2010,7 @@ private void TestDataAdapterFillResults(DataTable dataTable, IList value public void Dispose() { - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport)) + using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringHGSVBS)) { sqlConnection.Open(); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ApiTestTable.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ApiTestTable.cs index 2c39c59cc0..a605ffeb4b 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ApiTestTable.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ApiTestTable.cs @@ -19,8 +19,7 @@ public ApiTestTable(string tableName, ColumnEncryptionKey columnEncryptionKey1, public override void Create(SqlConnection sqlConnection) { string encryptionType = sqlConnection.ConnectionString.Contains("HGS") ? "RANDOMIZED" : "DETERMINISTIC"; - - string sql = + string sql = $@"CREATE TABLE [dbo].[{Name}] ( [CustomerId] [int] ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{columnEncryptionKey1.Name}], ENCRYPTION_TYPE = {encryptionType}, ALGORITHM = '{ColumnEncryptionAlgorithmName}'), @@ -28,8 +27,6 @@ [FirstName] [nvarchar](50) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (COLUMN_EN [LastName] [nvarchar](50) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{columnEncryptionKey2.Name}], ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = '{ColumnEncryptionAlgorithmName}') )"; - - using (SqlCommand command = sqlConnection.CreateCommand()) { command.CommandText = sql; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ColumnMasterKey.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ColumnMasterKey.cs index 199fd637f3..42b98c7003 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ColumnMasterKey.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ColumnMasterKey.cs @@ -3,6 +3,9 @@ // See the LICENSE file in the project root for more information. using System; +using System.Data; +using System.Data.SqlTypes; +using Xunit; namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted.Setup { @@ -21,7 +24,7 @@ protected ColumnMasterKey(string name) : base(name) public override void Create(SqlConnection sqlConnection) { - string sql = string.Empty; + string sql; var connStrings = sqlConnection.ConnectionString; if (connStrings.Contains("HGS") || connStrings.Contains("AAS")) { @@ -30,10 +33,10 @@ public override void Create(SqlConnection sqlConnection) WITH ( KEY_STORE_PROVIDER_NAME = N'{KeyStoreProviderName}', KEY_PATH = N'{KeyPath}', - ENCLAVE_COMPUTATIONS (SIGNATURE = {DataTestUtility.CertificateSignature}) + ENCLAVE_COMPUTATIONS (SIGNATURE ={DataTestUtility.CertificateSignature}) );"; } - else + else { sql = $@"CREATE COLUMN MASTER KEY [{Name}] diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs index bdddcd83f2..b41aa03f26 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs @@ -21,7 +21,7 @@ public static class DataTestUtility { public static readonly string NPConnectionString = null; public static readonly string TCPConnectionString = null; - public static readonly string TCPConnectionStringWithAEV2HGSVBSSupport = null; + public static readonly string TCPConnectionStringHGSVBS = null; public static readonly string AADAccessToken = null; public static readonly string AADPasswordConnectionString = null; public static readonly string AKVBaseUrl = null; @@ -52,7 +52,7 @@ private class Config { public string TCPConnectionString = null; public string NPConnectionString = null; - public string TCPConnectionStringWithAEV2HGSVBSSupport = null; + public string TCPConnectionStringHGSVBS = null; public string CertificateSignature = null; public string AADAccessToken = null; public string AADPasswordConnectionString = null; @@ -73,7 +73,7 @@ static DataTestUtility() NPConnectionString = c.NPConnectionString; TCPConnectionString = c.TCPConnectionString; - TCPConnectionStringWithAEV2HGSVBSSupport = c.TCPConnectionStringWithAEV2HGSVBSSupport; + TCPConnectionStringHGSVBS = c.TCPConnectionStringHGSVBS; CertificateSignature = c.CertificateSignature; AADAccessToken = c.AADAccessToken; AADPasswordConnectionString = c.AADPasswordConnectionString; @@ -96,7 +96,7 @@ static DataTestUtility() if (!string.IsNullOrEmpty(TCPConnectionString)) { connStrings.Add("TCPConnectionString", TCPConnectionString); - connStrings.Add("TCPConnectionStringWithAEV2HGSVBSSupport", TCPConnectionStringWithAEV2HGSVBSSupport); + connStrings.Add("TCPConnectionStringWithAEV2HGSVBSSupport", TCPConnectionStringHGSVBS); } } @@ -434,6 +434,8 @@ public static DataTable RunQuery(string connectionString, string sql) } return result; } + + } public class ConnectionStringProviderWithBooleanVariable : IEnumerable { @@ -441,8 +443,8 @@ public IEnumerator GetEnumerator() { yield return new object[] { DataTestUtility.TCPConnectionString, true }; yield return new object[] { DataTestUtility.TCPConnectionString, false }; - yield return new object[] { DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport, true }; - yield return new object[] { DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport, false }; + yield return new object[] { DataTestUtility.TCPConnectionStringHGSVBS, true }; + yield return new object[] { DataTestUtility.TCPConnectionStringHGSVBS, false }; } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); @@ -452,7 +454,7 @@ public class ConnectionStringProvider : IEnumerable public IEnumerator GetEnumerator() { yield return new object[] { DataTestUtility.TCPConnectionString }; - yield return new object[] { DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport }; + yield return new object[] { DataTestUtility.TCPConnectionStringHGSVBS }; } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/config.json b/src/Microsoft.Data.SqlClient/tests/ManualTests/config.json index c214f64ecc..2fde8c6ebb 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/config.json +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/config.json @@ -1,9 +1,9 @@ { "TCPConnectionString": "Data Source=tcp:localhost;Database=Northwind;Integrated Security=true", "NPConnectionString": "Data Source=np:localhost;Database=Northwind;Integrated Security=true;", - "TCPConnectionStringWithAEV2HGSVBSSupport": "Data Source = WIN-K4TQB0TR8JQ; Initial Catalog = Northwind; Column Encryption Setting = Enabled; Attestation Protocol = HGS; Enclave Attestation Url= http://WIN-K4TQB0TR8JQ/Attestation;User ID=sa; Password=Moonshine4me; Integrated Security= False;", + "TCPConnectionStringHGSVBS": "Data Source = WIN-K4TQB0TR8JQ; Initial Catalog = Northwind; Column Encryption Setting = Enabled; Attestation Protocol = HGS; Enclave Attestation Url= http://WIN-K4TQB0TR8JQ/Attestation;User ID=sa; Password=Moonshine4me; Integrated Security= False;", "CertificateSignature": "0x96CDFA34612108680E8B85C6AD2320776EC5CBC10A62A58408BD084DF58D98B696F0FCC7B1660F32253A59F33A16C739919A0978F7956B61FDD2B603A8D72139E33783691B993DD18837EEA12FAA139F3F10C7F7FCCDA12CCCAA7010B1D2DAEA1EE2D9BE6DB49AF8FDA9FAE907F4C47AE3AB85CEB8E1FF7B5285360419BD3FC9C80B605F23EC6FA50DA69F94F120B7E7250B8393B8BC15D4E952428981693EA67033594B85FA7503193F49083E1BD29C4BF52D02382AB06B9DAD9D9A2448C9B4CECC3734E5B07353C36A9868DB6C5FB7D92A1C2E499819678D07746C1DFA1C19AD14BA0CB18997AFD8E2F301ABCF69793B131D16772D32C337D70602FA02C25F", - "AADAccessToken": "", + "\",": null, "AADPasswordConnectionString": "", "AzureKeyVaultURL": "", "AzureKeyVaultClientId": "", From 7fca4be6cfe5109b9584ec766230b1dfbd616b7e Mon Sep 17 00:00:00 2001 From: Karina Zhou Date: Mon, 28 Oct 2019 14:17:20 -0700 Subject: [PATCH 37/63] update config --- src/Microsoft.Data.SqlClient/tests/ManualTests/config.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/config.json b/src/Microsoft.Data.SqlClient/tests/ManualTests/config.json index c214f64ecc..d7f07f83ba 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/config.json +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/config.json @@ -1,8 +1,8 @@ { "TCPConnectionString": "Data Source=tcp:localhost;Database=Northwind;Integrated Security=true", "NPConnectionString": "Data Source=np:localhost;Database=Northwind;Integrated Security=true;", - "TCPConnectionStringWithAEV2HGSVBSSupport": "Data Source = WIN-K4TQB0TR8JQ; Initial Catalog = Northwind; Column Encryption Setting = Enabled; Attestation Protocol = HGS; Enclave Attestation Url= http://WIN-K4TQB0TR8JQ/Attestation;User ID=sa; Password=Moonshine4me; Integrated Security= False;", - "CertificateSignature": "0x96CDFA34612108680E8B85C6AD2320776EC5CBC10A62A58408BD084DF58D98B696F0FCC7B1660F32253A59F33A16C739919A0978F7956B61FDD2B603A8D72139E33783691B993DD18837EEA12FAA139F3F10C7F7FCCDA12CCCAA7010B1D2DAEA1EE2D9BE6DB49AF8FDA9FAE907F4C47AE3AB85CEB8E1FF7B5285360419BD3FC9C80B605F23EC6FA50DA69F94F120B7E7250B8393B8BC15D4E952428981693EA67033594B85FA7503193F49083E1BD29C4BF52D02382AB06B9DAD9D9A2448C9B4CECC3734E5B07353C36A9868DB6C5FB7D92A1C2E499819678D07746C1DFA1C19AD14BA0CB18997AFD8E2F301ABCF69793B131D16772D32C337D70602FA02C25F", + "TCPConnectionStringWithAEV2HGSVBSSupport": "", + "CertificateSignature": "", "AADAccessToken": "", "AADPasswordConnectionString": "", "AzureKeyVaultURL": "", From a6929fab8551eb1b1b5286a2ca4555eee834db24 Mon Sep 17 00:00:00 2001 From: Karina Zhou Date: Mon, 28 Oct 2019 14:27:11 -0700 Subject: [PATCH 38/63] Add more connection string for enclave testing --- src/Microsoft.Data.SqlClient/tests/ManualTests/config.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/config.json b/src/Microsoft.Data.SqlClient/tests/ManualTests/config.json index 6d195aa9b8..fa37108d2c 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/config.json +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/config.json @@ -2,7 +2,10 @@ "TCPConnectionString": "Data Source=tcp:localhost;Database=Northwind;Integrated Security=true", "NPConnectionString": "Data Source=np:localhost;Database=Northwind;Integrated Security=true;", "TCPConnectionStringHGSVBS": "", + "TCPConnectionStringAASVBS": "", + "TCPConnectionStringAASSGX": "", "CertificateSignature": "", + "AADAccessToken": "", "AADPasswordConnectionString": "", "AzureKeyVaultURL": "", "AzureKeyVaultClientId": "", From 360237f81a824e7025b1e1dc4560ba63989d8dfa Mon Sep 17 00:00:00 2001 From: Javad Rahnama Date: Tue, 29 Oct 2019 10:03:45 -0700 Subject: [PATCH 39/63] Adjusting APIShoould and BulkCopyAE to wwork with AEV2 --- .../ManualTests/AlwaysEncrypted/ApiShould.cs | 887 +++++++++--------- .../ManualTests/AlwaysEncrypted/BulkCopyAE.cs | 16 +- .../TestFixtures/Setup/ColumnMasterKey.cs | 11 +- .../TestTrustedMasterKeyPaths.cs | 10 +- .../ManualTests/DataCommon/DataTestUtility.cs | 39 + 5 files changed, 517 insertions(+), 446 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs index 32a222109d..6b2b5a1320 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs @@ -315,7 +315,7 @@ public void SqlParameterProperties(string connection) } finally { - DropHelperProcedures(new string[] { inputProcedureName, outputProcedureName }); + DropHelperProcedures(new string[] { inputProcedureName, outputProcedureName }, connection); } } @@ -392,7 +392,7 @@ public void TestSqlDataAdapterFillDataTable(string connection) IList values = GetValues(dataHint: 71); - InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values); + InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection); var encryptionEnabledConnectionString = new SqlConnectionStringBuilder(connection) { @@ -449,17 +449,16 @@ public void TestSqlDataAdapterFillDataTable(string connection) } [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [InlineData(SchemaType.Source)] - [InlineData(SchemaType.Mapped)] - public void TestSqlDataAdapterFillSchema(SchemaType schemaType) + [ClassData(typeof(ConnectionStringProviderWithSchemaType))] + public void TestSqlDataAdapterFillSchema(string connection, SchemaType schemaType) { IList values = GetValues(dataHint: 44); int numberOfRows = 42; // Insert a bunch of rows in to the table. - int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values); + int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection); - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringHGSVBS)) + using (SqlConnection sqlConnection = new SqlConnection(connection)) { sqlConnection.Open(); SqlDataAdapter adapter = CreateSqlDataAdapter(sqlConnection); @@ -504,7 +503,7 @@ public void TestExecuteNonQuery(string connection, bool isAsync) int numberOfRows = 10 + i; // Insert a bunch of rows in to the table. - int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values); + int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection); Assert.Equal(numberOfRows, rowsAffected); rowsAffected = -1; @@ -519,6 +518,12 @@ public void TestExecuteNonQuery(string connection, bool isAsync) transaction: null, columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) { + if (isAsync) + { + //Increase Time out in Async mode. + sqlCommand.CommandTimeout = 60; + } + sqlCommand.Parameters.AddWithValue(@"FirstName", string.Format(@"Microsoft{0}", i + 100)); sqlCommand.Parameters.AddWithValue(@"CustomerId", values[0]); @@ -540,8 +545,7 @@ public void TestExecuteNonQuery(string connection, bool isAsync) [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] [ClassData(typeof(ConnectionStringProviderWithBooleanVariable))] - - public void TestExecuteScalar(string connection,bool isAsync) + public void TestExecuteScalar(string connection, bool isAsync) { Parallel.For(0, 10, i => { @@ -549,7 +553,7 @@ public void TestExecuteScalar(string connection,bool isAsync) int numberOfRows = 10 + i; // Insert a bunch of rows in to the table. - int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values); + int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection); using (SqlConnection sqlConnection = new SqlConnection(connection)) { @@ -563,6 +567,8 @@ public void TestExecuteScalar(string connection,bool isAsync) transaction: null, columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) { + sqlCommand.CommandTimeout = 60; + sqlCommand.Parameters.AddWithValue(@"CustomerId", values[0]); int customerId = -1; @@ -583,14 +589,13 @@ public void TestExecuteScalar(string connection,bool isAsync) } [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [InlineData(1)] - [InlineData(100)] - public void TestSqlDataAdapterBatchUpdate(int numberofRows) + [ClassData(typeof(ConnectionStringProviderWithIntegers))] + public void TestSqlDataAdapterBatchUpdate(string connection,int numberofRows) { DataTable dataTable = CreateDataTable(tableName: tableName, numberofRows: numberofRows); - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringHGSVBS)) + using (SqlConnection sqlConnection = new SqlConnection(connection)) { sqlConnection.Open(); @@ -627,18 +632,19 @@ public void TestSqlDataAdapterBatchUpdate(int numberofRows) } } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - public void TestExecuteReader() + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ClassData(typeof(ConnectionStringProvider))] + public void TestExecuteReader(string connection) { Parallel.For(0, 10, i => { IList values = GetValues(dataHint: 45 + i + 1); int numberOfRows = 10 + i; // Insert a bunch of rows in to the table. - int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values); + int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection); Assert.True(numberOfRows == rowsAffected, "Two values failed"); - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringHGSVBS)) + using (SqlConnection sqlConnection = new SqlConnection(connection)) { sqlConnection.Open(); @@ -649,6 +655,9 @@ public void TestExecuteReader() transaction: null, columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) { + //Increas command time out to a minute for async calls. + sqlCommand.CommandTimeout = 60; + sqlCommand.Parameters.AddWithValue(@"FirstName", string.Format(@"Microsoft{0}", i + 100)); sqlCommand.Parameters.AddWithValue(@"CustomerId", values[0]); IAsyncResult asyncResult = sqlCommand.BeginExecuteReader(); @@ -673,11 +682,8 @@ public void TestExecuteReader() } [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [InlineData(CommandBehavior.SingleResult)] - [InlineData(CommandBehavior.SingleRow)] - [InlineData(CommandBehavior.CloseConnection)] - [InlineData(CommandBehavior.SequentialAccess)] - public void TestExecuteReaderWithCommandBehavior(CommandBehavior commandBehavior) + [ClassData(typeof(ConnectionStringProviderWithCommandBehavior))] + public void TestExecuteReaderWithCommandBehavior(string connection, CommandBehavior commandBehavior) { string[] columnNames = new string[3] { "CustomerId", "FirstName", "LastName" }; string[] columnOrdinals = new string[3] { @"0", @"1", @"2" }; @@ -693,10 +699,10 @@ public void TestExecuteReaderWithCommandBehavior(CommandBehavior commandBehavior Assert.False(values == null || values.Count < 3, @"values should not be null and count should be >= 3."); int numberOfRows = 10 + i; // Insert a bunch of rows in to the table. - int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values); + int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection); Assert.Equal(rowsAffected, numberOfRows); - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringHGSVBS)) + using (SqlConnection sqlConnection = new SqlConnection(connection)) { sqlConnection.Open(); @@ -798,8 +804,9 @@ public void TestExecuteReaderWithCommandBehavior(CommandBehavior commandBehavior }); } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - public void TestPrepareWithExecuteNonQuery() + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ClassData(typeof(ConnectionStringProvider))] + public void TestPrepareWithExecuteNonQuery(string connection) { IList values = GetValues(dataHint: 52); @@ -808,11 +815,11 @@ public void TestPrepareWithExecuteNonQuery() int numberOfRows = 10; // Insert a bunch of rows in to the table. - int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values); + int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection); Assert.True(rowsAffected == numberOfRows, "number of rows affected is unexpected."); - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringHGSVBS)) + using (SqlConnection sqlConnection = new SqlConnection(connection)) { sqlConnection.Open(); using (SqlCommand sqlCommand = @@ -844,17 +851,18 @@ public void TestPrepareWithExecuteNonQuery() } } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - public void TestAsyncWriteDelayWithExecuteNonQueryAsync() + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ClassData(typeof(ConnectionStringProvider))] + public void TestAsyncWriteDelayWithExecuteNonQueryAsync(string connection) { IList values = GetValues(dataHint: 53); Assert.True(values != null && values.Count >= 3, @"values should not be null and count should be >= 3."); int numberOfRows = 10; // Insert a bunch of rows in to the table. - int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values); + int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection); - using (SqlConnection sqlconnection = new SqlConnection(DataTestUtility.TCPConnectionStringHGSVBS)) + using (SqlConnection sqlconnection = new SqlConnection(connection)) { sqlconnection.Open(); @@ -895,8 +903,9 @@ public void TestAsyncWriteDelayWithExecuteNonQueryAsync() } } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - public void TestAsyncWriteDelayWithExecuteReaderAsync() + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ClassData(typeof(ConnectionStringProvider))] + public void TestAsyncWriteDelayWithExecuteReaderAsync(string connection) { IList values = GetValues(dataHint: 53); @@ -905,11 +914,11 @@ public void TestAsyncWriteDelayWithExecuteReaderAsync() int numberOfRows = 10; // Insert a bunch of rows in to the table. - int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values); + int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection); Assert.True(rowsAffected == numberOfRows, "number of rows affected is unexpected."); - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringHGSVBS)) + using (SqlConnection sqlConnection = new SqlConnection(connection)) { sqlConnection.Open(); @@ -959,8 +968,9 @@ public void TestAsyncWriteDelayWithExecuteReaderAsync() } } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - public void TestPrepareWithExecuteNonQueryAsync() + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ClassData(typeof(ConnectionStringProvider))] + public void TestPrepareWithExecuteNonQueryAsync(string connection) { IList values = GetValues(dataHint: 53); @@ -969,11 +979,11 @@ public void TestPrepareWithExecuteNonQueryAsync() int numberOfRows = 10; // Insert a bunch of rows in to the table. - int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values); + int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection); Assert.True(rowsAffected == numberOfRows, "number of rows affected is unexpected."); - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringHGSVBS)) + using (SqlConnection sqlConnection = new SqlConnection(connection)) { sqlConnection.Open(); using (SqlCommand sqlCommand = new SqlCommand( @@ -1012,70 +1022,75 @@ public void TestPrepareWithExecuteNonQueryAsync() } [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [InlineData(CommandBehavior.Default)] - [InlineData(CommandBehavior.SequentialAccess)] - public void TestPrepareWithExecuteReaderAsync(CommandBehavior commandBehavior) + //[InlineData(CommandBehavior.Default)] + //[InlineData(CommandBehavior.SequentialAccess)] + [ClassData(typeof(ConnectionStringProviderWithCommandBehavior))] + public void TestPrepareWithExecuteReaderAsync(string connection, CommandBehavior commandBehavior) { - IList values = GetValues(dataHint: 54); - Assert.True(values != null && values.Count <= 3, @"values should not be null and count should be >= 3."); - int numberOfRows = 10; - - // Insert a bunch of rows in to the table. - int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values); + if (commandBehavior == CommandBehavior.Default || commandBehavior == CommandBehavior.SequentialAccess) + { + IList values = GetValues(dataHint: 54); + Assert.True(values != null && values.Count <= 3, @"values should not be null and count should be >= 3."); + int numberOfRows = 10; - Assert.True(rowsAffected == numberOfRows, "number of rows affected is unexpected."); + // Insert a bunch of rows in to the table. + int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection: connection); - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringHGSVBS)) - { - sqlConnection.Open(); + Assert.True(rowsAffected == numberOfRows, "number of rows affected is unexpected."); - using (SqlCommand sqlCommand = new SqlCommand($"SELECT * FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", - sqlConnection, - transaction: null, - columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) + using (SqlConnection sqlConnection = new SqlConnection(connection)) { - sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); - sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); + sqlConnection.Open(); - sqlCommand.Parameters[0].Value = values[0]; - sqlCommand.Parameters[1].Value = values[1]; + using (SqlCommand sqlCommand = new SqlCommand($"SELECT * FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", + sqlConnection, + transaction: null, + columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) + { + sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); + sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); - sqlCommand.Prepare(); + sqlCommand.Parameters[0].Value = values[0]; + sqlCommand.Parameters[1].Value = values[1]; - rowsAffected = 0; - IAsyncResult asyncResult = sqlCommand.BeginExecuteReader(commandBehavior); + sqlCommand.Prepare(); - using (SqlDataReader sqlDataReader = sqlCommand.EndExecuteReader(asyncResult)) - { - while (sqlDataReader.Read()) + rowsAffected = 0; + IAsyncResult asyncResult = sqlCommand.BeginExecuteReader(commandBehavior); + + using (SqlDataReader sqlDataReader = sqlCommand.EndExecuteReader(asyncResult)) { - rowsAffected++; + while (sqlDataReader.Read()) + { + rowsAffected++; - Assert.True(values != null && values.Count >= 3, @"values should not be null and should be with atleast 3 elements."); - Assert.True(sqlDataReader.GetInt32(0) == (int)values[0], "CustomerId value read from the table was incorrect."); - Assert.True(sqlDataReader.GetString(1) == (string)values[1], "FirstName value read from the table was incorrect."); - Assert.True(sqlDataReader.GetString(2) == (string)values[2], "LastName value read from the table was incorrect."); + Assert.True(values != null && values.Count >= 3, @"values should not be null and should be with atleast 3 elements."); + Assert.True(sqlDataReader.GetInt32(0) == (int)values[0], "CustomerId value read from the table was incorrect."); + Assert.True(sqlDataReader.GetString(1) == (string)values[1], "FirstName value read from the table was incorrect."); + Assert.True(sqlDataReader.GetString(2) == (string)values[2], "LastName value read from the table was incorrect."); + } } - } - Assert.True(rowsAffected == 10, "Unexpected number of rows affected as returned by ExecuteNonQueryAsync."); + Assert.True(rowsAffected == 10, "Unexpected number of rows affected as returned by ExecuteNonQueryAsync."); - sqlCommand.Parameters[0].Value = (int)values[0] + 1; - sqlCommand.Parameters[1].Value = values[1]; + sqlCommand.Parameters[0].Value = (int)values[0] + 1; + sqlCommand.Parameters[1].Value = values[1]; - Task executeTask = VerifyExecuteNonQueryAsync(sqlCommand); + Task executeTask = VerifyExecuteNonQueryAsync(sqlCommand); - rowsAffected = -1; - rowsAffected = executeTask.Result; + rowsAffected = -1; + rowsAffected = executeTask.Result; - Assert.True(rowsAffected == -1, "Unexpected number of rows affected as returned by ExecuteNonQueryAsync."); + Assert.True(rowsAffected == -1, "Unexpected number of rows affected as returned by ExecuteNonQueryAsync."); + } } } } [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [InlineData(SqlCommandColumnEncryptionSetting.Enabled)] - public void TestSqlDataReaderAPIs(SqlCommandColumnEncryptionSetting value) + [ClassData(typeof(ConnectionStringProvider))] + public void TestSqlDataReaderAPIs(string connection) { + var value = SqlCommandColumnEncryptionSetting.Enabled; char[] textValue = null; int numberOfRows = 100; string commandTextForEncryptionDisabledResultSetOnly = @"SELECT CustomerId, FirstName, LastName/*, BinaryColumn, NvarcharMaxColumn */ FROM [{0}]"; @@ -1085,11 +1100,11 @@ public void TestSqlDataReaderAPIs(SqlCommandColumnEncryptionSetting value) Assert.True(values != null && values.Count >= 3, @"values should not be null and count should be >= 3."); // Insert a bunch of rows in to the table. - int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values); + int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection); Assert.True(rowsAffected == numberOfRows, "number of rows affected is unexpected."); - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringHGSVBS)) + using (SqlConnection sqlConnection = new SqlConnection(connection)) { sqlConnection.Open(); @@ -1267,424 +1282,427 @@ public void TestSqlDataReaderAPIs(SqlCommandColumnEncryptionSetting value) } [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [InlineData(CommandBehavior.SequentialAccess)] - public void TestSqlDataReaderAPIsWithSequentialAccess(object value) + [ClassData(typeof(ConnectionStringProviderWithCommandBehavior))] + public void TestSqlDataReaderAPIsWithSequentialAccess(string connection, CommandBehavior value) { - char[] textValue = null; - CommandBehavior commandBehavior = (CommandBehavior)value; + if (value == CommandBehavior.SequentialAccess) + { + char[] textValue = null; + CommandBehavior commandBehavior = (CommandBehavior)value; - IList values = GetValues(dataHint: 56); + IList values = GetValues(dataHint: 56); - Assert.True(values != null && values.Count >= 3, @"values should not be null and count should be >= 3."); + Assert.True(values != null && values.Count >= 3, @"values should not be null and count should be >= 3."); - int numberOfRows = 10; + int numberOfRows = 10; - // Insert a bunch of rows in to the table. - int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values); + // Insert a bunch of rows in to the table. + int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection); - Assert.Equal(rowsAffected, numberOfRows); + Assert.Equal(rowsAffected, numberOfRows); - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringHGSVBS)) - { - sqlConnection.Open(); - using (SqlCommand sqlCommand = new SqlCommand($"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", - sqlConnection, - transaction: null, - columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) + using (SqlConnection sqlConnection = new SqlConnection(connection)) { - sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); - sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); + sqlConnection.Open(); + using (SqlCommand sqlCommand = new SqlCommand($"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", + sqlConnection, + transaction: null, + columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) + { + sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); + sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); - sqlCommand.Parameters[0].Value = values[0]; - sqlCommand.Parameters[1].Value = values[1]; + sqlCommand.Parameters[0].Value = values[0]; + sqlCommand.Parameters[1].Value = values[1]; - sqlCommand.Prepare(); - rowsAffected = 0; + sqlCommand.Prepare(); + rowsAffected = 0; - IAsyncResult asyncResult = sqlCommand.BeginExecuteReader(commandBehavior); - using (SqlDataReader sqlDataReader = sqlCommand.EndExecuteReader(asyncResult)) - { - Assert.True(sqlDataReader.GetName(0) == @"CustomerId", "CustomerId value read from the table was incorrect."); - Assert.True(sqlDataReader.GetName(1) == @"FirstName", "FirstName value read from the table was incorrect."); - Assert.True(sqlDataReader.GetName(2) == @"LastName", "LastName value read from the table was incorrect."); + IAsyncResult asyncResult = sqlCommand.BeginExecuteReader(commandBehavior); + using (SqlDataReader sqlDataReader = sqlCommand.EndExecuteReader(asyncResult)) + { + Assert.True(sqlDataReader.GetName(0) == @"CustomerId", "CustomerId value read from the table was incorrect."); + Assert.True(sqlDataReader.GetName(1) == @"FirstName", "FirstName value read from the table was incorrect."); + Assert.True(sqlDataReader.GetName(2) == @"LastName", "LastName value read from the table was incorrect."); - Assert.True(sqlDataReader.GetOrdinal(@"CustomerId") == 0, "CustomerId value read from the table was incorrect."); - Assert.True(sqlDataReader.GetOrdinal(@"FirstName") == 1, "FirstName value read from the table was incorrect."); - Assert.True(sqlDataReader.GetOrdinal(@"LastName") == 2, "LastName value read from the table was incorrect."); + Assert.True(sqlDataReader.GetOrdinal(@"CustomerId") == 0, "CustomerId value read from the table was incorrect."); + Assert.True(sqlDataReader.GetOrdinal(@"FirstName") == 1, "FirstName value read from the table was incorrect."); + Assert.True(sqlDataReader.GetOrdinal(@"LastName") == 2, "LastName value read from the table was incorrect."); - Assert.True(sqlDataReader.GetFieldType(0) == typeof(System.Int32), "CustomerId value read from the table was incorrect."); - Assert.True(sqlDataReader.GetFieldType(1) == typeof(System.String), "FirstName value read from the table was incorrect."); - Assert.True(sqlDataReader.GetFieldType(2) == typeof(System.String), "LastName value read from the table was incorrect."); + Assert.True(sqlDataReader.GetFieldType(0) == typeof(System.Int32), "CustomerId value read from the table was incorrect."); + Assert.True(sqlDataReader.GetFieldType(1) == typeof(System.String), "FirstName value read from the table was incorrect."); + Assert.True(sqlDataReader.GetFieldType(2) == typeof(System.String), "LastName value read from the table was incorrect."); - Assert.True(sqlDataReader.GetProviderSpecificFieldType(0) == typeof(System.Data.SqlTypes.SqlInt32), "CustomerId value read from the table was incorrect."); - Assert.True(sqlDataReader.GetProviderSpecificFieldType(1) == typeof(System.Data.SqlTypes.SqlString), "FirstName value read from the table was incorrect."); - Assert.True(sqlDataReader.GetProviderSpecificFieldType(2) == typeof(System.Data.SqlTypes.SqlString), "LastName value read from the table was incorrect."); + Assert.True(sqlDataReader.GetProviderSpecificFieldType(0) == typeof(System.Data.SqlTypes.SqlInt32), "CustomerId value read from the table was incorrect."); + Assert.True(sqlDataReader.GetProviderSpecificFieldType(1) == typeof(System.Data.SqlTypes.SqlString), "FirstName value read from the table was incorrect."); + Assert.True(sqlDataReader.GetProviderSpecificFieldType(2) == typeof(System.Data.SqlTypes.SqlString), "LastName value read from the table was incorrect."); - Assert.True(sqlDataReader.GetDataTypeName(0) == @"int", "CustomerId value read from the table was incorrect."); - Assert.True(sqlDataReader.GetDataTypeName(1) == @"nvarchar", "FirstName value read from the table was incorrect."); - Assert.True(sqlDataReader.GetDataTypeName(2) == @"nvarchar", "LastName value read from the table was incorrect."); + Assert.True(sqlDataReader.GetDataTypeName(0) == @"int", "CustomerId value read from the table was incorrect."); + Assert.True(sqlDataReader.GetDataTypeName(1) == @"nvarchar", "FirstName value read from the table was incorrect."); + Assert.True(sqlDataReader.GetDataTypeName(2) == @"nvarchar", "LastName value read from the table was incorrect."); - while (sqlDataReader.Read()) - { - textValue = new char[((string)values[1]).Length]; - sqlDataReader.GetChars(1, 0, textValue, 0, textValue.Length); - Assert.True(new string(textValue) == (string)values[1], @"Value returned by GetChars is unexpected."); + while (sqlDataReader.Read()) + { + textValue = new char[((string)values[1]).Length]; + sqlDataReader.GetChars(1, 0, textValue, 0, textValue.Length); + Assert.True(new string(textValue) == (string)values[1], @"Value returned by GetChars is unexpected."); - textValue = new char[((string)values[2]).Length]; - sqlDataReader.GetChars(2, 0, textValue, 0, textValue.Length); - Assert.True(new string(textValue) == (string)values[2], @"Value returned by GetChars is unexpected."); + textValue = new char[((string)values[2]).Length]; + sqlDataReader.GetChars(2, 0, textValue, 0, textValue.Length); + Assert.True(new string(textValue) == (string)values[2], @"Value returned by GetChars is unexpected."); + } } } - } - // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. - using (SqlCommand sqlCommand = new SqlCommand($@"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", - sqlConnection, - transaction: null, - columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) - { - sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); - sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); + // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. + using (SqlCommand sqlCommand = new SqlCommand($@"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", + sqlConnection, + transaction: null, + columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) + { + sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); + sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); - sqlCommand.Parameters[0].Value = values[0]; - sqlCommand.Parameters[1].Value = values[1]; + sqlCommand.Parameters[0].Value = values[0]; + sqlCommand.Parameters[1].Value = values[1]; - using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) - { - while (sqlDataReader.Read()) + using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) { - Exception ex = Assert.Throws(() => sqlDataReader.GetTextReader(1)); - Assert.Equal("Retrieving encrypted column 'FirstName' with CommandBehavior=SequentialAccess is not supported.", ex.Message); + while (sqlDataReader.Read()) + { + Exception ex = Assert.Throws(() => sqlDataReader.GetTextReader(1)); + Assert.Equal("Retrieving encrypted column 'FirstName' with CommandBehavior=SequentialAccess is not supported.", ex.Message); + } } } - } - // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. - using (SqlCommand sqlCommand = new SqlCommand($"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", - sqlConnection, - transaction: null, - columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) - { - sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); - sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); + // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. + using (SqlCommand sqlCommand = new SqlCommand($"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", + sqlConnection, + transaction: null, + columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) + { + sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); + sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); - sqlCommand.Parameters[0].Value = values[0]; - sqlCommand.Parameters[1].Value = values[1]; + sqlCommand.Parameters[0].Value = values[0]; + sqlCommand.Parameters[1].Value = values[1]; - using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) - { - while (sqlDataReader.Read()) + using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) { - // GetFieldValue - Assert.True(sqlDataReader.GetFieldValue(0) == (int)values[0], @"Value returned by GetFieldValue is unexpected."); - Assert.True(sqlDataReader.GetFieldValue(1) == (string)values[1], @"Value returned by GetFieldValue is unexpected."); - Assert.True(sqlDataReader.GetFieldValue(2) == (string)values[2], @"Value returned by GetFieldValue is unexpected."); + while (sqlDataReader.Read()) + { + // GetFieldValue + Assert.True(sqlDataReader.GetFieldValue(0) == (int)values[0], @"Value returned by GetFieldValue is unexpected."); + Assert.True(sqlDataReader.GetFieldValue(1) == (string)values[1], @"Value returned by GetFieldValue is unexpected."); + Assert.True(sqlDataReader.GetFieldValue(2) == (string)values[2], @"Value returned by GetFieldValue is unexpected."); + } } } - } - // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. - using (SqlCommand sqlCommand = - new SqlCommand($"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", - sqlConnection, transaction: null, columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) - { - sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); - sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); + // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. + using (SqlCommand sqlCommand = + new SqlCommand($"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", + sqlConnection, transaction: null, columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) + { + sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); + sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); - sqlCommand.Parameters[0].Value = values[0]; - sqlCommand.Parameters[1].Value = values[1]; + sqlCommand.Parameters[0].Value = values[0]; + sqlCommand.Parameters[1].Value = values[1]; - using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) - { - while (sqlDataReader.Read()) + using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) { - // GetSqlValue - Assert.True(((System.Data.SqlTypes.SqlInt32)sqlDataReader.GetSqlValue(0)).Value == (int)values[0], - @"Value returned by GetSqlValue is unexpected."); - Assert.True(((System.Data.SqlTypes.SqlString)sqlDataReader.GetSqlValue(1)).Value == (string)values[1], - @"Value returned by GetSqlValue is unexpected."); - Assert.True(((System.Data.SqlTypes.SqlString)sqlDataReader.GetSqlValue(2)).Value == (string)values[2], - @"Value returned by GetSqlValue is unexpected."); + while (sqlDataReader.Read()) + { + // GetSqlValue + Assert.True(((System.Data.SqlTypes.SqlInt32)sqlDataReader.GetSqlValue(0)).Value == (int)values[0], + @"Value returned by GetSqlValue is unexpected."); + Assert.True(((System.Data.SqlTypes.SqlString)sqlDataReader.GetSqlValue(1)).Value == (string)values[1], + @"Value returned by GetSqlValue is unexpected."); + Assert.True(((System.Data.SqlTypes.SqlString)sqlDataReader.GetSqlValue(2)).Value == (string)values[2], + @"Value returned by GetSqlValue is unexpected."); + } } } - } - - // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. - using (SqlCommand sqlCommand = new SqlCommand($@"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", - sqlConnection, - transaction: null, - columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) - { - sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); - sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); - - sqlCommand.Parameters[0].Value = values[0]; - sqlCommand.Parameters[1].Value = values[1]; - using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) + // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. + using (SqlCommand sqlCommand = new SqlCommand($@"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", + sqlConnection, + transaction: null, + columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) { - while (sqlDataReader.Read()) - { - object[] readValues = new object[values.Count]; - int numberofValuesRead = sqlDataReader.GetValues(readValues); + sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); + sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); - Assert.True(numberofValuesRead == values.Count, "the number of values returned by GetValues is unexpected."); + sqlCommand.Parameters[0].Value = values[0]; + sqlCommand.Parameters[1].Value = values[1]; - for (int i = 0; i < numberofValuesRead; i++) + using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) + { + while (sqlDataReader.Read()) { + object[] readValues = new object[values.Count]; + int numberofValuesRead = sqlDataReader.GetValues(readValues); - if (i != 3) - { - Assert.True(readValues[i].ToString() == values[i].ToString(), @"the values returned by GetValues is unexpected."); - } - else + Assert.True(numberofValuesRead == values.Count, "the number of values returned by GetValues is unexpected."); + + for (int i = 0; i < numberofValuesRead; i++) { - Assert.True(((byte[])values[i]).SequenceEqual((byte[])readValues[i]), - @"Value returned by GetValues is unexpected."); + + if (i != 3) + { + Assert.True(readValues[i].ToString() == values[i].ToString(), @"the values returned by GetValues is unexpected."); + } + else + { + Assert.True(((byte[])values[i]).SequenceEqual((byte[])readValues[i]), + @"Value returned by GetValues is unexpected."); + } } } } } - } - // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. - using (SqlCommand sqlCommand = - new SqlCommand( - $"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", - sqlConnection, - transaction: null, - columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) - { - sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); - sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); + // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. + using (SqlCommand sqlCommand = + new SqlCommand( + $"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", + sqlConnection, + transaction: null, + columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) + { + sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); + sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); - sqlCommand.Parameters[0].Value = values[0]; - sqlCommand.Parameters[1].Value = values[1]; + sqlCommand.Parameters[0].Value = values[0]; + sqlCommand.Parameters[1].Value = values[1]; - using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) - { - while (sqlDataReader.Read()) + using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) { - // GetSqlValues - object[] readValues = new object[values.Count]; - int numberofValuesRead = sqlDataReader.GetSqlValues(readValues); - - Assert.True(numberofValuesRead == values.Count, "the number of values returned by GetSqlValues is unexpected."); - Assert.True(((System.Data.SqlTypes.SqlInt32)readValues[0]).Value == (int)values[0], - @"Value returned by GetSqlValues is unexpected."); - Assert.True(((System.Data.SqlTypes.SqlString)readValues[1]).Value == (string)values[1], - @"Value returned by GetSqlValues is unexpected."); - Assert.True(((System.Data.SqlTypes.SqlString)readValues[2]).Value == (string)values[2], - @"Value returned by GetSqlValues is unexpected."); + while (sqlDataReader.Read()) + { + // GetSqlValues + object[] readValues = new object[values.Count]; + int numberofValuesRead = sqlDataReader.GetSqlValues(readValues); + + Assert.True(numberofValuesRead == values.Count, "the number of values returned by GetSqlValues is unexpected."); + Assert.True(((System.Data.SqlTypes.SqlInt32)readValues[0]).Value == (int)values[0], + @"Value returned by GetSqlValues is unexpected."); + Assert.True(((System.Data.SqlTypes.SqlString)readValues[1]).Value == (string)values[1], + @"Value returned by GetSqlValues is unexpected."); + Assert.True(((System.Data.SqlTypes.SqlString)readValues[2]).Value == (string)values[2], + @"Value returned by GetSqlValues is unexpected."); + } } } - } - // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. - using (SqlCommand sqlCommand = new SqlCommand($@"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", - sqlConnection, - transaction: null, - columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) - { - sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); - sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); + // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. + using (SqlCommand sqlCommand = new SqlCommand($@"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", + sqlConnection, + transaction: null, + columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) + { + sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); + sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); - sqlCommand.Parameters[0].Value = values[0]; - sqlCommand.Parameters[1].Value = values[1]; + sqlCommand.Parameters[0].Value = values[0]; + sqlCommand.Parameters[1].Value = values[1]; - using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) - { - while (sqlDataReader.Read()) + using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) { - // GetSqlValues - object[] readValues = new object[values.Count]; - int numberofValuesRead = sqlDataReader.GetSqlValues(readValues); - - Assert.True(numberofValuesRead == values.Count, "the number of values returned by GetSqlValues is unexpected."); - Assert.True(((System.Data.SqlTypes.SqlInt32)readValues[0]).Value == (int)values[0], - @"Value returned by GetSqlValues is unexpected."); - Assert.True(((System.Data.SqlTypes.SqlString)readValues[1]).Value == (string)values[1], - @"Value returned by GetSqlValues is unexpected."); - Assert.True(((System.Data.SqlTypes.SqlString)readValues[2]).Value == (string)values[2], - @"Value returned by GetSqlValues is unexpected."); + while (sqlDataReader.Read()) + { + // GetSqlValues + object[] readValues = new object[values.Count]; + int numberofValuesRead = sqlDataReader.GetSqlValues(readValues); + + Assert.True(numberofValuesRead == values.Count, "the number of values returned by GetSqlValues is unexpected."); + Assert.True(((System.Data.SqlTypes.SqlInt32)readValues[0]).Value == (int)values[0], + @"Value returned by GetSqlValues is unexpected."); + Assert.True(((System.Data.SqlTypes.SqlString)readValues[1]).Value == (string)values[1], + @"Value returned by GetSqlValues is unexpected."); + Assert.True(((System.Data.SqlTypes.SqlString)readValues[2]).Value == (string)values[2], + @"Value returned by GetSqlValues is unexpected."); + } } } - } - // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. - using (SqlCommand sqlCommand = - new SqlCommand($"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", - sqlConnection, transaction: null, - columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) - { - sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); - sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); + // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. + using (SqlCommand sqlCommand = + new SqlCommand($"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", + sqlConnection, transaction: null, + columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) + { + sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); + sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); - sqlCommand.Parameters[0].Value = values[0]; - sqlCommand.Parameters[1].Value = values[1]; + sqlCommand.Parameters[0].Value = values[0]; + sqlCommand.Parameters[1].Value = values[1]; - using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) - { - while (sqlDataReader.Read()) + using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) { - // IsDBNullAsync - Task isDbNullTask = sqlDataReader.IsDBNullAsync(0); + while (sqlDataReader.Read()) + { + // IsDBNullAsync + Task isDbNullTask = sqlDataReader.IsDBNullAsync(0); - Assert.True(isDbNullTask.Result == false, @"IsDBNullAsync unexpectedly returned false."); + Assert.True(isDbNullTask.Result == false, @"IsDBNullAsync unexpectedly returned false."); - isDbNullTask = sqlDataReader.IsDBNullAsync(1); - Assert.True(isDbNullTask.Result == false, @"IsDBNullAsync unexpectedly returned false."); + isDbNullTask = sqlDataReader.IsDBNullAsync(1); + Assert.True(isDbNullTask.Result == false, @"IsDBNullAsync unexpectedly returned false."); - isDbNullTask = sqlDataReader.IsDBNullAsync(2); - Assert.True(isDbNullTask.Result == false, @"IsDBNullAsync unexpectedly returned false."); + isDbNullTask = sqlDataReader.IsDBNullAsync(2); + Assert.True(isDbNullTask.Result == false, @"IsDBNullAsync unexpectedly returned false."); + } } } - } - // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. - using (SqlCommand sqlCommand = - new SqlCommand($"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", - sqlConnection, transaction: null, - columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) - { - sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); - sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); + // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. + using (SqlCommand sqlCommand = + new SqlCommand($"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", + sqlConnection, transaction: null, + columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) + { + sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); + sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); - sqlCommand.Parameters[0].Value = values[0]; - sqlCommand.Parameters[1].Value = values[1]; + sqlCommand.Parameters[0].Value = values[0]; + sqlCommand.Parameters[1].Value = values[1]; - using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) - { - while (sqlDataReader.Read()) + using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) { - // GetFieldValueAsync - Task getCustomerIdTask = sqlDataReader.GetFieldValueAsync(0); - Assert.True(getCustomerIdTask.Result == (int)values[0], @"Value returned by GetFieldValueAsync is unexpected."); + while (sqlDataReader.Read()) + { + // GetFieldValueAsync + Task getCustomerIdTask = sqlDataReader.GetFieldValueAsync(0); + Assert.True(getCustomerIdTask.Result == (int)values[0], @"Value returned by GetFieldValueAsync is unexpected."); - Task getFirstNameTask = sqlDataReader.GetFieldValueAsync(1); - Assert.True(getFirstNameTask.Result == (string)values[1], @"Value returned by GetFieldValueAsync is unexpected."); + Task getFirstNameTask = sqlDataReader.GetFieldValueAsync(1); + Assert.True(getFirstNameTask.Result == (string)values[1], @"Value returned by GetFieldValueAsync is unexpected."); - Task getLastNameTask = sqlDataReader.GetFieldValueAsync(2); - Assert.True(getLastNameTask.Result == (string)values[2], @"Value returned by GetFieldValueAsync is unexpected."); + Task getLastNameTask = sqlDataReader.GetFieldValueAsync(2); + Assert.True(getLastNameTask.Result == (string)values[2], @"Value returned by GetFieldValueAsync is unexpected."); + } } } - } - // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. - using (SqlCommand sqlCommand = - new SqlCommand($"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", - sqlConnection, transaction: null, - columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) - { - sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); - sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); + // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. + using (SqlCommand sqlCommand = + new SqlCommand($"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", + sqlConnection, transaction: null, + columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) + { + sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); + sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); - sqlCommand.Parameters[0].Value = values[0]; - sqlCommand.Parameters[1].Value = values[1]; + sqlCommand.Parameters[0].Value = values[0]; + sqlCommand.Parameters[1].Value = values[1]; - using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) - { - while (sqlDataReader.Read()) + using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) { - // IsDBNull - Assert.True(sqlDataReader.IsDBNull(0) == false, @"IsDBNull unexpectedly returned false."); - Assert.True(sqlDataReader.IsDBNull(1) == false, @"IsDBNull unexpectedly returned false."); - Assert.True(sqlDataReader.IsDBNull(2) == false, @"IsDBNull unexpectedly returned false."); + while (sqlDataReader.Read()) + { + // IsDBNull + Assert.True(sqlDataReader.IsDBNull(0) == false, @"IsDBNull unexpectedly returned false."); + Assert.True(sqlDataReader.IsDBNull(1) == false, @"IsDBNull unexpectedly returned false."); + Assert.True(sqlDataReader.IsDBNull(2) == false, @"IsDBNull unexpectedly returned false."); + } } } - } - // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. - using (SqlCommand sqlCommand = - new SqlCommand($"SELECT * FROM [{tableName}] WHERE LastName = @LastName AND CustomerId = @CustomerId", - sqlConnection, transaction: null, - columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) - { - sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int).Value = values[0]; - sqlCommand.Parameters.Add(@"LastName", SqlDbType.NVarChar).Value = values[2]; - - Task readAsyncTask = ReadAsync(sqlCommand, values, commandBehavior); - readAsyncTask.Wait(); - } + // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. + using (SqlCommand sqlCommand = + new SqlCommand($"SELECT * FROM [{tableName}] WHERE LastName = @LastName AND CustomerId = @CustomerId", + sqlConnection, transaction: null, + columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) + { + sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int).Value = values[0]; + sqlCommand.Parameters.Add(@"LastName", SqlDbType.NVarChar).Value = values[2]; - // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. - using (SqlCommand sqlCommand = - new SqlCommand($"UPDATE [{tableName}] SET FirstName = @FirstName WHERE CustomerId = @CustomerId", - sqlConnection, - transaction: null, - columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) - { - sqlCommand.Parameters.AddWithValue(@"FirstName", values[1]); - sqlCommand.Parameters.AddWithValue(@"CustomerId", values[0]); + Task readAsyncTask = ReadAsync(sqlCommand, values, commandBehavior); + readAsyncTask.Wait(); + } - using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) + // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. + using (SqlCommand sqlCommand = + new SqlCommand($"UPDATE [{tableName}] SET FirstName = @FirstName WHERE CustomerId = @CustomerId", + sqlConnection, + transaction: null, + columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) { - Assert.True(sqlDataReader.RecordsAffected == numberOfRows, @"number of rows returned by sqlDataReader.RecordsAffected is incorrect."); - } - } + sqlCommand.Parameters.AddWithValue(@"FirstName", values[1]); + sqlCommand.Parameters.AddWithValue(@"CustomerId", values[0]); - // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. - using (SqlCommand sqlCommand = new SqlCommand($"INSERT INTO [{tableName}] VALUES (@CustomerId, @FirstName, @LastName )", - sqlConnection, transaction: null, - columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) - { - sqlCommand.Parameters.AddWithValue(@"CustomerId", 60); - SqlParameter firstNameParameter = new SqlParameter(@"FirstName", System.Data.SqlDbType.NVarChar, 50); + using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) + { + Assert.True(sqlDataReader.RecordsAffected == numberOfRows, @"number of rows returned by sqlDataReader.RecordsAffected is incorrect."); + } + } - firstNameParameter.Direction = System.Data.ParameterDirection.Input; - firstNameParameter.Value = DBNull.Value; + // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. + using (SqlCommand sqlCommand = new SqlCommand($"INSERT INTO [{tableName}] VALUES (@CustomerId, @FirstName, @LastName )", + sqlConnection, transaction: null, + columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) + { + sqlCommand.Parameters.AddWithValue(@"CustomerId", 60); + SqlParameter firstNameParameter = new SqlParameter(@"FirstName", System.Data.SqlDbType.NVarChar, 50); - sqlCommand.Parameters.Add(firstNameParameter); - sqlCommand.Parameters.AddWithValue(@"LastName", @"Corporation60"); - sqlCommand.ExecuteNonQuery(); - } + firstNameParameter.Direction = System.Data.ParameterDirection.Input; + firstNameParameter.Value = DBNull.Value; - // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. - using (SqlCommand sqlCommand = - new SqlCommand($"SELECT * FROM [{tableName}] WHERE LastName = @LastName AND CustomerId = @CustomerId", - sqlConnection, - transaction: null, - columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) - { - sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int).Value = 60; - sqlCommand.Parameters.Add(@"LastName", SqlDbType.NVarChar).Value = @"Corporation60"; + sqlCommand.Parameters.Add(firstNameParameter); + sqlCommand.Parameters.AddWithValue(@"LastName", @"Corporation60"); + sqlCommand.ExecuteNonQuery(); + } - using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) + // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. + using (SqlCommand sqlCommand = + new SqlCommand($"SELECT * FROM [{tableName}] WHERE LastName = @LastName AND CustomerId = @CustomerId", + sqlConnection, + transaction: null, + columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) { - while (sqlDataReader.Read()) + sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int).Value = 60; + sqlCommand.Parameters.Add(@"LastName", SqlDbType.NVarChar).Value = @"Corporation60"; + + using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) { - // IsDBNull - Assert.True(sqlDataReader.IsDBNull(0) == false, @"IsDBNull unexpectedly returned false."); - Assert.True(sqlDataReader.IsDBNull(1) == true, @"IsDBNull unexpectedly returned true."); - Assert.True(sqlDataReader.IsDBNull(2) == false, @"IsDBNull unexpectedly returned false."); + while (sqlDataReader.Read()) + { + // IsDBNull + Assert.True(sqlDataReader.IsDBNull(0) == false, @"IsDBNull unexpectedly returned false."); + Assert.True(sqlDataReader.IsDBNull(1) == true, @"IsDBNull unexpectedly returned true."); + Assert.True(sqlDataReader.IsDBNull(2) == false, @"IsDBNull unexpectedly returned false."); + } } } - } - - // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. - using (SqlCommand sqlCommand = - new SqlCommand($"SELECT * FROM [{tableName}] WHERE LastName = @LastName AND CustomerId = @CustomerId", - sqlConnection, - transaction: null, - columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) - { - sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int).Value = 60; - sqlCommand.Parameters.Add(@"LastName", SqlDbType.NVarChar).Value = @"Corporation60"; - using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) + // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. + using (SqlCommand sqlCommand = + new SqlCommand($"SELECT * FROM [{tableName}] WHERE LastName = @LastName AND CustomerId = @CustomerId", + sqlConnection, + transaction: null, + columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) { - while (sqlDataReader.Read()) + sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int).Value = 60; + sqlCommand.Parameters.Add(@"LastName", SqlDbType.NVarChar).Value = @"Corporation60"; + + using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) { - // IsDBNullAsync - Task isDbNullTask = sqlDataReader.IsDBNullAsync(0); + while (sqlDataReader.Read()) + { + // IsDBNullAsync + Task isDbNullTask = sqlDataReader.IsDBNullAsync(0); - Assert.True(isDbNullTask.Result == false, @"IsDBNullAsync unexpectedly returned false."); + Assert.True(isDbNullTask.Result == false, @"IsDBNullAsync unexpectedly returned false."); - isDbNullTask = sqlDataReader.IsDBNullAsync(1); - Assert.True(isDbNullTask.Result == true, @"IsDBNullAsync unexpectedly returned true."); + isDbNullTask = sqlDataReader.IsDBNullAsync(1); + Assert.True(isDbNullTask.Result == true, @"IsDBNullAsync unexpectedly returned true."); - isDbNullTask = sqlDataReader.IsDBNullAsync(2); - Assert.True(isDbNullTask.Result == false, @"IsDBNullAsync unexpectedly returned false."); + isDbNullTask = sqlDataReader.IsDBNullAsync(2); + Assert.True(isDbNullTask.Result == false, @"IsDBNullAsync unexpectedly returned false."); + } } } } @@ -1692,37 +1710,39 @@ public void TestSqlDataReaderAPIsWithSequentialAccess(object value) } [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [InlineData(CommandBehavior.Default)] - [InlineData(CommandBehavior.SequentialAccess)] - public void TestSqlCommandSequentialAccessCodePaths(object value) + [ClassData(typeof(ConnectionStringProviderWithCommandBehavior))] + public void TestSqlCommandSequentialAccessCodePaths(string connection, CommandBehavior value) { - CommandBehavior commandBehavior = (CommandBehavior)value; - IList values = GetValues(dataHint: 57); + if (value == CommandBehavior.Default || value == CommandBehavior.SequentialAccess) + { + CommandBehavior commandBehavior = (CommandBehavior)value; + IList values = GetValues(dataHint: 57); - Assert.True(values != null && values.Count >= 3, @"values should not be null and count should be >= 3."); + Assert.True(values != null && values.Count >= 3, @"values should not be null and count should be >= 3."); - int numberOfRows = 300; + int numberOfRows = 300; - //Insert a bunch of rows in to the table. - int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values); + //Insert a bunch of rows in to the table. + int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection); - Assert.True(rowsAffected == numberOfRows, "number of rows affected is unexpected."); + Assert.True(rowsAffected == numberOfRows, "number of rows affected is unexpected."); - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringHGSVBS)) - { - sqlConnection.Open(); - // Test SqlDataReader.GetStream() on encrypted column, throw an exception. - using (SqlCommand sqlCommand = new SqlCommand($"SELECT * FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", - sqlConnection, - transaction: null, - columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) + using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringHGSVBS)) { - sqlCommand.Parameters.AddWithValue(@"CustomerId", values[0]); - sqlCommand.Parameters.AddWithValue(@"FirstName", values[1]); - - using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader()) + sqlConnection.Open(); + // Test SqlDataReader.GetStream() on encrypted column, throw an exception. + using (SqlCommand sqlCommand = new SqlCommand($"SELECT * FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", + sqlConnection, + transaction: null, + columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) { - Assert.True(sqlDataReader.VisibleFieldCount == values.Count, @"sqlDataReader.VisibleFieldCount returned unexpected result."); + sqlCommand.Parameters.AddWithValue(@"CustomerId", values[0]); + sqlCommand.Parameters.AddWithValue(@"FirstName", values[1]); + + using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader()) + { + Assert.True(sqlDataReader.VisibleFieldCount == values.Count, @"sqlDataReader.VisibleFieldCount returned unexpected result."); + } } } } @@ -1848,11 +1868,11 @@ private DataTable CreateDataTable(string tableName, int numberofRows) /// /// /// - private int InsertRows(string tableName, int numberofRows, IList values) + private int InsertRows(string tableName, int numberofRows, IList values, string connection) { int rowsAffected = 0; - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringHGSVBS)) + using (SqlConnection sqlConnection = new SqlConnection(connection)) { sqlConnection.Open(); @@ -1876,9 +1896,9 @@ private int InsertRows(string tableName, int numberofRows, IList values) /// Drops the specified procedures. /// /// - private void DropHelperProcedures(string[] procNames) + private void DropHelperProcedures(string[] procNames, string connection) { - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringHGSVBS)) + using (SqlConnection sqlConnection = new SqlConnection(connection)) { sqlConnection.Open(); foreach (string name in procNames) @@ -2010,11 +2030,14 @@ private void TestDataAdapterFillResults(DataTable dataTable, IList value public void Dispose() { - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringHGSVBS)) + foreach (var connection in DataTestUtility.connStrings.Values) { - sqlConnection.Open(); + using (SqlConnection sqlConnection = new SqlConnection(connection)) + { + sqlConnection.Open(); - Table.DeleteData(fixture.ApiTestTable.Name, sqlConnection); + Table.DeleteData(fixture.ApiTestTable.Name, sqlConnection); + } } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/BulkCopyAE.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/BulkCopyAE.cs index 8260d145e6..2eecca3762 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/BulkCopyAE.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/BulkCopyAE.cs @@ -26,8 +26,9 @@ public BulkCopyAE(SQLSetupStrategyCertStoreProvider fixture) tableName = fixture.BulkCopyAETestTable.Name; } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - public void TestBulkCopyString() + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ClassData(typeof(ConnectionStringProvider))] + public void TestBulkCopyString(string connectionString) { var dataTable = new DataTable(); dataTable.Columns.Add("c1", typeof(string)); @@ -38,7 +39,7 @@ public void TestBulkCopyString() dataTable.Rows.Add(dataRow); dataTable.AcceptChanges(); - var encryptionEnabledConnectionString = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) + var encryptionEnabledConnectionString = new SqlConnectionStringBuilder(connectionString) { ColumnEncryptionSetting = SqlConnectionColumnEncryptionSetting.Enabled }.ConnectionString; @@ -64,10 +65,13 @@ public void TestBulkCopyString() public void Dispose() { - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionString)) + foreach (var connection in DataTestUtility.connStrings.Values) { - sqlConnection.Open(); - Table.DeleteData(fixture.BulkCopyAETestTable.Name, sqlConnection); + using (SqlConnection sqlConnection = new SqlConnection(connection)) + { + sqlConnection.Open(); + Table.DeleteData(fixture.BulkCopyAETestTable.Name, sqlConnection); + } } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ColumnMasterKey.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ColumnMasterKey.cs index 42b98c7003..44f9ec2859 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ColumnMasterKey.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ColumnMasterKey.cs @@ -28,12 +28,17 @@ public override void Create(SqlConnection sqlConnection) var connStrings = sqlConnection.ConnectionString; if (connStrings.Contains("HGS") || connStrings.Contains("AAS")) { + + SqlColumnEncryptionCertificateStoreProvider sqlColumnCertStoreProvider = new SqlColumnEncryptionCertificateStoreProvider(); + byte[] cmkSign = sqlColumnCertStoreProvider.SignColumnMasterKeyMetadata(KeyPath, true); + string cmkSignStr = string.Concat("0x", BitConverter.ToString(cmkSign).Replace("-", string.Empty)); + sql = - $@"CREATE COLUMN MASTER KEY [{Name}] - WITH ( + $@"CREATE COLUMN MASTER KEY [{Name}] + WITH ( KEY_STORE_PROVIDER_NAME = N'{KeyStoreProviderName}', KEY_PATH = N'{KeyPath}', - ENCLAVE_COMPUTATIONS (SIGNATURE ={DataTestUtility.CertificateSignature}) + ENCLAVE_COMPUTATIONS (SIGNATURE = {cmkSignStr}) );"; } else diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestTrustedMasterKeyPaths.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestTrustedMasterKeyPaths.cs index 967d2d521c..c433c9e1b2 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestTrustedMasterKeyPaths.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestTrustedMasterKeyPaths.cs @@ -68,7 +68,7 @@ public void TestTrustedColumnEncryptionMasterKeyPathsWithNullDictionary() using (SqlConnection sqlConnection = new SqlConnection(string.Concat(defaultConnectionString, @";Column Encryption Setting = Enabled;"))) { sqlConnection.Open(); - + // Test INPUT parameter on an encrypted parameter using (SqlCommand sqlCommand = new SqlCommand( $@"SELECT CustomerId, FirstName, LastName @@ -94,7 +94,7 @@ FROM [{tableName}] public void TestTrustedColumnEncryptionMasterKeyPathsWithOneServer() { SqlConnectionStringBuilder connBuilder = new SqlConnectionStringBuilder(defaultConnectionString); - + // 2.. Test with valid key path // // Clear existing dictionary. @@ -111,7 +111,7 @@ public void TestTrustedColumnEncryptionMasterKeyPathsWithOneServer() using (SqlConnection sqlConnection = new SqlConnection(string.Concat(defaultConnectionString, @";Column Encryption Setting = Enabled;"))) { sqlConnection.Open(); - + // Test INPUT parameter on an encrypted parameter using (SqlCommand sqlCommand = new SqlCommand( $@"SELECT CustomerId, FirstName, LastName @@ -137,7 +137,7 @@ FROM [{tableName}] public void TestTrustedColumnEncryptionMasterKeyPathsWithMultipleServers() { SqlConnectionStringBuilder connBuilder = new SqlConnectionStringBuilder(defaultConnectionString); - + // 3. Test with multiple servers with multiple key paths // // Clear existing dictionary. @@ -171,7 +171,7 @@ public void TestTrustedColumnEncryptionMasterKeyPathsWithMultipleServers() using (SqlConnection sqlConnection = new SqlConnection(string.Concat(defaultConnectionString, @";Column Encryption Setting = Enabled;"))) { sqlConnection.Open(); - + // Test INPUT parameter on an encrypted parameter using (SqlCommand sqlCommand = new SqlCommand( $@"SELECT CustomerId, FirstName, LastName diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs index b41aa03f26..e6b7c833a8 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs @@ -459,4 +459,43 @@ public IEnumerator GetEnumerator() } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } + public class ConnectionStringProviderWithCommandBehavior : IEnumerable + { + public IEnumerator GetEnumerator() + { + yield return new object[] { DataTestUtility.TCPConnectionString, CommandBehavior.SingleResult }; + yield return new object[] { DataTestUtility.TCPConnectionString, CommandBehavior.SingleRow }; + yield return new object[] { DataTestUtility.TCPConnectionString, CommandBehavior.CloseConnection }; + yield return new object[] { DataTestUtility.TCPConnectionString, CommandBehavior.SequentialAccess }; + yield return new object[] { DataTestUtility.TCPConnectionStringHGSVBS, CommandBehavior.SingleResult }; + yield return new object[] { DataTestUtility.TCPConnectionStringHGSVBS, CommandBehavior.SingleRow }; + yield return new object[] { DataTestUtility.TCPConnectionStringHGSVBS, CommandBehavior.CloseConnection }; + yield return new object[] { DataTestUtility.TCPConnectionStringHGSVBS, CommandBehavior.SequentialAccess }; + } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + + public class ConnectionStringProviderWithSchemaType : IEnumerable + { + public IEnumerator GetEnumerator() + { + yield return new object[] { DataTestUtility.TCPConnectionString, SchemaType.Source }; + yield return new object[] { DataTestUtility.TCPConnectionString, SchemaType.Mapped }; + yield return new object[] { DataTestUtility.TCPConnectionStringHGSVBS, SchemaType.Source }; + yield return new object[] { DataTestUtility.TCPConnectionStringHGSVBS, SchemaType.Mapped }; + } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + + public class ConnectionStringProviderWithIntegers : IEnumerable + { + public IEnumerator GetEnumerator() + { + yield return new object[] { DataTestUtility.TCPConnectionString, 1 }; + yield return new object[] { DataTestUtility.TCPConnectionString, 100}; + yield return new object[] { DataTestUtility.TCPConnectionStringHGSVBS, 1 }; + yield return new object[] { DataTestUtility.TCPConnectionStringHGSVBS, 100 }; + } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } } From c88ea66bfc3b1b9c2c628e5a4b4167790244351c Mon Sep 17 00:00:00 2001 From: Javad Rahnama Date: Tue, 29 Oct 2019 10:08:13 -0700 Subject: [PATCH 40/63] code clean up --- .../AlwaysEncrypted/TestFixtures/Setup/ColumnMasterKey.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ColumnMasterKey.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ColumnMasterKey.cs index 44f9ec2859..232fa8dc62 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ColumnMasterKey.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ColumnMasterKey.cs @@ -19,9 +19,6 @@ protected ColumnMasterKey(string name) : base(name) protected string KeyStoreProviderName { get; set; } public abstract string KeyPath { get; } - //public const byte[] Signature =Configuration() - - public override void Create(SqlConnection sqlConnection) { string sql; From e7f2fb6254e9aedca578c5f3db1277978251a8b2 Mon Sep 17 00:00:00 2001 From: Karina Zhou Date: Wed, 30 Oct 2019 13:11:30 -0700 Subject: [PATCH 41/63] Improve the enclave support in tests (ApiShould / BulkCopyAE / CspProviderExt / End2EndSmokeTests) --- .../ManualTests/AlwaysEncrypted/ApiShould.cs | 901 +++++++++--------- .../ManualTests/AlwaysEncrypted/BulkCopyAE.cs | 8 +- .../AlwaysEncrypted/ConversionTests.cs | 2 +- .../AlwaysEncrypted/CspProviderExt.cs | 19 +- .../AlwaysEncrypted/End2EndSmokeTests.cs | 313 +++--- .../TestFixtures/DatabaseHelper.cs | 83 ++ .../TestFixtures/SQLSetupStrategy.cs | 13 +- .../SQLSetupStrategyCertStoreProvider.cs | 2 +- .../TestFixtures/SQLSetupStrategyCspExt.cs | 4 +- .../TestFixtures/Setup/ApiTestTable.cs | 2 +- .../TestFixtures/Setup/BulkCopyAETestTable.cs | 3 +- .../TestFixtures/Setup/ColumnMasterKey.cs | 9 +- .../TestFixtures/Setup/CspColumnMasterKey.cs | 10 +- .../Setup/CspProviderColumnMasterKey.cs | 31 + .../ManualTests/DataCommon/DataTestUtility.cs | 112 +-- ....Data.SqlClient.ManualTesting.Tests.csproj | 1 + .../tests/ManualTests/config.json | 4 +- 17 files changed, 864 insertions(+), 653 deletions(-) create mode 100644 src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CspProviderColumnMasterKey.cs diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs index 6b2b5a1320..366aceb9a8 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs @@ -31,10 +31,12 @@ public ApiShould(SQLSetupStrategyCertStoreProvider fixture) tableName = fixture.ApiTestTable.Name; } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [ClassData(typeof(ConnectionStringProviderWithBooleanVariable))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ClassData(typeof(AEConnectionStringProviderWithBooleanVariable))] public void TestSqlTransactionCommitRollbackWithTransparentInsert(string connection, bool isPermitted) { + CleanUpTable(connection, tableName); + using (SqlConnection sqlConnection = new SqlConnection(connection)) { sqlConnection.Open(); @@ -68,10 +70,12 @@ public void TestSqlTransactionCommitRollbackWithTransparentInsert(string connect } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [ClassData(typeof(ConnectionStringProvider))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ClassData(typeof(AEConnectionStringProvider))] public void TestSqlTransactionRollbackToSavePoint(string connection) { + CleanUpTable(connection, tableName); + using (SqlConnection sqlConnection = new SqlConnection(connection)) { sqlConnection.Open(); @@ -112,8 +116,8 @@ public void TestSqlTransactionRollbackToSavePoint(string connection) } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [ClassData(typeof(ConnectionStringProvider))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ClassData(typeof(AEConnectionStringProvider))] public void SqlParameterProperties(string connection) { string tableName = fixture.SqlParameterPropertiesTable.Name; @@ -127,6 +131,8 @@ public void SqlParameterProperties(string connection) const int decimalColumnScale = 4; const int timeColumnScale = 5; + CleanUpTable(connection, tableName); + using (SqlConnection sqlConnection = new SqlConnection(connection)) { try @@ -383,10 +389,12 @@ private void InsertCustomerRecord(SqlConnection sqlConnection, SqlTransaction sq } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [ClassData(typeof(ConnectionStringProvider))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ClassData(typeof(AEConnectionStringProvider))] public void TestSqlDataAdapterFillDataTable(string connection) { + CleanUpTable(connection, tableName); + const string DummyParamName = "@dummyParam"; int numberOfRows = 100; @@ -448,10 +456,12 @@ public void TestSqlDataAdapterFillDataTable(string connection) } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [ClassData(typeof(ConnectionStringProviderWithSchemaType))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ClassData(typeof(AEConnectionStringProviderWithSchemaType))] public void TestSqlDataAdapterFillSchema(string connection, SchemaType schemaType) { + CleanUpTable(connection, tableName); + IList values = GetValues(dataHint: 44); int numberOfRows = 42; @@ -493,10 +503,12 @@ private void ValidateSchema(DataColumnCollection dataColumns) Assert.Equal(typeof(string), dataColumns[2].DataType); } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [ClassData(typeof(ConnectionStringProviderWithBooleanVariable))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ClassData(typeof(AEConnectionStringProviderWithBooleanVariable))] public void TestExecuteNonQuery(string connection, bool isAsync) { + CleanUpTable(connection, tableName); + Parallel.For(0, 10, i => { IList values = GetValues(dataHint: 45 + i + 1); @@ -518,10 +530,10 @@ public void TestExecuteNonQuery(string connection, bool isAsync) transaction: null, columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) { - if (isAsync) + if (DataTestUtility.EnclaveEnabled) { - //Increase Time out in Async mode. - sqlCommand.CommandTimeout = 60; + //Increase Time out for enclave-enabled server. + sqlCommand.CommandTimeout = 90; } sqlCommand.Parameters.AddWithValue(@"FirstName", string.Format(@"Microsoft{0}", i + 100)); @@ -543,10 +555,12 @@ public void TestExecuteNonQuery(string connection, bool isAsync) }); } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [ClassData(typeof(ConnectionStringProviderWithBooleanVariable))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ClassData(typeof(AEConnectionStringProviderWithBooleanVariable))] public void TestExecuteScalar(string connection, bool isAsync) { + CleanUpTable(connection, tableName); + Parallel.For(0, 10, i => { IList values = GetValues(dataHint: 42); @@ -567,7 +581,12 @@ public void TestExecuteScalar(string connection, bool isAsync) transaction: null, columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) { - sqlCommand.CommandTimeout = 60; + if (DataTestUtility.EnclaveEnabled) + { + // Increase timeout for enclave-enabled server + sqlCommand.CommandTimeout = 60; + } + sqlCommand.Parameters.AddWithValue(@"CustomerId", values[0]); int customerId = -1; @@ -588,10 +607,11 @@ public void TestExecuteScalar(string connection, bool isAsync) }); } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [ClassData(typeof(ConnectionStringProviderWithIntegers))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ClassData(typeof(AEConnectionStringProviderWithIntegers))] public void TestSqlDataAdapterBatchUpdate(string connection,int numberofRows) { + CleanUpTable(connection, tableName); DataTable dataTable = CreateDataTable(tableName: tableName, numberofRows: numberofRows); @@ -632,10 +652,12 @@ public void TestSqlDataAdapterBatchUpdate(string connection,int numberofRows) } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [ClassData(typeof(ConnectionStringProvider))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ClassData(typeof(AEConnectionStringProvider))] public void TestExecuteReader(string connection) { + CleanUpTable(connection, tableName); + Parallel.For(0, 10, i => { IList values = GetValues(dataHint: 45 + i + 1); @@ -655,8 +677,11 @@ public void TestExecuteReader(string connection) transaction: null, columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) { - //Increas command time out to a minute for async calls. - sqlCommand.CommandTimeout = 60; + if (DataTestUtility.EnclaveEnabled) + { + //Increas command time out to a minute for enclave-enabled server. + sqlCommand.CommandTimeout = 60; + } sqlCommand.Parameters.AddWithValue(@"FirstName", string.Format(@"Microsoft{0}", i + 100)); sqlCommand.Parameters.AddWithValue(@"CustomerId", values[0]); @@ -681,8 +706,8 @@ public void TestExecuteReader(string connection) }); } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [ClassData(typeof(ConnectionStringProviderWithCommandBehavior))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ClassData(typeof(AEConnectionStringProviderWithCommandBehaviorSet1))] public void TestExecuteReaderWithCommandBehavior(string connection, CommandBehavior commandBehavior) { string[] columnNames = new string[3] { "CustomerId", "FirstName", "LastName" }; @@ -693,6 +718,8 @@ public void TestExecuteReaderWithCommandBehavior(string connection, CommandBehav string[] dataType = new string[3] { @"System.Int32", @"System.String", @"System.String" }; string[] columnSizes = new string[3] { @"4", @"50", @"50" }; + CleanUpTable(connection, tableName); + Parallel.For(0, 1, i => { IList values = GetValues(dataHint: 16 + i); @@ -804,10 +831,12 @@ public void TestExecuteReaderWithCommandBehavior(string connection, CommandBehav }); } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [ClassData(typeof(ConnectionStringProvider))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ClassData(typeof(AEConnectionStringProvider))] public void TestPrepareWithExecuteNonQuery(string connection) { + CleanUpTable(connection, tableName); + IList values = GetValues(dataHint: 52); Assert.True(values != null && values.Count >= 3, @"values should not be null and count should be >= 3."); @@ -851,10 +880,12 @@ public void TestPrepareWithExecuteNonQuery(string connection) } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [ClassData(typeof(ConnectionStringProvider))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ClassData(typeof(AEConnectionStringProvider))] public void TestAsyncWriteDelayWithExecuteNonQueryAsync(string connection) { + CleanUpTable(connection, tableName); + IList values = GetValues(dataHint: 53); Assert.True(values != null && values.Count >= 3, @"values should not be null and count should be >= 3."); int numberOfRows = 10; @@ -903,10 +934,12 @@ public void TestAsyncWriteDelayWithExecuteNonQueryAsync(string connection) } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [ClassData(typeof(ConnectionStringProvider))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ClassData(typeof(AEConnectionStringProvider))] public void TestAsyncWriteDelayWithExecuteReaderAsync(string connection) { + CleanUpTable(connection, tableName); + IList values = GetValues(dataHint: 53); Assert.True(values != null && values.Count >= 3, @"values should not be null and count should be >= 3."); @@ -968,10 +1001,12 @@ public void TestAsyncWriteDelayWithExecuteReaderAsync(string connection) } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [ClassData(typeof(ConnectionStringProvider))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ClassData(typeof(AEConnectionStringProvider))] public void TestPrepareWithExecuteNonQueryAsync(string connection) { + CleanUpTable(connection, tableName); + IList values = GetValues(dataHint: 53); Assert.True(values != null && values.Count >= 3, @"values should not be null and count should be >= 3."); @@ -1021,76 +1056,75 @@ public void TestPrepareWithExecuteNonQueryAsync(string connection) } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - //[InlineData(CommandBehavior.Default)] - //[InlineData(CommandBehavior.SequentialAccess)] - [ClassData(typeof(ConnectionStringProviderWithCommandBehavior))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ClassData(typeof(AEConnectionStringProviderWithCommandBehaviorSet2))] public void TestPrepareWithExecuteReaderAsync(string connection, CommandBehavior commandBehavior) { - if (commandBehavior == CommandBehavior.Default || commandBehavior == CommandBehavior.SequentialAccess) - { - IList values = GetValues(dataHint: 54); - Assert.True(values != null && values.Count <= 3, @"values should not be null and count should be >= 3."); - int numberOfRows = 10; + CleanUpTable(connection, tableName); - // Insert a bunch of rows in to the table. - int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection: connection); + IList values = GetValues(dataHint: 54); + Assert.True(values != null && values.Count <= 3, @"values should not be null and count should be >= 3."); + int numberOfRows = 10; - Assert.True(rowsAffected == numberOfRows, "number of rows affected is unexpected."); + // Insert a bunch of rows in to the table. + int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection: connection); - using (SqlConnection sqlConnection = new SqlConnection(connection)) - { - sqlConnection.Open(); + Assert.True(rowsAffected == numberOfRows, "number of rows affected is unexpected."); - using (SqlCommand sqlCommand = new SqlCommand($"SELECT * FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", - sqlConnection, - transaction: null, - columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) - { - sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); - sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); + using (SqlConnection sqlConnection = new SqlConnection(connection)) + { + sqlConnection.Open(); - sqlCommand.Parameters[0].Value = values[0]; - sqlCommand.Parameters[1].Value = values[1]; + using (SqlCommand sqlCommand = new SqlCommand($"SELECT * FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", + sqlConnection, + transaction: null, + columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) + { + sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); + sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); + + sqlCommand.Parameters[0].Value = values[0]; + sqlCommand.Parameters[1].Value = values[1]; - sqlCommand.Prepare(); + sqlCommand.Prepare(); - rowsAffected = 0; - IAsyncResult asyncResult = sqlCommand.BeginExecuteReader(commandBehavior); + rowsAffected = 0; + IAsyncResult asyncResult = sqlCommand.BeginExecuteReader(commandBehavior); - using (SqlDataReader sqlDataReader = sqlCommand.EndExecuteReader(asyncResult)) + using (SqlDataReader sqlDataReader = sqlCommand.EndExecuteReader(asyncResult)) + { + while (sqlDataReader.Read()) { - while (sqlDataReader.Read()) - { - rowsAffected++; + rowsAffected++; - Assert.True(values != null && values.Count >= 3, @"values should not be null and should be with atleast 3 elements."); - Assert.True(sqlDataReader.GetInt32(0) == (int)values[0], "CustomerId value read from the table was incorrect."); - Assert.True(sqlDataReader.GetString(1) == (string)values[1], "FirstName value read from the table was incorrect."); - Assert.True(sqlDataReader.GetString(2) == (string)values[2], "LastName value read from the table was incorrect."); - } + Assert.True(values != null && values.Count >= 3, @"values should not be null and should be with atleast 3 elements."); + Assert.True(sqlDataReader.GetInt32(0) == (int)values[0], "CustomerId value read from the table was incorrect."); + Assert.True(sqlDataReader.GetString(1) == (string)values[1], "FirstName value read from the table was incorrect."); + Assert.True(sqlDataReader.GetString(2) == (string)values[2], "LastName value read from the table was incorrect."); } - Assert.True(rowsAffected == 10, "Unexpected number of rows affected as returned by ExecuteNonQueryAsync."); + } + Assert.True(rowsAffected == 10, "Unexpected number of rows affected as returned by ExecuteNonQueryAsync."); - sqlCommand.Parameters[0].Value = (int)values[0] + 1; - sqlCommand.Parameters[1].Value = values[1]; + sqlCommand.Parameters[0].Value = (int)values[0] + 1; + sqlCommand.Parameters[1].Value = values[1]; - Task executeTask = VerifyExecuteNonQueryAsync(sqlCommand); + Task executeTask = VerifyExecuteNonQueryAsync(sqlCommand); - rowsAffected = -1; - rowsAffected = executeTask.Result; + rowsAffected = -1; + rowsAffected = executeTask.Result; - Assert.True(rowsAffected == -1, "Unexpected number of rows affected as returned by ExecuteNonQueryAsync."); - } + Assert.True(rowsAffected == -1, "Unexpected number of rows affected as returned by ExecuteNonQueryAsync."); } } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [ClassData(typeof(ConnectionStringProvider))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ClassData(typeof(AEConnectionStringProvider))] public void TestSqlDataReaderAPIs(string connection) { - var value = SqlCommandColumnEncryptionSetting.Enabled; + CleanUpTable(connection, tableName); + + SqlCommandColumnEncryptionSetting value = SqlCommandColumnEncryptionSetting.Enabled; char[] textValue = null; int numberOfRows = 100; string commandTextForEncryptionDisabledResultSetOnly = @"SELECT CustomerId, FirstName, LastName/*, BinaryColumn, NvarcharMaxColumn */ FROM [{0}]"; @@ -1281,471 +1315,477 @@ public void TestSqlDataReaderAPIs(string connection) } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [ClassData(typeof(ConnectionStringProviderWithCommandBehavior))] - public void TestSqlDataReaderAPIsWithSequentialAccess(string connection, CommandBehavior value) + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ClassData(typeof(AEConnectionStringProvider))] + public void TestSqlDataReaderAPIsWithSequentialAccess(string connection) { - if (value == CommandBehavior.SequentialAccess) - { - char[] textValue = null; - CommandBehavior commandBehavior = (CommandBehavior)value; + CleanUpTable(connection, tableName); - IList values = GetValues(dataHint: 56); + CommandBehavior value = CommandBehavior.SequentialAccess; + char[] textValue = null; + CommandBehavior commandBehavior = (CommandBehavior)value; - Assert.True(values != null && values.Count >= 3, @"values should not be null and count should be >= 3."); + IList values = GetValues(dataHint: 56); - int numberOfRows = 10; + Assert.True(values != null && values.Count >= 3, @"values should not be null and count should be >= 3."); - // Insert a bunch of rows in to the table. - int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection); + int numberOfRows = 10; - Assert.Equal(rowsAffected, numberOfRows); + // Insert a bunch of rows in to the table. + int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection); - using (SqlConnection sqlConnection = new SqlConnection(connection)) + Assert.Equal(rowsAffected, numberOfRows); + + using (SqlConnection sqlConnection = new SqlConnection(connection)) + { + sqlConnection.Open(); + using (SqlCommand sqlCommand = new SqlCommand($"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", + sqlConnection, + transaction: null, + columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) { - sqlConnection.Open(); - using (SqlCommand sqlCommand = new SqlCommand($"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", - sqlConnection, - transaction: null, - columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) - { - sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); - sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); + sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); + sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); - sqlCommand.Parameters[0].Value = values[0]; - sqlCommand.Parameters[1].Value = values[1]; + sqlCommand.Parameters[0].Value = values[0]; + sqlCommand.Parameters[1].Value = values[1]; - sqlCommand.Prepare(); - rowsAffected = 0; + sqlCommand.Prepare(); + rowsAffected = 0; - IAsyncResult asyncResult = sqlCommand.BeginExecuteReader(commandBehavior); - using (SqlDataReader sqlDataReader = sqlCommand.EndExecuteReader(asyncResult)) - { - Assert.True(sqlDataReader.GetName(0) == @"CustomerId", "CustomerId value read from the table was incorrect."); - Assert.True(sqlDataReader.GetName(1) == @"FirstName", "FirstName value read from the table was incorrect."); - Assert.True(sqlDataReader.GetName(2) == @"LastName", "LastName value read from the table was incorrect."); + IAsyncResult asyncResult = sqlCommand.BeginExecuteReader(commandBehavior); + using (SqlDataReader sqlDataReader = sqlCommand.EndExecuteReader(asyncResult)) + { + Assert.True(sqlDataReader.GetName(0) == @"CustomerId", "CustomerId value read from the table was incorrect."); + Assert.True(sqlDataReader.GetName(1) == @"FirstName", "FirstName value read from the table was incorrect."); + Assert.True(sqlDataReader.GetName(2) == @"LastName", "LastName value read from the table was incorrect."); - Assert.True(sqlDataReader.GetOrdinal(@"CustomerId") == 0, "CustomerId value read from the table was incorrect."); - Assert.True(sqlDataReader.GetOrdinal(@"FirstName") == 1, "FirstName value read from the table was incorrect."); - Assert.True(sqlDataReader.GetOrdinal(@"LastName") == 2, "LastName value read from the table was incorrect."); + Assert.True(sqlDataReader.GetOrdinal(@"CustomerId") == 0, "CustomerId value read from the table was incorrect."); + Assert.True(sqlDataReader.GetOrdinal(@"FirstName") == 1, "FirstName value read from the table was incorrect."); + Assert.True(sqlDataReader.GetOrdinal(@"LastName") == 2, "LastName value read from the table was incorrect."); - Assert.True(sqlDataReader.GetFieldType(0) == typeof(System.Int32), "CustomerId value read from the table was incorrect."); - Assert.True(sqlDataReader.GetFieldType(1) == typeof(System.String), "FirstName value read from the table was incorrect."); - Assert.True(sqlDataReader.GetFieldType(2) == typeof(System.String), "LastName value read from the table was incorrect."); + Assert.True(sqlDataReader.GetFieldType(0) == typeof(System.Int32), "CustomerId value read from the table was incorrect."); + Assert.True(sqlDataReader.GetFieldType(1) == typeof(System.String), "FirstName value read from the table was incorrect."); + Assert.True(sqlDataReader.GetFieldType(2) == typeof(System.String), "LastName value read from the table was incorrect."); - Assert.True(sqlDataReader.GetProviderSpecificFieldType(0) == typeof(System.Data.SqlTypes.SqlInt32), "CustomerId value read from the table was incorrect."); - Assert.True(sqlDataReader.GetProviderSpecificFieldType(1) == typeof(System.Data.SqlTypes.SqlString), "FirstName value read from the table was incorrect."); - Assert.True(sqlDataReader.GetProviderSpecificFieldType(2) == typeof(System.Data.SqlTypes.SqlString), "LastName value read from the table was incorrect."); + Assert.True(sqlDataReader.GetProviderSpecificFieldType(0) == typeof(System.Data.SqlTypes.SqlInt32), "CustomerId value read from the table was incorrect."); + Assert.True(sqlDataReader.GetProviderSpecificFieldType(1) == typeof(System.Data.SqlTypes.SqlString), "FirstName value read from the table was incorrect."); + Assert.True(sqlDataReader.GetProviderSpecificFieldType(2) == typeof(System.Data.SqlTypes.SqlString), "LastName value read from the table was incorrect."); - Assert.True(sqlDataReader.GetDataTypeName(0) == @"int", "CustomerId value read from the table was incorrect."); - Assert.True(sqlDataReader.GetDataTypeName(1) == @"nvarchar", "FirstName value read from the table was incorrect."); - Assert.True(sqlDataReader.GetDataTypeName(2) == @"nvarchar", "LastName value read from the table was incorrect."); + Assert.True(sqlDataReader.GetDataTypeName(0) == @"int", "CustomerId value read from the table was incorrect."); + Assert.True(sqlDataReader.GetDataTypeName(1) == @"nvarchar", "FirstName value read from the table was incorrect."); + Assert.True(sqlDataReader.GetDataTypeName(2) == @"nvarchar", "LastName value read from the table was incorrect."); - while (sqlDataReader.Read()) - { - textValue = new char[((string)values[1]).Length]; - sqlDataReader.GetChars(1, 0, textValue, 0, textValue.Length); - Assert.True(new string(textValue) == (string)values[1], @"Value returned by GetChars is unexpected."); + while (sqlDataReader.Read()) + { + textValue = new char[((string)values[1]).Length]; + sqlDataReader.GetChars(1, 0, textValue, 0, textValue.Length); + Assert.True(new string(textValue) == (string)values[1], @"Value returned by GetChars is unexpected."); - textValue = new char[((string)values[2]).Length]; - sqlDataReader.GetChars(2, 0, textValue, 0, textValue.Length); - Assert.True(new string(textValue) == (string)values[2], @"Value returned by GetChars is unexpected."); - } + textValue = new char[((string)values[2]).Length]; + sqlDataReader.GetChars(2, 0, textValue, 0, textValue.Length); + Assert.True(new string(textValue) == (string)values[2], @"Value returned by GetChars is unexpected."); } } + } - // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. - using (SqlCommand sqlCommand = new SqlCommand($@"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", - sqlConnection, - transaction: null, - columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) - { - sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); - sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); + // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. + using (SqlCommand sqlCommand = new SqlCommand($@"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", + sqlConnection, + transaction: null, + columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) + { + sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); + sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); - sqlCommand.Parameters[0].Value = values[0]; - sqlCommand.Parameters[1].Value = values[1]; + sqlCommand.Parameters[0].Value = values[0]; + sqlCommand.Parameters[1].Value = values[1]; - using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) + using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) + { + while (sqlDataReader.Read()) { - while (sqlDataReader.Read()) - { - Exception ex = Assert.Throws(() => sqlDataReader.GetTextReader(1)); - Assert.Equal("Retrieving encrypted column 'FirstName' with CommandBehavior=SequentialAccess is not supported.", ex.Message); - } + Exception ex = Assert.Throws(() => sqlDataReader.GetTextReader(1)); + Assert.Equal("Retrieving encrypted column 'FirstName' with CommandBehavior=SequentialAccess is not supported.", ex.Message); } } + } - // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. - using (SqlCommand sqlCommand = new SqlCommand($"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", - sqlConnection, - transaction: null, - columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) - { - sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); - sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); + // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. + using (SqlCommand sqlCommand = new SqlCommand($"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", + sqlConnection, + transaction: null, + columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) + { + sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); + sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); - sqlCommand.Parameters[0].Value = values[0]; - sqlCommand.Parameters[1].Value = values[1]; + sqlCommand.Parameters[0].Value = values[0]; + sqlCommand.Parameters[1].Value = values[1]; - using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) + using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) + { + while (sqlDataReader.Read()) { - while (sqlDataReader.Read()) - { - // GetFieldValue - Assert.True(sqlDataReader.GetFieldValue(0) == (int)values[0], @"Value returned by GetFieldValue is unexpected."); - Assert.True(sqlDataReader.GetFieldValue(1) == (string)values[1], @"Value returned by GetFieldValue is unexpected."); - Assert.True(sqlDataReader.GetFieldValue(2) == (string)values[2], @"Value returned by GetFieldValue is unexpected."); - } + // GetFieldValue + Assert.True(sqlDataReader.GetFieldValue(0) == (int)values[0], @"Value returned by GetFieldValue is unexpected."); + Assert.True(sqlDataReader.GetFieldValue(1) == (string)values[1], @"Value returned by GetFieldValue is unexpected."); + Assert.True(sqlDataReader.GetFieldValue(2) == (string)values[2], @"Value returned by GetFieldValue is unexpected."); } } + } - // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. - using (SqlCommand sqlCommand = - new SqlCommand($"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", - sqlConnection, transaction: null, columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) - { - sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); - sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); + // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. + using (SqlCommand sqlCommand = + new SqlCommand($"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", + sqlConnection, transaction: null, columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) + { + sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); + sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); - sqlCommand.Parameters[0].Value = values[0]; - sqlCommand.Parameters[1].Value = values[1]; + sqlCommand.Parameters[0].Value = values[0]; + sqlCommand.Parameters[1].Value = values[1]; - using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) + using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) + { + while (sqlDataReader.Read()) { - while (sqlDataReader.Read()) - { - // GetSqlValue - Assert.True(((System.Data.SqlTypes.SqlInt32)sqlDataReader.GetSqlValue(0)).Value == (int)values[0], - @"Value returned by GetSqlValue is unexpected."); - Assert.True(((System.Data.SqlTypes.SqlString)sqlDataReader.GetSqlValue(1)).Value == (string)values[1], - @"Value returned by GetSqlValue is unexpected."); - Assert.True(((System.Data.SqlTypes.SqlString)sqlDataReader.GetSqlValue(2)).Value == (string)values[2], - @"Value returned by GetSqlValue is unexpected."); - } + // GetSqlValue + Assert.True(((System.Data.SqlTypes.SqlInt32)sqlDataReader.GetSqlValue(0)).Value == (int)values[0], + @"Value returned by GetSqlValue is unexpected."); + Assert.True(((System.Data.SqlTypes.SqlString)sqlDataReader.GetSqlValue(1)).Value == (string)values[1], + @"Value returned by GetSqlValue is unexpected."); + Assert.True(((System.Data.SqlTypes.SqlString)sqlDataReader.GetSqlValue(2)).Value == (string)values[2], + @"Value returned by GetSqlValue is unexpected."); } } + } - // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. - using (SqlCommand sqlCommand = new SqlCommand($@"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", - sqlConnection, - transaction: null, - columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) - { - sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); - sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); + // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. + using (SqlCommand sqlCommand = new SqlCommand($@"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", + sqlConnection, + transaction: null, + columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) + { + sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); + sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); - sqlCommand.Parameters[0].Value = values[0]; - sqlCommand.Parameters[1].Value = values[1]; + sqlCommand.Parameters[0].Value = values[0]; + sqlCommand.Parameters[1].Value = values[1]; - using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) + using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) + { + while (sqlDataReader.Read()) { - while (sqlDataReader.Read()) - { - object[] readValues = new object[values.Count]; - int numberofValuesRead = sqlDataReader.GetValues(readValues); + object[] readValues = new object[values.Count]; + int numberofValuesRead = sqlDataReader.GetValues(readValues); - Assert.True(numberofValuesRead == values.Count, "the number of values returned by GetValues is unexpected."); + Assert.True(numberofValuesRead == values.Count, "the number of values returned by GetValues is unexpected."); - for (int i = 0; i < numberofValuesRead; i++) - { + for (int i = 0; i < numberofValuesRead; i++) + { - if (i != 3) - { - Assert.True(readValues[i].ToString() == values[i].ToString(), @"the values returned by GetValues is unexpected."); - } - else - { - Assert.True(((byte[])values[i]).SequenceEqual((byte[])readValues[i]), - @"Value returned by GetValues is unexpected."); - } + if (i != 3) + { + Assert.True(readValues[i].ToString() == values[i].ToString(), @"the values returned by GetValues is unexpected."); + } + else + { + Assert.True(((byte[])values[i]).SequenceEqual((byte[])readValues[i]), + @"Value returned by GetValues is unexpected."); } } } } + } - // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. - using (SqlCommand sqlCommand = - new SqlCommand( - $"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", - sqlConnection, - transaction: null, - columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) - { - sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); - sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); + // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. + using (SqlCommand sqlCommand = + new SqlCommand( + $"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", + sqlConnection, + transaction: null, + columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) + { + sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); + sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); - sqlCommand.Parameters[0].Value = values[0]; - sqlCommand.Parameters[1].Value = values[1]; + sqlCommand.Parameters[0].Value = values[0]; + sqlCommand.Parameters[1].Value = values[1]; - using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) + using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) + { + while (sqlDataReader.Read()) { - while (sqlDataReader.Read()) - { - // GetSqlValues - object[] readValues = new object[values.Count]; - int numberofValuesRead = sqlDataReader.GetSqlValues(readValues); - - Assert.True(numberofValuesRead == values.Count, "the number of values returned by GetSqlValues is unexpected."); - Assert.True(((System.Data.SqlTypes.SqlInt32)readValues[0]).Value == (int)values[0], - @"Value returned by GetSqlValues is unexpected."); - Assert.True(((System.Data.SqlTypes.SqlString)readValues[1]).Value == (string)values[1], - @"Value returned by GetSqlValues is unexpected."); - Assert.True(((System.Data.SqlTypes.SqlString)readValues[2]).Value == (string)values[2], - @"Value returned by GetSqlValues is unexpected."); - } + // GetSqlValues + object[] readValues = new object[values.Count]; + int numberofValuesRead = sqlDataReader.GetSqlValues(readValues); + + Assert.True(numberofValuesRead == values.Count, "the number of values returned by GetSqlValues is unexpected."); + Assert.True(((System.Data.SqlTypes.SqlInt32)readValues[0]).Value == (int)values[0], + @"Value returned by GetSqlValues is unexpected."); + Assert.True(((System.Data.SqlTypes.SqlString)readValues[1]).Value == (string)values[1], + @"Value returned by GetSqlValues is unexpected."); + Assert.True(((System.Data.SqlTypes.SqlString)readValues[2]).Value == (string)values[2], + @"Value returned by GetSqlValues is unexpected."); } } + } - // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. - using (SqlCommand sqlCommand = new SqlCommand($@"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", - sqlConnection, - transaction: null, - columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) - { - sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); - sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); + // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. + using (SqlCommand sqlCommand = new SqlCommand($@"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", + sqlConnection, + transaction: null, + columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) + { + sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); + sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); - sqlCommand.Parameters[0].Value = values[0]; - sqlCommand.Parameters[1].Value = values[1]; + sqlCommand.Parameters[0].Value = values[0]; + sqlCommand.Parameters[1].Value = values[1]; - using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) + using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) + { + while (sqlDataReader.Read()) { - while (sqlDataReader.Read()) - { - // GetSqlValues - object[] readValues = new object[values.Count]; - int numberofValuesRead = sqlDataReader.GetSqlValues(readValues); - - Assert.True(numberofValuesRead == values.Count, "the number of values returned by GetSqlValues is unexpected."); - Assert.True(((System.Data.SqlTypes.SqlInt32)readValues[0]).Value == (int)values[0], - @"Value returned by GetSqlValues is unexpected."); - Assert.True(((System.Data.SqlTypes.SqlString)readValues[1]).Value == (string)values[1], - @"Value returned by GetSqlValues is unexpected."); - Assert.True(((System.Data.SqlTypes.SqlString)readValues[2]).Value == (string)values[2], - @"Value returned by GetSqlValues is unexpected."); - } + // GetSqlValues + object[] readValues = new object[values.Count]; + int numberofValuesRead = sqlDataReader.GetSqlValues(readValues); + + Assert.True(numberofValuesRead == values.Count, "the number of values returned by GetSqlValues is unexpected."); + Assert.True(((System.Data.SqlTypes.SqlInt32)readValues[0]).Value == (int)values[0], + @"Value returned by GetSqlValues is unexpected."); + Assert.True(((System.Data.SqlTypes.SqlString)readValues[1]).Value == (string)values[1], + @"Value returned by GetSqlValues is unexpected."); + Assert.True(((System.Data.SqlTypes.SqlString)readValues[2]).Value == (string)values[2], + @"Value returned by GetSqlValues is unexpected."); } } + } - // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. - using (SqlCommand sqlCommand = - new SqlCommand($"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", - sqlConnection, transaction: null, - columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) - { - sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); - sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); + // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. + using (SqlCommand sqlCommand = + new SqlCommand($"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", + sqlConnection, transaction: null, + columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) + { + sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); + sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); - sqlCommand.Parameters[0].Value = values[0]; - sqlCommand.Parameters[1].Value = values[1]; + sqlCommand.Parameters[0].Value = values[0]; + sqlCommand.Parameters[1].Value = values[1]; - using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) + using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) + { + while (sqlDataReader.Read()) { - while (sqlDataReader.Read()) - { - // IsDBNullAsync - Task isDbNullTask = sqlDataReader.IsDBNullAsync(0); + // IsDBNullAsync + Task isDbNullTask = sqlDataReader.IsDBNullAsync(0); - Assert.True(isDbNullTask.Result == false, @"IsDBNullAsync unexpectedly returned false."); + Assert.True(isDbNullTask.Result == false, @"IsDBNullAsync unexpectedly returned false."); - isDbNullTask = sqlDataReader.IsDBNullAsync(1); - Assert.True(isDbNullTask.Result == false, @"IsDBNullAsync unexpectedly returned false."); + isDbNullTask = sqlDataReader.IsDBNullAsync(1); + Assert.True(isDbNullTask.Result == false, @"IsDBNullAsync unexpectedly returned false."); - isDbNullTask = sqlDataReader.IsDBNullAsync(2); - Assert.True(isDbNullTask.Result == false, @"IsDBNullAsync unexpectedly returned false."); - } + isDbNullTask = sqlDataReader.IsDBNullAsync(2); + Assert.True(isDbNullTask.Result == false, @"IsDBNullAsync unexpectedly returned false."); } } + } - // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. - using (SqlCommand sqlCommand = - new SqlCommand($"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", - sqlConnection, transaction: null, - columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) - { - sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); - sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); + // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. + using (SqlCommand sqlCommand = + new SqlCommand($"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", + sqlConnection, transaction: null, + columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) + { + sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); + sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); - sqlCommand.Parameters[0].Value = values[0]; - sqlCommand.Parameters[1].Value = values[1]; + sqlCommand.Parameters[0].Value = values[0]; + sqlCommand.Parameters[1].Value = values[1]; - using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) + using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) + { + while (sqlDataReader.Read()) { - while (sqlDataReader.Read()) - { - // GetFieldValueAsync - Task getCustomerIdTask = sqlDataReader.GetFieldValueAsync(0); - Assert.True(getCustomerIdTask.Result == (int)values[0], @"Value returned by GetFieldValueAsync is unexpected."); + // GetFieldValueAsync + Task getCustomerIdTask = sqlDataReader.GetFieldValueAsync(0); + Assert.True(getCustomerIdTask.Result == (int)values[0], @"Value returned by GetFieldValueAsync is unexpected."); - Task getFirstNameTask = sqlDataReader.GetFieldValueAsync(1); - Assert.True(getFirstNameTask.Result == (string)values[1], @"Value returned by GetFieldValueAsync is unexpected."); + Task getFirstNameTask = sqlDataReader.GetFieldValueAsync(1); + Assert.True(getFirstNameTask.Result == (string)values[1], @"Value returned by GetFieldValueAsync is unexpected."); - Task getLastNameTask = sqlDataReader.GetFieldValueAsync(2); - Assert.True(getLastNameTask.Result == (string)values[2], @"Value returned by GetFieldValueAsync is unexpected."); - } + Task getLastNameTask = sqlDataReader.GetFieldValueAsync(2); + Assert.True(getLastNameTask.Result == (string)values[2], @"Value returned by GetFieldValueAsync is unexpected."); } } + } - // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. - using (SqlCommand sqlCommand = - new SqlCommand($"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", - sqlConnection, transaction: null, - columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) - { - sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); - sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); + // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. + using (SqlCommand sqlCommand = + new SqlCommand($"SELECT CustomerId, FirstName, LastName FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", + sqlConnection, transaction: null, + columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) + { + sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); + sqlCommand.Parameters.Add(@"FirstName", SqlDbType.NVarChar, ((string)values[1]).Length); - sqlCommand.Parameters[0].Value = values[0]; - sqlCommand.Parameters[1].Value = values[1]; + sqlCommand.Parameters[0].Value = values[0]; + sqlCommand.Parameters[1].Value = values[1]; - using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) + using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) + { + while (sqlDataReader.Read()) { - while (sqlDataReader.Read()) - { - // IsDBNull - Assert.True(sqlDataReader.IsDBNull(0) == false, @"IsDBNull unexpectedly returned false."); - Assert.True(sqlDataReader.IsDBNull(1) == false, @"IsDBNull unexpectedly returned false."); - Assert.True(sqlDataReader.IsDBNull(2) == false, @"IsDBNull unexpectedly returned false."); - } + // IsDBNull + Assert.True(sqlDataReader.IsDBNull(0) == false, @"IsDBNull unexpectedly returned false."); + Assert.True(sqlDataReader.IsDBNull(1) == false, @"IsDBNull unexpectedly returned false."); + Assert.True(sqlDataReader.IsDBNull(2) == false, @"IsDBNull unexpectedly returned false."); } } + } - // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. - using (SqlCommand sqlCommand = - new SqlCommand($"SELECT * FROM [{tableName}] WHERE LastName = @LastName AND CustomerId = @CustomerId", - sqlConnection, transaction: null, - columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) - { - sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int).Value = values[0]; - sqlCommand.Parameters.Add(@"LastName", SqlDbType.NVarChar).Value = values[2]; + // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. + using (SqlCommand sqlCommand = + new SqlCommand($"SELECT * FROM [{tableName}] WHERE LastName = @LastName AND CustomerId = @CustomerId", + sqlConnection, transaction: null, + columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) + { + sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int).Value = values[0]; + sqlCommand.Parameters.Add(@"LastName", SqlDbType.NVarChar).Value = values[2]; - Task readAsyncTask = ReadAsync(sqlCommand, values, commandBehavior); - readAsyncTask.Wait(); - } + Task readAsyncTask = ReadAsync(sqlCommand, values, commandBehavior); + readAsyncTask.Wait(); + } - // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. - using (SqlCommand sqlCommand = - new SqlCommand($"UPDATE [{tableName}] SET FirstName = @FirstName WHERE CustomerId = @CustomerId", - sqlConnection, - transaction: null, - columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) - { - sqlCommand.Parameters.AddWithValue(@"FirstName", values[1]); - sqlCommand.Parameters.AddWithValue(@"CustomerId", values[0]); + // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. + using (SqlCommand sqlCommand = + new SqlCommand($"UPDATE [{tableName}] SET FirstName = @FirstName WHERE CustomerId = @CustomerId", + sqlConnection, + transaction: null, + columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) + { + sqlCommand.Parameters.AddWithValue(@"FirstName", values[1]); + sqlCommand.Parameters.AddWithValue(@"CustomerId", values[0]); - using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) - { - Assert.True(sqlDataReader.RecordsAffected == numberOfRows, @"number of rows returned by sqlDataReader.RecordsAffected is incorrect."); - } + using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) + { + Assert.True(sqlDataReader.RecordsAffected == numberOfRows, @"number of rows returned by sqlDataReader.RecordsAffected is incorrect."); } + } - // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. - using (SqlCommand sqlCommand = new SqlCommand($"INSERT INTO [{tableName}] VALUES (@CustomerId, @FirstName, @LastName )", - sqlConnection, transaction: null, - columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) - { - sqlCommand.Parameters.AddWithValue(@"CustomerId", 60); - SqlParameter firstNameParameter = new SqlParameter(@"FirstName", System.Data.SqlDbType.NVarChar, 50); + // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. + using (SqlCommand sqlCommand = new SqlCommand($"INSERT INTO [{tableName}] VALUES (@CustomerId, @FirstName, @LastName )", + sqlConnection, transaction: null, + columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) + { + sqlCommand.Parameters.AddWithValue(@"CustomerId", 60); + SqlParameter firstNameParameter = new SqlParameter(@"FirstName", System.Data.SqlDbType.NVarChar, 50); - firstNameParameter.Direction = System.Data.ParameterDirection.Input; - firstNameParameter.Value = DBNull.Value; + firstNameParameter.Direction = System.Data.ParameterDirection.Input; + firstNameParameter.Value = DBNull.Value; - sqlCommand.Parameters.Add(firstNameParameter); - sqlCommand.Parameters.AddWithValue(@"LastName", @"Corporation60"); - sqlCommand.ExecuteNonQuery(); - } + sqlCommand.Parameters.Add(firstNameParameter); + sqlCommand.Parameters.AddWithValue(@"LastName", @"Corporation60"); + sqlCommand.ExecuteNonQuery(); + } - // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. - using (SqlCommand sqlCommand = - new SqlCommand($"SELECT * FROM [{tableName}] WHERE LastName = @LastName AND CustomerId = @CustomerId", - sqlConnection, - transaction: null, - columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) - { - sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int).Value = 60; - sqlCommand.Parameters.Add(@"LastName", SqlDbType.NVarChar).Value = @"Corporation60"; + // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. + using (SqlCommand sqlCommand = + new SqlCommand($"SELECT * FROM [{tableName}] WHERE LastName = @LastName AND CustomerId = @CustomerId", + sqlConnection, + transaction: null, + columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) + { + sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int).Value = 60; + sqlCommand.Parameters.Add(@"LastName", SqlDbType.NVarChar).Value = @"Corporation60"; - using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) + using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) + { + while (sqlDataReader.Read()) { - while (sqlDataReader.Read()) - { - // IsDBNull - Assert.True(sqlDataReader.IsDBNull(0) == false, @"IsDBNull unexpectedly returned false."); - Assert.True(sqlDataReader.IsDBNull(1) == true, @"IsDBNull unexpectedly returned true."); - Assert.True(sqlDataReader.IsDBNull(2) == false, @"IsDBNull unexpectedly returned false."); - } + // IsDBNull + Assert.True(sqlDataReader.IsDBNull(0) == false, @"IsDBNull unexpectedly returned false."); + Assert.True(sqlDataReader.IsDBNull(1) == true, @"IsDBNull unexpectedly returned true."); + Assert.True(sqlDataReader.IsDBNull(2) == false, @"IsDBNull unexpectedly returned false."); } } + } - // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. - using (SqlCommand sqlCommand = - new SqlCommand($"SELECT * FROM [{tableName}] WHERE LastName = @LastName AND CustomerId = @CustomerId", - sqlConnection, - transaction: null, - columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) - { - sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int).Value = 60; - sqlCommand.Parameters.Add(@"LastName", SqlDbType.NVarChar).Value = @"Corporation60"; + // We use different commands for every API test, since SequentialAccess does not let you access a column more than once. + using (SqlCommand sqlCommand = + new SqlCommand($"SELECT * FROM [{tableName}] WHERE LastName = @LastName AND CustomerId = @CustomerId", + sqlConnection, + transaction: null, + columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) + { + sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int).Value = 60; + sqlCommand.Parameters.Add(@"LastName", SqlDbType.NVarChar).Value = @"Corporation60"; - using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) + using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(commandBehavior)) + { + while (sqlDataReader.Read()) { - while (sqlDataReader.Read()) - { - // IsDBNullAsync - Task isDbNullTask = sqlDataReader.IsDBNullAsync(0); + // IsDBNullAsync + Task isDbNullTask = sqlDataReader.IsDBNullAsync(0); - Assert.True(isDbNullTask.Result == false, @"IsDBNullAsync unexpectedly returned false."); + Assert.True(isDbNullTask.Result == false, @"IsDBNullAsync unexpectedly returned false."); - isDbNullTask = sqlDataReader.IsDBNullAsync(1); - Assert.True(isDbNullTask.Result == true, @"IsDBNullAsync unexpectedly returned true."); + isDbNullTask = sqlDataReader.IsDBNullAsync(1); + Assert.True(isDbNullTask.Result == true, @"IsDBNullAsync unexpectedly returned true."); - isDbNullTask = sqlDataReader.IsDBNullAsync(2); - Assert.True(isDbNullTask.Result == false, @"IsDBNullAsync unexpectedly returned false."); - } + isDbNullTask = sqlDataReader.IsDBNullAsync(2); + Assert.True(isDbNullTask.Result == false, @"IsDBNullAsync unexpectedly returned false."); } } } } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [ClassData(typeof(ConnectionStringProviderWithCommandBehavior))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ClassData(typeof(AEConnectionStringProviderWithCommandBehaviorSet2))] public void TestSqlCommandSequentialAccessCodePaths(string connection, CommandBehavior value) { - if (value == CommandBehavior.Default || value == CommandBehavior.SequentialAccess) - { - CommandBehavior commandBehavior = (CommandBehavior)value; - IList values = GetValues(dataHint: 57); + CleanUpTable(connection, tableName); - Assert.True(values != null && values.Count >= 3, @"values should not be null and count should be >= 3."); + CommandBehavior commandBehavior = (CommandBehavior)value; + IList values = GetValues(dataHint: 57); - int numberOfRows = 300; + Assert.True(values != null && values.Count >= 3, @"values should not be null and count should be >= 3."); - //Insert a bunch of rows in to the table. - int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection); + int numberOfRows = 100; + + //Insert a bunch of rows in to the table. + int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection); - Assert.True(rowsAffected == numberOfRows, "number of rows affected is unexpected."); + Assert.True(rowsAffected == numberOfRows, "number of rows affected is unexpected."); - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringHGSVBS)) + using (SqlConnection sqlConnection = new SqlConnection(connection)) + { + sqlConnection.Open(); + // Test SqlDataReader.GetStream() on encrypted column, throw an exception. + using (SqlCommand sqlCommand = new SqlCommand($"SELECT * FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", + sqlConnection, + transaction: null, + columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) { - sqlConnection.Open(); - // Test SqlDataReader.GetStream() on encrypted column, throw an exception. - using (SqlCommand sqlCommand = new SqlCommand($"SELECT * FROM [{tableName}] WHERE FirstName = @FirstName AND CustomerId = @CustomerId", - sqlConnection, - transaction: null, - columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) + if (DataTestUtility.EnclaveEnabled) { - sqlCommand.Parameters.AddWithValue(@"CustomerId", values[0]); - sqlCommand.Parameters.AddWithValue(@"FirstName", values[1]); + //Increase Time out for enclave-enabled server. + sqlCommand.CommandTimeout = 90; + } - using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader()) - { - Assert.True(sqlDataReader.VisibleFieldCount == values.Count, @"sqlDataReader.VisibleFieldCount returned unexpected result."); - } + sqlCommand.Parameters.AddWithValue(@"CustomerId", values[0]); + sqlCommand.Parameters.AddWithValue(@"FirstName", values[1]); + + using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader()) + { + Assert.True(sqlDataReader.VisibleFieldCount == values.Count, @"sqlDataReader.VisibleFieldCount returned unexpected result."); } } } + } private SqlDataAdapter CreateSqlDataAdapter(SqlConnection sqlConnection) @@ -2028,9 +2068,18 @@ private void TestDataAdapterFillResults(DataTable dataTable, IList value } } + private void CleanUpTable(string connString, string tableName) + { + using (var sqlConnection = new SqlConnection(connString)) + { + sqlConnection.Open(); + Table.DeleteData(tableName, sqlConnection); + } + } + public void Dispose() { - foreach (var connection in DataTestUtility.connStrings.Values) + foreach (string connection in DataTestUtility.AEConnStringsSetup) { using (SqlConnection sqlConnection = new SqlConnection(connection)) { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/BulkCopyAE.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/BulkCopyAE.cs index 2eecca3762..dccd65736a 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/BulkCopyAE.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/BulkCopyAE.cs @@ -26,8 +26,8 @@ public BulkCopyAE(SQLSetupStrategyCertStoreProvider fixture) tableName = fixture.BulkCopyAETestTable.Name; } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [ClassData(typeof(ConnectionStringProvider))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ClassData(typeof(AEConnectionStringProvider))] public void TestBulkCopyString(string connectionString) { var dataTable = new DataTable(); @@ -53,6 +53,8 @@ public void TestBulkCopyString(string connectionString) }) { connection.Open(); + Table.DeleteData(tableName, connection); + bulkCopy.WriteToServer(dataTable); string queryString = "SELECT * FROM [" + tableName + "];"; @@ -65,7 +67,7 @@ public void TestBulkCopyString(string connectionString) public void Dispose() { - foreach (var connection in DataTestUtility.connStrings.Values) + foreach (string connection in DataTestUtility.AEConnStringsSetup) { using (SqlConnection sqlConnection = new SqlConnection(connection)) { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ConversionTests.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ConversionTests.cs index 98bc52b03d..d761d6fc8c 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ConversionTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ConversionTests.cs @@ -55,7 +55,7 @@ public ColumnMetaData(SqlDbType columnType, int columnSize, int precision, int s public ConversionTests() { certificate = CertificateUtility.CreateCertificate(); - columnMasterKey = new CspColumnMasterKey(DatabaseHelper.GenerateUniqueName("CMK"), certificate.Thumbprint); + columnMasterKey = new CspColumnMasterKey(DatabaseHelper.GenerateUniqueName("CMK"), certificate.Thumbprint, certStoreProvider, DataTestUtility.EnclaveEnabled); databaseObjects.Add(columnMasterKey); columnEncryptionKey = new ColumnEncryptionKey(DatabaseHelper.GenerateUniqueName("CEK"), diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/CspProviderExt.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/CspProviderExt.cs index 5e5e197ce4..64802500aa 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/CspProviderExt.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/CspProviderExt.cs @@ -5,6 +5,7 @@ using System; using System.Linq; using System.Security.Cryptography.X509Certificates; +using Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted.Setup; using Xunit; namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted @@ -17,8 +18,9 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted public class CspProviderExt { // [Fact(Skip="Run this in non-parallel mode")] or [ConditionalFact()] - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - public void TestKeysFromCertificatesCreatedWithMultipleCryptoProviders() + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ClassData(typeof(AEConnectionStringProvider))] + public void TestKeysFromCertificatesCreatedWithMultipleCryptoProviders(string connectionString) { const string providersRegistryKeyPath = @"SOFTWARE\Microsoft\Cryptography\Defaults\Provider"; Microsoft.Win32.RegistryKey defaultCryptoProvidersRegistryKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(providersRegistryKeyPath); @@ -68,10 +70,12 @@ public void TestKeysFromCertificatesCreatedWithMultipleCryptoProviders() sqlSetupStrategyCsp = new SQLSetupStrategyCspExt(cspPath); string tableName = sqlSetupStrategyCsp.CspProviderTable.Name; - using (SqlConnection sqlConn = new SqlConnection(DataTestUtility.TCPConnectionString)) + using (SqlConnection sqlConn = new SqlConnection(connectionString)) { sqlConn.Open(); + Table.DeleteData(tableName, sqlConn); + // insert 1 row data Customer customer = new Customer(45, "Microsoft", "Corporation"); @@ -141,8 +145,9 @@ public void TestRoundTripWithCSPAndCertStoreProvider() } } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - public void TestEncryptDecryptWithCSP() + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ClassData(typeof(AEConnectionStringProvider))] + public void TestEncryptDecryptWithCSP(string connectionString) { string providerName = @"Microsoft Enhanced RSA and AES Cryptographic Provider"; string keyIdentifier = "BasicCMK"; @@ -157,10 +162,12 @@ public void TestEncryptDecryptWithCSP() try { - using (SqlConnection sqlConn = new SqlConnection(DataTestUtility.TCPConnectionString)) + using (SqlConnection sqlConn = new SqlConnection(connectionString)) { sqlConn.Open(); + Table.DeleteData(tableName, sqlConn); + // insert 1 row data Customer customer = new Customer(45, "Microsoft", "Corporation"); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/End2EndSmokeTests.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/End2EndSmokeTests.cs index 396f7116bc..a8cbde4f1b 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/End2EndSmokeTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/End2EndSmokeTests.cs @@ -4,6 +4,8 @@ using System; using System.Threading.Tasks; +using System.Collections; +using System.Collections.Generic; using Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted.Setup; using Xunit; @@ -23,16 +25,14 @@ public End2EndSmokeTests(SQLSetupStrategyCertStoreProvider fixture) } // tests - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [InlineData(@"select CustomerId, FirstName, LastName from [{0}] ", 3, new string[] { @"int", @"string", @"string" })] - [InlineData(@"select CustomerId, FirstName from [{0}] ", 2, new string[] { @"int", @"string" })] - [InlineData(@"select LastName from [{0}] ", 1, new string[] { @"string" })] - public void TestSelectOnEncryptedNonEncryptedColumns(string selectQuery, int totalColumnsInSelect, string[] types) + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ClassData(typeof(TestSelectOnEncryptedNonEncryptedColumnsData))] + public void TestSelectOnEncryptedNonEncryptedColumns(string connString, string selectQuery, int totalColumnsInSelect, string[] types) { Assert.False(string.IsNullOrWhiteSpace(selectQuery), "FAILED: select query should not be null or empty."); Assert.True(totalColumnsInSelect <= 3, "FAILED: totalColumnsInSelect should <= 3."); - using (SqlConnection sqlConn = new SqlConnection(DataTestUtility.TCPConnectionString)) + using (SqlConnection sqlConn = new SqlConnection(connString)) { sqlConn.Open(); @@ -59,118 +59,10 @@ public void TestSelectOnEncryptedNonEncryptedColumns(string selectQuery, int tot } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [InlineData(true, /*sync*/ - @"select CustomerId, FirstName, LastName from [{0}] where CustomerId = @CustomerId and FirstName = @FirstName", - 3, /*total number of columns in select statement*/ - new string[] { @"int", /*unencrypted datatype of first column in select statement*/ - @"string", /*unencrypted datatype of second column in select statement*/ - @"string" /*datatype of third column in select statement*/}, - 2, /*no:of input parameters*/ - new object[] { @"CustomerId", /*input parameter name*/ - @"int", /*input parameter data type*/ - 45, /*input parameter value*/ - @"FirstName", /*input parameter name*/ - @"string", /*input parameter data type*/ - @"Microsoft" /*input parameter value*/})] - [InlineData(true, /*sync*/ - @"select CustomerId, FirstName, LastName from [{0}] where CustomerId = @CustomerId", - 3, /*total number of columns in select statement*/ - new string[] { @"int", /*unencrypted datatype of first column in select statement*/ - @"string", /*unencrypted datatype of second column in select statement*/ - @"string" /*datatype of third column in select statement*/}, - 1, /*no:of input parameters*/ - new object[] { @"CustomerId", /*input parameter name*/ - @"int", /*input parameter data type*/ - 45, /*input parameter value*/})] - [InlineData(true, /*sync*/ - @"select CustomerId, FirstName, LastName from [{0}] where FirstName = @FirstName", - 3, /*total number of columns in select statement*/ - new string[] { @"int", /*unencrypted datatype of first column in select statement*/ - @"string", /*unencrypted datatype of second column in select statement*/ - @"string" /*datatype of third column in select statement*/}, - 1, /*no:of input parameters*/ - new object[] { @"FirstName", /*input parameter name*/ - @"string", /*input parameter data type*/ - @"Microsoft" /*input parameter value*/})] - [InlineData(true, /*sync*/ - @"select CustomerId, FirstName, LastName from [{0}] where LastName = @LastName", - 3, /*total number of columns in select statement*/ - new string[] { @"int", /*unencrypted datatype of first column in select statement*/ - @"string", /*unencrypted datatype of second column in select statement*/ - @"string" /*datatype of third column in select statement*/}, - 1, /*no:of input parameters*/ - new object[] { @"LastName", /*input parameter name*/ - @"string", /*input parameter data type*/ - @"Corporation" /*input parameter value*/})] - [InlineData(true, /*sync*/ - @"select CustomerId, FirstName from [{0}] where CustomerId = @CustomerId and FirstName = @FirstName", - 2, /*total number of columns in select statement*/ - new string[] { @"int", /*unencrypted datatype of first column in select statement*/ - @"string", /*unencrypted datatype of second column in select statement*/}, - 2, /*no:of input parameters*/ - new object[] { @"CustomerId", /*input parameter name*/ - @"int", /*input parameter data type*/ - 45, /*input parameter value*/ - @"FirstName", /*input parameter name*/ - @"string", /*input parameter data type*/ - @"Microsoft" /*input parameter value*/})] - [InlineData(false, /*sync*/ - @"select CustomerId, FirstName, LastName from [{0}] where CustomerId = @CustomerId and FirstName = @FirstName", - 3, /*total number of columns in select statement*/ - new string[] { @"int", /*unencrypted datatype of first column in select statement*/ - @"string", /*unencrypted datatype of second column in select statement*/ - @"string" /*datatype of third column in select statement*/}, - 2, /*no:of input parameters*/ - new object[] { @"CustomerId", /*input parameter name*/ - @"int", /*input parameter data type*/ - 45, /*input parameter value*/ - @"FirstName", /*input parameter name*/ - @"string", /*input parameter data type*/ - @"Microsoft" /*input parameter value*/})] - [InlineData(false, /*sync*/ - @"select CustomerId, FirstName, LastName from [{0}] where CustomerId = @CustomerId", - 3, /*total number of columns in select statement*/ - new string[] { @"int", /*unencrypted datatype of first column in select statement*/ - @"string", /*unencrypted datatype of second column in select statement*/ - @"string" /*datatype of third column in select statement*/}, - 1, /*no:of input parameters*/ - new object[] { @"CustomerId", /*input parameter name*/ - @"int", /*input parameter data type*/ - 45, /*input parameter value*/})] - [InlineData(false, /*sync*/ - @"select CustomerId, FirstName, LastName from [{0}] where FirstName = @FirstName", - 3, /*total number of columns in select statement*/ - new string[] { @"int", /*unencrypted datatype of first column in select statement*/ - @"string", /*unencrypted datatype of second column in select statement*/ - @"string" /*datatype of third column in select statement*/}, - 1, /*no:of input parameters*/ - new object[] { @"FirstName", /*input parameter name*/ - @"string", /*input parameter data type*/ - @"Microsoft" /*input parameter value*/})] - [InlineData(false, /*sync*/ - @"select CustomerId, FirstName, LastName from [{0}] where LastName = @LastName", - 3, /*total number of columns in select statement*/ - new string[] { @"int", /*unencrypted datatype of first column in select statement*/ - @"string", /*unencrypted datatype of second column in select statement*/ - @"string" /*datatype of third column in select statement*/}, - 1, /*no:of input parameters*/ - new object[] { @"LastName", /*input parameter name*/ - @"string", /*input parameter data type*/ - @"Corporation" /*input parameter value*/})] - [InlineData(false, /*sync*/ - @"select CustomerId, FirstName from [{0}] where CustomerId = @CustomerId and FirstName = @FirstName", - 2, /*total number of columns in select statement*/ - new string[] { @"int", /*unencrypted datatype of first column in select statement*/ - @"string", /*unencrypted datatype of second column in select statement*/}, - 2, /*no:of input parameters*/ - new object[] { @"CustomerId", /*input parameter name*/ - @"int", /*input parameter data type*/ - 45, /*input parameter value*/ - @"FirstName", /*input parameter name*/ - @"string", /*input parameter data type*/ - @"Microsoft" /*input parameter value*/})] - public void TestSelectOnEncryptedNonEncryptedColumnsWithEncryptedParameters(bool sync, + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ClassData(typeof(TestSelectOnEncryptedNonEncryptedColumnsWithEncryptedParametersData))] + public void TestSelectOnEncryptedNonEncryptedColumnsWithEncryptedParameters(string connString, + bool sync, string selectQuery, int totalColumnsInSelect, string[] types, @@ -180,7 +72,7 @@ public void TestSelectOnEncryptedNonEncryptedColumnsWithEncryptedParameters(bool Assert.False(string.IsNullOrWhiteSpace(selectQuery), "FAILED: select query should not be null or empty."); Assert.True(totalColumnsInSelect <= 3, "FAILED: totalColumnsInSelect should <= 3."); - using (SqlConnection sqlConn = new SqlConnection(DataTestUtility.TCPConnectionString)) + using (SqlConnection sqlConn = new SqlConnection(connString)) { sqlConn.Open(); @@ -309,11 +201,188 @@ private void CompareResults(SqlDataReader sqlDataReader, string[] parameterTypes public void Dispose() { - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionString)) + foreach (string connStrAE in DataTestUtility.AEConnStringsSetup) { - sqlConnection.Open(); - Table.DeleteData(fixture.End2EndSmokeTable.Name, sqlConnection); + using (SqlConnection sqlConnection = new SqlConnection(connStrAE)) + { + sqlConnection.Open(); + Table.DeleteData(fixture.End2EndSmokeTable.Name, sqlConnection); + } + } + } + } + + public class TestSelectOnEncryptedNonEncryptedColumnsData : IEnumerable + { + public IEnumerator GetEnumerator() + { + foreach (string connStrAE in DataTestUtility.AEConnStrings) + { + yield return new object[] { connStrAE, @"select CustomerId, FirstName, LastName from [{0}] ", 3, new string[] { @"int", @"string", @"string" } }; + yield return new object[] { connStrAE, @"select CustomerId, FirstName from [{0}] ", 2, new string[] { @"int", @"string" } }; + yield return new object[] { connStrAE, @"select LastName from [{0}] ", 1, new string[] { @"string" }}; + } + } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + + public class TestSelectOnEncryptedNonEncryptedColumnsWithEncryptedParametersData : IEnumerable + { + public IEnumerator GetEnumerator() + { + foreach (string connStrAE in DataTestUtility.AEConnStrings) + { + yield return new object[] { + connStrAE, + true, /*sync*/ + @"select CustomerId, FirstName, LastName from [{0}] where CustomerId = @CustomerId and FirstName = @FirstName", + 3, /*total number of columns in select statement*/ + new string[] { @"int", /*unencrypted datatype of first column in select statement*/ + @"string", /*unencrypted datatype of second column in select statement*/ + @"string" /*datatype of third column in select statement*/}, + 2, /*no:of input parameters*/ + new object[] { @"CustomerId", /*input parameter name*/ + @"int", /*input parameter data type*/ + 45, /*input parameter value*/ + @"FirstName", /*input parameter name*/ + @"string", /*input parameter data type*/ + @"Microsoft" /*input parameter value*/} + }; + + yield return new object[] { + connStrAE, + true, /*sync*/ + @"select CustomerId, FirstName, LastName from [{0}] where CustomerId = @CustomerId", + 3, /*total number of columns in select statement*/ + new string[] { @"int", /*unencrypted datatype of first column in select statement*/ + @"string", /*unencrypted datatype of second column in select statement*/ + @"string" /*datatype of third column in select statement*/}, + 1, /*no:of input parameters*/ + new object[] { @"CustomerId", /*input parameter name*/ + @"int", /*input parameter data type*/ + 45, /*input parameter value*/} + }; + + yield return new object[] { + connStrAE, + true, /*sync*/ + @"select CustomerId, FirstName, LastName from [{0}] where FirstName = @FirstName", + 3, /*total number of columns in select statement*/ + new string[] { @"int", /*unencrypted datatype of first column in select statement*/ + @"string", /*unencrypted datatype of second column in select statement*/ + @"string" /*datatype of third column in select statement*/}, + 1, /*no:of input parameters*/ + new object[] { @"FirstName", /*input parameter name*/ + @"string", /*input parameter data type*/ + @"Microsoft" /*input parameter value*/} + }; + + yield return new object[] { + connStrAE, + true, /*sync*/ + @"select CustomerId, FirstName, LastName from [{0}] where LastName = @LastName", + 3, /*total number of columns in select statement*/ + new string[] { @"int", /*unencrypted datatype of first column in select statement*/ + @"string", /*unencrypted datatype of second column in select statement*/ + @"string" /*datatype of third column in select statement*/}, + 1, /*no:of input parameters*/ + new object[] { @"LastName", /*input parameter name*/ + @"string", /*input parameter data type*/ + @"Corporation" /*input parameter value*/} + }; + + yield return new object[] { + connStrAE, + true, /*sync*/ + @"select CustomerId, FirstName from [{0}] where CustomerId = @CustomerId and FirstName = @FirstName", + 2, /*total number of columns in select statement*/ + new string[] { @"int", /*unencrypted datatype of first column in select statement*/ + @"string", /*unencrypted datatype of second column in select statement*/}, + 2, /*no:of input parameters*/ + new object[] { @"CustomerId", /*input parameter name*/ + @"int", /*input parameter data type*/ + 45, /*input parameter value*/ + @"FirstName", /*input parameter name*/ + @"string", /*input parameter data type*/ + @"Microsoft" /*input parameter value*/} + }; + + yield return new object[] { + connStrAE, + false, /*sync*/ + @"select CustomerId, FirstName, LastName from [{0}] where CustomerId = @CustomerId and FirstName = @FirstName", + 3, /*total number of columns in select statement*/ + new string[] { @"int", /*unencrypted datatype of first column in select statement*/ + @"string", /*unencrypted datatype of second column in select statement*/ + @"string" /*datatype of third column in select statement*/}, + 2, /*no:of input parameters*/ + new object[] { @"CustomerId", /*input parameter name*/ + @"int", /*input parameter data type*/ + 45, /*input parameter value*/ + @"FirstName", /*input parameter name*/ + @"string", /*input parameter data type*/ + @"Microsoft" /*input parameter value*/} + }; + + yield return new object[] { + connStrAE, + false, /*sync*/ + @"select CustomerId, FirstName, LastName from [{0}] where CustomerId = @CustomerId", + 3, /*total number of columns in select statement*/ + new string[] { @"int", /*unencrypted datatype of first column in select statement*/ + @"string", /*unencrypted datatype of second column in select statement*/ + @"string" /*datatype of third column in select statement*/}, + 1, /*no:of input parameters*/ + new object[] { @"CustomerId", /*input parameter name*/ + @"int", /*input parameter data type*/ + 45, /*input parameter value*/} + }; + + yield return new object[] { + connStrAE, + false, /*sync*/ + @"select CustomerId, FirstName, LastName from [{0}] where FirstName = @FirstName", + 3, /*total number of columns in select statement*/ + new string[] { @"int", /*unencrypted datatype of first column in select statement*/ + @"string", /*unencrypted datatype of second column in select statement*/ + @"string" /*datatype of third column in select statement*/}, + 1, /*no:of input parameters*/ + new object[] { @"FirstName", /*input parameter name*/ + @"string", /*input parameter data type*/ + @"Microsoft" /*input parameter value*/} + }; + + yield return new object[] { + connStrAE, + false, /*sync*/ + @"select CustomerId, FirstName, LastName from [{0}] where LastName = @LastName", + 3, /*total number of columns in select statement*/ + new string[] { @"int", /*unencrypted datatype of first column in select statement*/ + @"string", /*unencrypted datatype of second column in select statement*/ + @"string" /*datatype of third column in select statement*/}, + 1, /*no:of input parameters*/ + new object[] { @"LastName", /*input parameter name*/ + @"string", /*input parameter data type*/ + @"Corporation" /*input parameter value*/} + }; + + yield return new object[] { + connStrAE, + false, /*sync*/ + @"select CustomerId, FirstName from [{0}] where CustomerId = @CustomerId and FirstName = @FirstName", + 2, /*total number of columns in select statement*/ + new string[] { @"int", /*unencrypted datatype of first column in select statement*/ + @"string", /*unencrypted datatype of second column in select statement*/}, + 2, /*no:of input parameters*/ + new object[] { @"CustomerId", /*input parameter name*/ + @"int", /*input parameter data type*/ + 45, /*input parameter value*/ + @"FirstName", /*input parameter name*/ + @"string", /*input parameter data type*/ + @"Microsoft" /*input parameter value*/} + }; } } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/DatabaseHelper.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/DatabaseHelper.cs index 0c6c4fd94e..2aaa911a61 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/DatabaseHelper.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/DatabaseHelper.cs @@ -3,6 +3,9 @@ // See the LICENSE file in the project root for more information. using System; +using System.Data; +using System.Collections; +using System.Collections.Generic; using System.Security.Cryptography; namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted @@ -45,4 +48,84 @@ internal static byte[] GenerateRandomBytes(int length) internal static string GenerateUniqueName(string baseName) => string.Concat("AE_", baseName, "_", Guid.NewGuid().ToString().Replace('-', '_')); } + + public class AEConnectionStringProviderWithBooleanVariable : IEnumerable + { + public IEnumerator GetEnumerator() + { + foreach (string connStrAE in DataTestUtility.AEConnStrings) + { + yield return new object[] {connStrAE, true}; + yield return new object[] {connStrAE, false}; + } + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + + public class AEConnectionStringProvider : IEnumerable + { + public IEnumerator GetEnumerator() + { + foreach (string connStrAE in DataTestUtility.AEConnStrings) + { + yield return new object[] {connStrAE}; + } + } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + + public class AEConnectionStringProviderWithCommandBehaviorSet1 : IEnumerable + { + public IEnumerator GetEnumerator() + { + foreach (string connStrAE in DataTestUtility.AEConnStrings) + { + yield return new object[] { connStrAE, CommandBehavior.SingleResult }; + yield return new object[] { connStrAE, CommandBehavior.SingleRow }; + yield return new object[] { connStrAE, CommandBehavior.CloseConnection }; + yield return new object[] { connStrAE, CommandBehavior.SequentialAccess }; + } + } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + + public class AEConnectionStringProviderWithCommandBehaviorSet2 : IEnumerable + { + public IEnumerator GetEnumerator() + { + foreach (string connStrAE in DataTestUtility.AEConnStrings) + { + yield return new object[] { connStrAE, CommandBehavior.Default }; + yield return new object[] { connStrAE, CommandBehavior.SequentialAccess }; + } + } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + + public class AEConnectionStringProviderWithSchemaType : IEnumerable + { + public IEnumerator GetEnumerator() + { + foreach (string connStrAE in DataTestUtility.AEConnStrings) + { + yield return new object[] { connStrAE, SchemaType.Source }; + yield return new object[] { connStrAE, SchemaType.Mapped }; + } + } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + + public class AEConnectionStringProviderWithIntegers : IEnumerable + { + public IEnumerator GetEnumerator() + { + foreach (string connStrAE in DataTestUtility.AEConnStrings) + { + yield return new object[] { connStrAE, 1 }; + yield return new object[] { connStrAE, 100 }; + } + } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } } 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 1f96596f96..dbdd475491 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategy.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategy.cs @@ -20,21 +20,18 @@ public class SQLSetupStrategy : IDisposable public Table SqlParameterPropertiesTable { get; private set; } public Table End2EndSmokeTable { get; private set; } public Table TrustedMasterKeyPathsTestTable { get; private set; } - protected List databaseObjects = new List(); - protected Dictionary ConnectionStrings = new Dictionary(); public SQLSetupStrategy() { certificate = CertificateUtility.CreateCertificate(); - ConnectionStrings = DataTestUtility.connStrings; } protected SQLSetupStrategy(string customKeyPath) => keyPath = customKeyPath; internal virtual void SetupDatabase() { - foreach (var value in ConnectionStrings.Values) + foreach (string value in DataTestUtility.AEConnStringsSetup) { using (SqlConnection sqlConnection = new SqlConnection(value)) { @@ -42,12 +39,6 @@ internal virtual void SetupDatabase() databaseObjects.ForEach(o => o.Create(sqlConnection)); } - //using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionStringWithAEV2HGSVBSSupport)) - //{ - // sqlConnection.Open(); - // databaseObjects.ForEach(o => o.Create(sqlConnection)); - //} - // Insert data for TrustedMasterKeyPaths tests. SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(value); builder.ConnectTimeout = 10000; @@ -100,7 +91,7 @@ protected List
CreateTables(IList columnEncryptionKe public void Dispose() { databaseObjects.Reverse(); - foreach (var value in ConnectionStrings.Values) + foreach (string value in DataTestUtility.AEConnStringsSetup) { using (SqlConnection sqlConnection = new SqlConnection(value)) { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategyCertStoreProvider.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategyCertStoreProvider.cs index 21b94f08a5..9327a4a384 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategyCertStoreProvider.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategyCertStoreProvider.cs @@ -22,7 +22,7 @@ public SQLSetupStrategyCertStoreProvider() : base() internal override void SetupDatabase() { - CspColumnMasterKey = new CspColumnMasterKey(GenerateUniqueName("CMK"), certificate.Thumbprint); + CspColumnMasterKey = new CspColumnMasterKey(GenerateUniqueName("CMK"), certificate.Thumbprint, CertStoreProvider, DataTestUtility.EnclaveEnabled); databaseObjects.Add(CspColumnMasterKey); List columnEncryptionKeys = CreateColumnEncryptionKeys(CspColumnMasterKey, 2, CertStoreProvider); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategyCspExt.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategyCspExt.cs index 2e040ae0a7..3b965d5e7f 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategyCspExt.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategyCspExt.cs @@ -20,7 +20,7 @@ public SQLSetupStrategyCspExt(string cspKeyPath) : base(cspKeyPath) internal override void SetupDatabase() { - ColumnMasterKey columnMasterKey = new CspColumnMasterKey(GenerateUniqueName("CspExt"), SqlColumnEncryptionCspProvider.ProviderName, keyPath); + ColumnMasterKey columnMasterKey = new CspProviderColumnMasterKey(GenerateUniqueName("CspExt"), SqlColumnEncryptionCspProvider.ProviderName, keyPath); databaseObjects.Add(columnMasterKey); List columnEncryptionKeys = CreateColumnEncryptionKeys(columnMasterKey, 2, keyStoreProvider); @@ -29,7 +29,7 @@ internal override void SetupDatabase() Table table = CreateTable(columnEncryptionKeys); databaseObjects.Add(table); - foreach(var value in DataTestUtility.connStrings.Values) + foreach(string value in DataTestUtility.AEConnStringsSetup) { using (SqlConnection sqlConnection = new SqlConnection(value)) { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ApiTestTable.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ApiTestTable.cs index a605ffeb4b..d31cea6baa 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ApiTestTable.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ApiTestTable.cs @@ -18,7 +18,7 @@ public ApiTestTable(string tableName, ColumnEncryptionKey columnEncryptionKey1, public override void Create(SqlConnection sqlConnection) { - string encryptionType = sqlConnection.ConnectionString.Contains("HGS") ? "RANDOMIZED" : "DETERMINISTIC"; + string encryptionType = DataTestUtility.EnclaveEnabled ? "RANDOMIZED" : "DETERMINISTIC"; string sql = $@"CREATE TABLE [dbo].[{Name}] ( diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/BulkCopyAETestTable.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/BulkCopyAETestTable.cs index 56be674863..a32f47cedb 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/BulkCopyAETestTable.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/BulkCopyAETestTable.cs @@ -18,10 +18,11 @@ public BulkCopyAETestTable(string tableName, ColumnEncryptionKey columnEncryptio public override void Create(SqlConnection sqlConnection) { + string encryptionType = DataTestUtility.EnclaveEnabled ? "RANDOMIZED" : "DETERMINISTIC"; string sql = $@"CREATE TABLE [dbo].[{Name}] ( - [c1] varchar(2000) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{columnEncryptionKey1.Name}], ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = '{ColumnEncryptionAlgorithmName}') + [c1] varchar(2000) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{columnEncryptionKey1.Name}], ENCRYPTION_TYPE = {encryptionType}, ALGORITHM = '{ColumnEncryptionAlgorithmName}') )"; using (SqlCommand command = sqlConnection.CreateCommand()) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ColumnMasterKey.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ColumnMasterKey.cs index 232fa8dc62..5eb02ffcd9 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ColumnMasterKey.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ColumnMasterKey.cs @@ -17,19 +17,15 @@ protected ColumnMasterKey(string name) : base(name) } protected string KeyStoreProviderName { get; set; } + protected string cmkSignStr { get; set; } public abstract string KeyPath { get; } public override void Create(SqlConnection sqlConnection) { string sql; var connStrings = sqlConnection.ConnectionString; - if (connStrings.Contains("HGS") || connStrings.Contains("AAS")) + if (DataTestUtility.EnclaveEnabled && !String.IsNullOrEmpty(cmkSignStr)) { - - SqlColumnEncryptionCertificateStoreProvider sqlColumnCertStoreProvider = new SqlColumnEncryptionCertificateStoreProvider(); - byte[] cmkSign = sqlColumnCertStoreProvider.SignColumnMasterKeyMetadata(KeyPath, true); - string cmkSignStr = string.Concat("0x", BitConverter.ToString(cmkSign).Replace("-", string.Empty)); - sql = $@"CREATE COLUMN MASTER KEY [{Name}] WITH ( @@ -47,6 +43,7 @@ public override void Create(SqlConnection sqlConnection) KEY_PATH = N'{KeyPath}' );"; } + using (SqlCommand command = sqlConnection.CreateCommand()) { if (!string.IsNullOrEmpty(sql)) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CspColumnMasterKey.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CspColumnMasterKey.cs index 82fd583f5c..22b31ab888 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CspColumnMasterKey.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CspColumnMasterKey.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 System; using System.Security.Cryptography.X509Certificates; namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted.Setup @@ -13,17 +14,22 @@ public class CspColumnMasterKey : ColumnMasterKey public string Thumbprint { get; } public override string KeyPath { get; } - public CspColumnMasterKey(string name, string certificateThumbprint) : base(name) + public CspColumnMasterKey(string name, string certificateThumbprint, SqlColumnEncryptionKeyStoreProvider certStoreProvider, bool allEnclaveComputations) : base(name) { KeyStoreProviderName = @"MSSQL_CERTIFICATE_STORE"; Thumbprint = certificateThumbprint; KeyPath = string.Concat(CertificateStoreLocation.ToString(), "/", CertificateStoreName.ToString(), "/", Thumbprint); + + byte[] cmkSign = certStoreProvider.SignColumnMasterKeyMetadata(KeyPath, allEnclaveComputations); + cmkSignStr = string.Concat("0x", BitConverter.ToString(cmkSign).Replace("-", string.Empty)); } - public CspColumnMasterKey(string name, string providerName, string cspKeyPath) : base(name) + public CspColumnMasterKey(string name, string providerName, string cspKeyPath, SqlColumnEncryptionKeyStoreProvider certStoreProvider, bool allEnclaveComputations) : base(name) { KeyStoreProviderName = providerName; KeyPath = cspKeyPath; + byte[] cmkSign = certStoreProvider.SignColumnMasterKeyMetadata(KeyPath, allEnclaveComputations); + cmkSignStr = string.Concat("0x", BitConverter.ToString(cmkSign).Replace("-", string.Empty)); } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CspProviderColumnMasterKey.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CspProviderColumnMasterKey.cs new file mode 100644 index 0000000000..1774140057 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CspProviderColumnMasterKey.cs @@ -0,0 +1,31 @@ +// 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.Security.Cryptography.X509Certificates; + +namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted.Setup +{ + public class CspProviderColumnMasterKey : ColumnMasterKey + { + public StoreLocation CertificateStoreLocation { get; set; } = StoreLocation.CurrentUser; + public StoreName CertificateStoreName { get; set; } = StoreName.My; + public string Thumbprint { get; } + public override string KeyPath { get; } + + public CspProviderColumnMasterKey(string name, string certificateThumbprint) : base(name) + { + KeyStoreProviderName = @"MSSQL_CERTIFICATE_STORE"; + Thumbprint = certificateThumbprint; + KeyPath = string.Concat(CertificateStoreLocation.ToString(), "/", CertificateStoreName.ToString(), "/", Thumbprint); + } + + public CspProviderColumnMasterKey(string name, string providerName, string cspKeyPath) : base(name) + { + KeyStoreProviderName = providerName; + KeyPath = cspKeyPath; + } + + } +} diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs index e6b7c833a8..bde3d10e56 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs @@ -22,14 +22,17 @@ public static class DataTestUtility public static readonly string NPConnectionString = null; public static readonly string TCPConnectionString = null; public static readonly string TCPConnectionStringHGSVBS = null; + public static readonly string TCPConnectionStringAASVBS = null; + public static readonly string TCPConnectionStringAASSGX = null; public static readonly string AADAccessToken = null; public static readonly string AADPasswordConnectionString = null; public static readonly string AKVBaseUrl = null; public static readonly string AKVUrl = null; public static readonly string AKVClientId = null; public static readonly string AKVClientSecret = null; - public static Dictionary connStrings = new Dictionary(); - public static string CertificateSignature; + public static List AEConnStrings = new List(); + public static List AEConnStringsSetup = new List(); + public static readonly bool EnclaveEnabled = false; public static readonly bool SupportsIntegratedSecurity = false; public static readonly bool SupportsLocalDb = false; public static readonly bool SupportsFileStream = false; @@ -53,12 +56,14 @@ private class Config public string TCPConnectionString = null; public string NPConnectionString = null; public string TCPConnectionStringHGSVBS = null; - public string CertificateSignature = null; + public string TCPConnectionStringAASVBS = null; + public string TCPConnectionStringAASSGX = null; public string AADAccessToken = null; public string AADPasswordConnectionString = null; public string AzureKeyVaultURL = null; public string AzureKeyVaultClientId = null; public string AzureKeyVaultClientSecret = null; + public bool EnclaveEnabled = false; public bool SupportsIntegratedSecurity = false; public bool SupportsLocalDb = false; public bool SupportsFileStream = false; @@ -74,12 +79,14 @@ static DataTestUtility() NPConnectionString = c.NPConnectionString; TCPConnectionString = c.TCPConnectionString; TCPConnectionStringHGSVBS = c.TCPConnectionStringHGSVBS; - CertificateSignature = c.CertificateSignature; + TCPConnectionStringAASVBS = c.TCPConnectionStringAASVBS; + TCPConnectionStringAASSGX = c.TCPConnectionStringAASSGX; AADAccessToken = c.AADAccessToken; AADPasswordConnectionString = c.AADPasswordConnectionString; SupportsLocalDb = c.SupportsLocalDb; SupportsIntegratedSecurity = c.SupportsIntegratedSecurity; SupportsFileStream = c.SupportsFileStream; + EnclaveEnabled = c.EnclaveEnabled; string url = c.AzureKeyVaultURL; Uri AKVBaseUri = null; @@ -93,10 +100,33 @@ static DataTestUtility() AKVClientId = c.AzureKeyVaultClientId; AKVClientSecret = c.AzureKeyVaultClientSecret; } - if (!string.IsNullOrEmpty(TCPConnectionString)) + + if (EnclaveEnabled) + { + if (!string.IsNullOrEmpty(TCPConnectionStringHGSVBS)) + { + AEConnStrings.Add(TCPConnectionStringHGSVBS); + AEConnStringsSetup.Add(TCPConnectionStringHGSVBS); + } + + if (!string.IsNullOrEmpty(TCPConnectionStringAASVBS)) + { + AEConnStrings.Add(TCPConnectionStringAASVBS); + } + + if (!string.IsNullOrEmpty(TCPConnectionStringAASSGX)) + { + AEConnStrings.Add(TCPConnectionStringAASSGX); + AEConnStringsSetup.Add(TCPConnectionStringAASSGX); + } + } + else { - connStrings.Add("TCPConnectionString", TCPConnectionString); - connStrings.Add("TCPConnectionStringWithAEV2HGSVBSSupport", TCPConnectionStringHGSVBS); + if (!string.IsNullOrEmpty(TCPConnectionString)) + { + AEConnStrings.Add(TCPConnectionString); + AEConnStringsSetup.Add(TCPConnectionString); + } } } @@ -124,7 +154,12 @@ public static bool IsDatabasePresent(string name) public static bool AreConnStringsSetup() { - return connStrings.TryGetValue("TCPConnectionString", out _); + return !string.IsNullOrEmpty(NPConnectionString) && !string.IsNullOrEmpty(TCPConnectionString); + } + + public static bool AreConnStringSetupForAE() + { + return AEConnStrings.Count > 0; } public static bool IsAADPasswordConnStrSetup() @@ -435,67 +470,6 @@ public static DataTable RunQuery(string connectionString, string sql) return result; } - - } - public class ConnectionStringProviderWithBooleanVariable : IEnumerable - { - public IEnumerator GetEnumerator() - { - yield return new object[] { DataTestUtility.TCPConnectionString, true }; - yield return new object[] { DataTestUtility.TCPConnectionString, false }; - yield return new object[] { DataTestUtility.TCPConnectionStringHGSVBS, true }; - yield return new object[] { DataTestUtility.TCPConnectionStringHGSVBS, false }; - } - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - } - public class ConnectionStringProvider : IEnumerable - { - public IEnumerator GetEnumerator() - { - yield return new object[] { DataTestUtility.TCPConnectionString }; - yield return new object[] { DataTestUtility.TCPConnectionStringHGSVBS }; - - } - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - } - public class ConnectionStringProviderWithCommandBehavior : IEnumerable - { - public IEnumerator GetEnumerator() - { - yield return new object[] { DataTestUtility.TCPConnectionString, CommandBehavior.SingleResult }; - yield return new object[] { DataTestUtility.TCPConnectionString, CommandBehavior.SingleRow }; - yield return new object[] { DataTestUtility.TCPConnectionString, CommandBehavior.CloseConnection }; - yield return new object[] { DataTestUtility.TCPConnectionString, CommandBehavior.SequentialAccess }; - yield return new object[] { DataTestUtility.TCPConnectionStringHGSVBS, CommandBehavior.SingleResult }; - yield return new object[] { DataTestUtility.TCPConnectionStringHGSVBS, CommandBehavior.SingleRow }; - yield return new object[] { DataTestUtility.TCPConnectionStringHGSVBS, CommandBehavior.CloseConnection }; - yield return new object[] { DataTestUtility.TCPConnectionStringHGSVBS, CommandBehavior.SequentialAccess }; - } - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - } - - public class ConnectionStringProviderWithSchemaType : IEnumerable - { - public IEnumerator GetEnumerator() - { - yield return new object[] { DataTestUtility.TCPConnectionString, SchemaType.Source }; - yield return new object[] { DataTestUtility.TCPConnectionString, SchemaType.Mapped }; - yield return new object[] { DataTestUtility.TCPConnectionStringHGSVBS, SchemaType.Source }; - yield return new object[] { DataTestUtility.TCPConnectionStringHGSVBS, SchemaType.Mapped }; - } - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } - public class ConnectionStringProviderWithIntegers : IEnumerable - { - public IEnumerator GetEnumerator() - { - yield return new object[] { DataTestUtility.TCPConnectionString, 1 }; - yield return new object[] { DataTestUtility.TCPConnectionString, 100}; - yield return new object[] { DataTestUtility.TCPConnectionStringHGSVBS, 1 }; - yield return new object[] { DataTestUtility.TCPConnectionStringHGSVBS, 100 }; - } - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj b/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj index 9d64b773ea..ee301cb1b0 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj @@ -33,6 +33,7 @@ + diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/config.json b/src/Microsoft.Data.SqlClient/tests/ManualTests/config.json index fa37108d2c..27218a2f5e 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/config.json +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/config.json @@ -1,10 +1,10 @@ { - "TCPConnectionString": "Data Source=tcp:localhost;Database=Northwind;Integrated Security=true", + "TCPConnectionString": "Data Source=tcp:localhost;Database=Northwind;Integrated Security=true;", "NPConnectionString": "Data Source=np:localhost;Database=Northwind;Integrated Security=true;", "TCPConnectionStringHGSVBS": "", "TCPConnectionStringAASVBS": "", "TCPConnectionStringAASSGX": "", - "CertificateSignature": "", + "EnclaveEnabled": false, "AADAccessToken": "", "AADPasswordConnectionString": "", "AzureKeyVaultURL": "", From 657e50882a56182bc1208f50a92a6a9e1ae9e0ff Mon Sep 17 00:00:00 2001 From: Karina Zhou Date: Wed, 30 Oct 2019 15:46:07 -0700 Subject: [PATCH 42/63] Fix named argument error and bring change from master --- .../netcore/src/Resources/SR.resx | 2 +- .../ManualTests/AlwaysEncrypted/ApiShould.cs | 26 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx index cd79f44b12..3cbf99b3c8 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx @@ -121,7 +121,7 @@ Invalid index {0} for this {1} with Count={2}. - An {0} with {1} '{2}' is not contained by this {3}. + A {0} with {1} '{2}' is not contained by this {3}. The {0} only accepts non-null {1} type objects, not {2} objects. diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs index 366aceb9a8..5e989eeb90 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs @@ -400,7 +400,7 @@ public void TestSqlDataAdapterFillDataTable(string connection) IList values = GetValues(dataHint: 71); - InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection); + InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection: connection); var encryptionEnabledConnectionString = new SqlConnectionStringBuilder(connection) { @@ -466,7 +466,7 @@ public void TestSqlDataAdapterFillSchema(string connection, SchemaType schemaTyp int numberOfRows = 42; // Insert a bunch of rows in to the table. - int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection); + int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection: connection); using (SqlConnection sqlConnection = new SqlConnection(connection)) { @@ -515,7 +515,7 @@ public void TestExecuteNonQuery(string connection, bool isAsync) int numberOfRows = 10 + i; // Insert a bunch of rows in to the table. - int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection); + int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection: connection); Assert.Equal(numberOfRows, rowsAffected); rowsAffected = -1; @@ -567,7 +567,7 @@ public void TestExecuteScalar(string connection, bool isAsync) int numberOfRows = 10 + i; // Insert a bunch of rows in to the table. - int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection); + int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection: connection); using (SqlConnection sqlConnection = new SqlConnection(connection)) { @@ -663,7 +663,7 @@ public void TestExecuteReader(string connection) IList values = GetValues(dataHint: 45 + i + 1); int numberOfRows = 10 + i; // Insert a bunch of rows in to the table. - int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection); + int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection: connection); Assert.True(numberOfRows == rowsAffected, "Two values failed"); using (SqlConnection sqlConnection = new SqlConnection(connection)) @@ -726,7 +726,7 @@ public void TestExecuteReaderWithCommandBehavior(string connection, CommandBehav Assert.False(values == null || values.Count < 3, @"values should not be null and count should be >= 3."); int numberOfRows = 10 + i; // Insert a bunch of rows in to the table. - int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection); + int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection: connection); Assert.Equal(rowsAffected, numberOfRows); using (SqlConnection sqlConnection = new SqlConnection(connection)) @@ -844,7 +844,7 @@ public void TestPrepareWithExecuteNonQuery(string connection) int numberOfRows = 10; // Insert a bunch of rows in to the table. - int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection); + int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection: connection); Assert.True(rowsAffected == numberOfRows, "number of rows affected is unexpected."); @@ -891,7 +891,7 @@ public void TestAsyncWriteDelayWithExecuteNonQueryAsync(string connection) int numberOfRows = 10; // Insert a bunch of rows in to the table. - int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection); + int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection: connection); using (SqlConnection sqlconnection = new SqlConnection(connection)) { @@ -947,7 +947,7 @@ public void TestAsyncWriteDelayWithExecuteReaderAsync(string connection) int numberOfRows = 10; // Insert a bunch of rows in to the table. - int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection); + int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection: connection); Assert.True(rowsAffected == numberOfRows, "number of rows affected is unexpected."); @@ -1014,7 +1014,7 @@ public void TestPrepareWithExecuteNonQueryAsync(string connection) int numberOfRows = 10; // Insert a bunch of rows in to the table. - int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection); + int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection: connection); Assert.True(rowsAffected == numberOfRows, "number of rows affected is unexpected."); @@ -1134,7 +1134,7 @@ public void TestSqlDataReaderAPIs(string connection) Assert.True(values != null && values.Count >= 3, @"values should not be null and count should be >= 3."); // Insert a bunch of rows in to the table. - int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection); + int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection: connection); Assert.True(rowsAffected == numberOfRows, "number of rows affected is unexpected."); @@ -1332,7 +1332,7 @@ public void TestSqlDataReaderAPIsWithSequentialAccess(string connection) int numberOfRows = 10; // Insert a bunch of rows in to the table. - int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection); + int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection: connection); Assert.Equal(rowsAffected, numberOfRows); @@ -1757,7 +1757,7 @@ public void TestSqlCommandSequentialAccessCodePaths(string connection, CommandBe int numberOfRows = 100; //Insert a bunch of rows in to the table. - int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection); + int rowsAffected = InsertRows(tableName: tableName, numberofRows: numberOfRows, values: values, connection: connection); Assert.True(rowsAffected == numberOfRows, "number of rows affected is unexpected."); From 1674894d180e14590396c621561d295353231384 Mon Sep 17 00:00:00 2001 From: Javad Rahnama Date: Wed, 30 Oct 2019 17:18:38 -0700 Subject: [PATCH 43/63] Comments resolved on AEV2 PR --- .../SqlConnectionAttestationProtocol.xml | 21 ++ .../Microsoft.Data.SqlClient.NetCoreApp.cs | 19 +- .../Data/Common/DbConnectionStringCommon.cs | 44 --- .../SqlClient/EnclaveDelegate.NetCoreApp.cs | 22 -- .../Microsoft/Data/SqlClient/SqlConnection.cs | 43 +-- .../Data/SqlClient/SqlConnectionString.cs | 11 +- .../src/Microsoft/Data/SqlClient/TdsEnums.cs | 16 +- .../netcore/src/Resources/SR.Designer.cs | 318 +++++------------- .../netfx/ref/Microsoft.Data.SqlClient.cs | 18 +- .../AlwaysEncryptedEnclaveProviderUtils.cs | 1 - .../Data/SqlClient/EnclaveDelegate.cs | 58 ---- .../Microsoft/Data/SqlClient/SqlCommand.cs | 196 +++-------- .../Data/SqlClient/SqlConnectionString.cs | 5 +- .../src/Microsoft/Data/SqlClient/TdsEnums.cs | 16 +- .../AlwaysEncryptedTests/Utility.cs | 5 - 15 files changed, 191 insertions(+), 602 deletions(-) create mode 100644 doc/snippets/Microsoft.Data.SqlClient/SqlConnectionAttestationProtocol.xml diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionAttestationProtocol.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionAttestationProtocol.xml new file mode 100644 index 0000000000..3f64ef38b8 --- /dev/null +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionAttestationProtocol.xml @@ -0,0 +1,21 @@ + + + + + Specifies a value for Attestation Protocol. + + + + If the attestation protocol is not specified. Use this as default value. + 0 + + + Attestation portocol for Azure Attestation Service + 1 + + + Attestation protocol for Host Guardian Service + 3 + + + diff --git a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.NetCoreApp.cs index a5a8df9df8..09119e9c50 100644 --- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.NetCoreApp.cs @@ -100,22 +100,17 @@ public enum SqlConnectionColumnEncryptionSetting /// Enabled = 1, } - /// - /// To add include file for docs - /// + + /// public enum SqlConnectionAttestationProtocol { - /// - /// To add include file for docs - /// + /// NotSpecified = 0, - /// - /// To add include file for docs - /// + + /// AAS = 1, - /// - /// To add include file for docs - /// + + /// HGS = 3 } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs index 1857d76064..e88144906d 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs @@ -148,18 +148,9 @@ internal static bool TryConvertToApplicationIntent(string value, out Application } } - /// - /// Column Encryption Setting. - /// const string ColumnEncryptionSettingEnabledString = "Enabled"; const string ColumnEncryptionSettingDisabledString = "Disabled"; - /// - /// Convert a string value to the corresponding SqlConnectionColumnEncryptionSetting. - /// - /// - /// - /// internal static bool TryConvertToColumnEncryptionSetting(string value, out SqlConnectionColumnEncryptionSetting result) { bool isSuccess = false; @@ -182,22 +173,12 @@ internal static bool TryConvertToColumnEncryptionSetting(string value, out SqlCo return isSuccess; } - /// - /// Is it a valid connection level column encryption setting ? - /// - /// - /// internal static bool IsValidColumnEncryptionSetting(SqlConnectionColumnEncryptionSetting value) { Debug.Assert(Enum.GetNames(typeof(SqlConnectionColumnEncryptionSetting)).Length == 2, "SqlConnectionColumnEncryptionSetting enum has changed, update needed"); return value == SqlConnectionColumnEncryptionSetting.Enabled || value == SqlConnectionColumnEncryptionSetting.Disabled; } - /// - /// Convert connection level column encryption setting value to string. - /// - /// - /// internal static string ColumnEncryptionSettingToString(SqlConnectionColumnEncryptionSetting value) { Debug.Assert(IsValidColumnEncryptionSetting(value), "value is not a valid connection level column encryption setting."); @@ -216,18 +197,9 @@ internal static string ColumnEncryptionSettingToString(SqlConnectionColumnEncryp #region <> - /// - /// Attestation Protocol. - /// const string AttestationProtocolHGS = "HGS"; const string AttestationProtocolAAS = "AAS"; - /// - /// Convert a string value to the corresponding SqlConnectionAttestationProtocol - /// - /// - /// - /// internal static bool TryConvertToAttestationProtocol(string value, out SqlConnectionAttestationProtocol result) { bool isSuccess = false; @@ -364,16 +336,6 @@ internal static string ApplicationIntentToString(ApplicationIntent value) } } - /// - /// This method attempts to convert the given value tp ApplicationIntent enum. The algorithm is: - /// * if the value is from type string, it will be matched against ApplicationIntent enum names only, using ordinal, case-insensitive comparer - /// * if the value is from type ApplicationIntent, it will be used as is - /// * if the value is from integral type (SByte, Int16, Int32, Int64, Byte, UInt16, UInt32, or UInt64), it will be converted to enum - /// * if the value is another enum or any other type, it will be blocked with an appropriate ArgumentException - /// - /// in any case above, if the converted value is out of valid range, the method raises ArgumentOutOfRangeException. - /// - /// application intent value in the valid range internal static ApplicationIntent ConvertToApplicationIntent(string keyword, object value) { Debug.Assert(null != value, "ConvertToApplicationIntent(null)"); @@ -539,12 +501,6 @@ internal static SqlAuthenticationMethod ConvertToAuthenticationType(string keywo } } - /// - /// Convert the provided value to a SqlConnectionColumnEncryptionSetting. - /// - /// - /// - /// internal static SqlConnectionColumnEncryptionSetting ConvertToColumnEncryptionSetting(string keyword, object value) { if (null == value) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetCoreApp.cs index 9a5cbf62a1..da95f9eb3d 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetCoreApp.cs @@ -7,9 +7,6 @@ namespace Microsoft.Data.SqlClient { - /// - /// A delegate for communicating with secure enclave - /// internal partial class EnclaveDelegate { private static readonly string GetSerializedAttestationParametersName = "GetSerializedAttestationParameters"; @@ -56,15 +53,6 @@ internal byte[] GetSerializedAttestationParameters(SqlEnclaveAttestationParamete return CombineByteArrays(new[] { attestationProtocolBytes, attestationProtocolInputLengthBytes, attestationProtocolInputBytes, clientDHPublicKeyLengthBytes, clientDHPublicKey }); } - /// - /// Create a new enclave session - /// - /// attestation protocol - /// enclave type - /// servername - /// attestation url for attestation service endpoint - /// attestation info from SQL Server - /// attestation parameters internal void CreateEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, string serverName, string attestationUrl, byte[] attestationInfo, SqlEnclaveAttestationParameters attestationParameters) { @@ -87,16 +75,6 @@ internal void CreateEnclaveSession(SqlConnectionAttestationProtocol attestationP } } - /// - /// Generate the byte package that needs to be sent to the enclave - /// - /// attestation protocol - /// Keys to be sent to enclave - /// - /// enclave type - /// server name - /// url for attestation endpoint - /// internal EnclavePackage GenerateEnclavePackage(SqlConnectionAttestationProtocol attestationProtocol, Dictionary keysTobeSentToEnclave, string queryText, string enclaveType, string serverName, string enclaveAttestationUrl) { diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs index 20e32310f4..9cc7353e98 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -74,17 +74,13 @@ private static readonly Dictionary // Lock to control setting of _CustomColumnEncryptionKeyStoreProviders private static readonly Object _CustomColumnEncryptionKeyProvidersLock = new Object(); - /// - /// Custom provider list should be provided by the user. We shallow copy the user supplied dictionary into a ReadOnlyDictionary. - /// Custom provider list can only supplied once per application. - /// + // Custom provider list should be provided by the user. We shallow copy the user supplied dictionary into a ReadOnlyDictionary. + // Custom provider list can only supplied once per application. private static ReadOnlyDictionary _CustomColumnEncryptionKeyStoreProviders; - /// - /// Dictionary object holding trusted key paths for various SQL Servers. - /// Key to the dictionary is a SQL Server Name - /// IList contains a list of trusted key paths. - /// + // Dictionary object holding trusted key paths for various SQL Servers. + // Key to the dictionary is a SQL Server Name + // IList contains a list of trusted key paths. private static readonly ConcurrentDictionary> _ColumnEncryptionTrustedMasterKeyPaths = new ConcurrentDictionary>(concurrencyLevel: 4 * Environment.ProcessorCount /* default value in ConcurrentDictionary*/, capacity: 1, @@ -149,12 +145,7 @@ private SqlConnection(SqlConnection connection) CacheConnectionStringProperties(); } - /// - /// This function walks through both system and custom column encryption key store providers and returns an object if found. - /// - /// Provider Name to be searched in System Provider diction and Custom provider dictionary. - /// If the provider is found, returns the corresponding SqlColumnEncryptionKeyStoreProvider instance. - /// true if the provider is found, else returns false + // This function walks through both system and custom column encryption key store providers and returns an object if found. static internal bool TryGetColumnEncryptionKeyStoreProvider(string providerName, out SqlColumnEncryptionKeyStoreProvider columnKeyStoreProvider) { Debug.Assert(!string.IsNullOrWhiteSpace(providerName), "Provider name is invalid"); @@ -181,20 +172,14 @@ static internal bool TryGetColumnEncryptionKeyStoreProvider(string providerName, } } - /// - /// This function returns a list of system provider dictionary currently supported by this driver. - /// - /// Combined list of provider names + // This function returns a list of system provider dictionary currently supported by this driver. static internal List GetColumnEncryptionSystemKeyStoreProviders() { HashSet providerNames = new HashSet(_SystemColumnEncryptionKeyStoreProviders.Keys); return providerNames.ToList(); } - /// - /// This function returns a list of custom provider dictionary currently registered. - /// - /// Combined list of provider names + // This function returns a list of custom provider dictionary currently registered. static internal List GetColumnEncryptionCustomKeyStoreProviders() { if (_CustomColumnEncryptionKeyStoreProviders != null) @@ -206,9 +191,7 @@ static internal List GetColumnEncryptionCustomKeyStoreProviders() return new List(); } - /// - /// Is this connection using column encryption ? - /// + // Is this connection using column encryption ? internal bool IsColumnEncryptionSettingEnabled { get @@ -271,14 +254,10 @@ public static void RegisterColumnEncryptionKeyStoreProviders(IDictionary - /// Get enclave attestation url to be used with enclave based Always Encrypted - /// + // Get enclave attestation url to be used with enclave based Always Encrypted internal string EnclaveAttestationUrl => ((SqlConnectionString)ConnectionOptions).EnclaveAttestationUrl; - /// - /// Get attestation protocol - /// + // Get attestation protocol internal SqlConnectionAttestationProtocol AttestationProtocol { get diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionString.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionString.cs index f642e84a64..dc73c42ab7 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionString.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionString.cs @@ -691,10 +691,7 @@ internal SqlAuthenticationMethod ConvertValueToAuthenticationType() } } - /// - /// Convert the value to SqlConnectionColumnEncryptionSetting. - /// - /// + // Convert the value to SqlConnectionColumnEncryptionSetting. internal SqlConnectionColumnEncryptionSetting ConvertValueToColumnEncryptionSetting() { if (!TryGetParsetableValue(KEY.ColumnEncryptionSetting, out string value)) @@ -716,10 +713,8 @@ internal SqlConnectionColumnEncryptionSetting ConvertValueToColumnEncryptionSett } } - /// - /// Convert the value to SqlConnectionAttestationProtocol - /// - /// + + // Convert the value to SqlConnectionAttestationProtocol internal SqlConnectionAttestationProtocol ConvertValueToAttestationProtocol() { if (!TryGetParsetableValue(KEY.AttestationProtocol, out string value)) 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 902bc0f094..48aadf85e5 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 @@ -1047,24 +1047,16 @@ internal enum ParsingErrorState DataClassificationInvalidInformationTypeIndex = 27 } - /// - /// - /// + /// public enum SqlConnectionAttestationProtocol { - /// - /// If the attestation protocol is not specified. Use this as default value. - /// + /// NotSpecified = 0, - /// - /// Attestation portocol for Azure Attestation Service - /// + /// AAS = 1, - /// - /// Attestation protocol for Host Guardian Service - /// + /// HGS = 3 } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs index 0eb3bea77b..4156437061 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs @@ -4065,711 +4065,553 @@ internal static string TCE_AttestationInfoNotReturnedFromSQLServer { } } - /// - /// Looks up a localized string similar to {0} should be identical on all commands ({1}, {2}, {3}, {4}) when doing batch updates.. - /// + // Looks up a localized string similar to {0} should be identical on all commands ({1}, {2}, {3}, {4}) when doing batch updates.. internal static string TCE_BatchedUpdateColumnEncryptionSettingMismatch { get { return ResourceManager.GetString("TCE_BatchedUpdateColumnEncryptionSettingMismatch", resourceCulture); } } - /// - /// Looks up a localized string similar to Failed to instantiate an enclave provider with type '{1}' for name '{0}'. Error message: {2}. - /// + // Looks up a localized string similar to Failed to instantiate an enclave provider with type '{1}' for name '{0}'. Error message: {2}. internal static string TCE_CannotCreateSqlColumnEncryptionEnclaveProvider { get { return ResourceManager.GetString("TCE_CannotCreateSqlColumnEncryptionEnclaveProvider", resourceCulture); } } - /// - /// Looks up a localized string similar to Failed to read the configuration section for enclave providers. Make sure the section is correctly formatted in your application configuration file. Error Message: {0}. - /// + // Looks up a localized string similar to Failed to read the configuration section for enclave providers. Make sure the section is correctly formatted in your application configuration file. Error Message: {0}. internal static string TCE_CannotGetSqlColumnEncryptionEnclaveProviderConfig { get { return ResourceManager.GetString("TCE_CannotGetSqlColumnEncryptionEnclaveProviderConfig", resourceCulture); } } - /// - /// Looks up a localized string similar to Key store providers cannot be set more than once.. - /// + // Looks up a localized string similar to Key store providers cannot be set more than once.. internal static string TCE_CanOnlyCallOnce { get { return ResourceManager.GetString("TCE_CanOnlyCallOnce", resourceCulture); } } - /// - /// Looks up a localized string similar to Certificate with thumbprint '{0}' not found in certificate store '{1}' in certificate location '{2}'.. - /// + // Looks up a localized string similar to Certificate with thumbprint '{0}' not found in certificate store '{1}' in certificate location '{2}'.. internal static string TCE_CertificateNotFound { get { return ResourceManager.GetString("TCE_CertificateNotFound", resourceCulture); } } - /// - /// Looks up a localized string similar to Certificate with thumbprint '{0}' not found in certificate store '{1}' in certificate location '{2}'. Verify the certificate path in the column master key definition in the database is correct, and the certificate has been imported correctly into the certificate location/store.. - /// + // Looks up a localized string similar to Certificate with thumbprint '{0}' not found in certificate store '{1}' in certificate location '{2}'. Verify the certificate path in the column master key definition in the database is correct, and the certificate has been imported correctly into the certificate location/store.. internal static string TCE_CertificateNotFoundSysErr { get { return ResourceManager.GetString("TCE_CertificateNotFoundSysErr", resourceCulture); } } - /// - /// Looks up a localized string similar to Certificate specified in key path '{0}' does not have a private key to encrypt a column encryption key. Verify the certificate is imported correctly.. - /// + // Looks up a localized string similar to Certificate specified in key path '{0}' does not have a private key to encrypt a column encryption key. Verify the certificate is imported correctly.. internal static string TCE_CertificateWithNoPrivateKey { get { return ResourceManager.GetString("TCE_CertificateWithNoPrivateKey", resourceCulture); } } - /// - /// Looks up a localized string similar to Certificate specified in key path '{0}' does not have a private key to decrypt a column encryption key. Verify the certificate is imported correctly.. - /// + // Looks up a localized string similar to Certificate specified in key path '{0}' does not have a private key to decrypt a column encryption key. Verify the certificate is imported correctly.. internal static string TCE_CertificateWithNoPrivateKeySysErr { get { return ResourceManager.GetString("TCE_CertificateWithNoPrivateKeySysErr", resourceCulture); } } - /// - /// Looks up a localized string similar to Failed to decrypt column '{0}'.. - /// + // Looks up a localized string similar to Failed to decrypt column '{0}'.. internal static string TCE_ColumnDecryptionFailed { get { return ResourceManager.GetString("TCE_ColumnDecryptionFailed", resourceCulture); } } - /// - /// Looks up a localized string similar to Internal Error. Encrypted column encryption keys not found when trying to send the keys to the enclave.. - /// + // Looks up a localized string similar to Internal Error. Encrypted column encryption keys not found when trying to send the keys to the enclave.. internal static string TCE_ColumnEncryptionKeysNotFound { get { return ResourceManager.GetString("TCE_ColumnEncryptionKeysNotFound", resourceCulture); } } - /// - /// Looks up a localized string similar to Internal error. The signature returned by SQL Server for enclave-enabled column master key, specified at key path '{0}', cannot be null or empty.. - /// + // Looks up a localized string similar to Internal error. The signature returned by SQL Server for enclave-enabled column master key, specified at key path '{0}', cannot be null or empty.. internal static string TCE_ColumnMasterKeySignatureNotFound { get { return ResourceManager.GetString("TCE_ColumnMasterKeySignatureNotFound", resourceCulture); } } - /// - /// Looks up a localized string similar to The signature returned by SQL Server for the column master key, specified in key path '{0}', is invalid (does not match the computed signature). Recreate column master key metadata, making sure the signature inside the metadata is computed using the column master key being referenced in the metadata. If the error persists, please contact Microsoft for assistance.. - /// + // Looks up a localized string similar to The signature returned by SQL Server for the column master key, specified in key path '{0}', is invalid (does not match the computed signature). Recreate column master key metadata, making sure the signature inside the metadata is computed using the column master key being referenced in the metadata. If the error persists, please contact Microsoft for assistance.. internal static string TCE_ColumnMasterKeySignatureVerificationFailed { get { return ResourceManager.GetString("TCE_ColumnMasterKeySignatureVerificationFailed", resourceCulture); } } - /// - /// Looks up a localized string similar to Decryption failed. The last 10 bytes of the encrypted column encryption key are: '{0}'. The first 10 bytes of ciphertext are: '{1}'.. - /// + // Looks up a localized string similar to Decryption failed. The last 10 bytes of the encrypted column encryption key are: '{0}'. The first 10 bytes of ciphertext are: '{1}'.. internal static string TCE_DecryptionFailed { get { return ResourceManager.GetString("TCE_DecryptionFailed", resourceCulture); } } - /// - /// Looks up a localized string similar to Internal Error. Empty argument '{0}' specified when constructing an object of type '{1}'. '{0}' cannot be empty.. - /// + // Looks up a localized string similar to Internal Error. Empty argument '{0}' specified when constructing an object of type '{1}'. '{0}' cannot be empty.. internal static string TCE_EmptyArgumentInConstructorInternal { get { return ResourceManager.GetString("TCE_EmptyArgumentInConstructorInternal", resourceCulture); } } - /// - /// Looks up a localized string similar to Internal Error. Argument '{0}' cannot be empty when executing method '{1}.{2}'.. - /// + // Looks up a localized string similar to Internal Error. Argument '{0}' cannot be empty when executing method '{1}.{2}'.. internal static string TCE_EmptyArgumentInternal { get { return ResourceManager.GetString("TCE_EmptyArgumentInternal", resourceCulture); } } - /// - /// Looks up a localized string similar to Empty certificate thumbprint specified in certificate path '{0}'.. - /// + // Looks up a localized string similar to Empty certificate thumbprint specified in certificate path '{0}'.. internal static string TCE_EmptyCertificateThumbprint { get { return ResourceManager.GetString("TCE_EmptyCertificateThumbprint", resourceCulture); } } - /// - /// Looks up a localized string similar to Internal error. Empty certificate thumbprint specified in certificate path '{0}'.. - /// + // Looks up a localized string similar to Internal error. Empty certificate thumbprint specified in certificate path '{0}'.. internal static string TCE_EmptyCertificateThumbprintSysErr { get { return ResourceManager.GetString("TCE_EmptyCertificateThumbprintSysErr", resourceCulture); } } - /// - /// Looks up a localized string similar to Empty key identifier specified in column master key path: '{0}'. Use the following format for a key stored in a Microsoft Cryptography API: Next Generation (CNG) provider: <CNG Provider Name>{1}<Key Identifier>.. - /// + // Looks up a localized string similar to Empty key identifier specified in column master key path: '{0}'. Use the following format for a key stored in a Microsoft Cryptography API: Next Generation (CNG) provider: <CNG Provider Name>{1}<Key Identifier>.. internal static string TCE_EmptyCngKeyId { get { return ResourceManager.GetString("TCE_EmptyCngKeyId", resourceCulture); } } - /// - /// Looks up a localized string similar to Internal error. Empty key identifier specified in column master key path: '{0}'. Use the following format for a key stored in a Microsoft Cryptography API: Next Generation (CNG) provider: <CNG Provider Name>{1}<Key Identifier>.. - /// + // Looks up a localized string similar to Internal error. Empty key identifier specified in column master key path: '{0}'. Use the following format for a key stored in a Microsoft Cryptography API: Next Generation (CNG) provider: <CNG Provider Name>{1}<Key Identifier>.. internal static string TCE_EmptyCngKeyIdSysErr { get { return ResourceManager.GetString("TCE_EmptyCngKeyIdSysErr", resourceCulture); } } - /// - /// Looks up a localized string similar to Empty Microsoft Cryptography API: Next Generation (CNG) provider name specified in column master key path: '{0}'. Use the following format for a key stored in a Microsoft Cryptography API: Next Generation (CNG) provider: <CNG Provider Name>{1}<Key Identifier>.. - /// + // Looks up a localized string similar to Empty Microsoft Cryptography API: Next Generation (CNG) provider name specified in column master key path: '{0}'. Use the following format for a key stored in a Microsoft Cryptography API: Next Generation (CNG) provider: <CNG Provider Name>{1}<Key Identifier>.. internal static string TCE_EmptyCngName { get { return ResourceManager.GetString("TCE_EmptyCngName", resourceCulture); } } - /// - /// Looks up a localized string similar to Internal error. Empty Microsoft Cryptography API: Next Generation (CNG) provider name specified in column master key path: '{0}'. Use the following format for a key stored in a Microsoft Cryptography API: Next Generation (CNG) provider: <CNG Provider Name>{1}<Key Identifier>.. - /// + // Looks up a localized string similar to Internal error. Empty Microsoft Cryptography API: Next Generation (CNG) provider name specified in column master key path: '{0}'. Use the following format for a key stored in a Microsoft Cryptography API: Next Generation (CNG) provider: <CNG Provider Name>{1}<Key Identifier>.. internal static string TCE_EmptyCngNameSysErr { get { return ResourceManager.GetString("TCE_EmptyCngNameSysErr", resourceCulture); } } - /// - /// Looks up a localized string similar to Empty column encryption key specified.. - /// + // Looks up a localized string similar to Empty column encryption key specified.. internal static string TCE_EmptyColumnEncryptionKey { get { return ResourceManager.GetString("TCE_EmptyColumnEncryptionKey", resourceCulture); } } - /// - /// Looks up a localized string similar to Empty key identifier specified in column master key path: '{0}'. Use the following format for a key stored in a Microsoft cryptographic service provider (CSP): <CSP Provider Name>{1}<Key Identifier>.. - /// + // Looks up a localized string similar to Empty key identifier specified in column master key path: '{0}'. Use the following format for a key stored in a Microsoft cryptographic service provider (CSP): <CSP Provider Name>{1}<Key Identifier>.. internal static string TCE_EmptyCspKeyId { get { return ResourceManager.GetString("TCE_EmptyCspKeyId", resourceCulture); } } - /// - /// Looks up a localized string similar to Internal error. Empty key identifier specified in column master key path: '{0}'. Use the following format for a key stored in a Microsoft cryptographic service provider (CSP): <CSP Provider Name>{1}<Key Identifier>.. - /// + // Looks up a localized string similar to Internal error. Empty key identifier specified in column master key path: '{0}'. Use the following format for a key stored in a Microsoft cryptographic service provider (CSP): <CSP Provider Name>{1}<Key Identifier>.. internal static string TCE_EmptyCspKeyIdSysErr { get { return ResourceManager.GetString("TCE_EmptyCspKeyIdSysErr", resourceCulture); } } - /// - /// Looks up a localized string similar to Empty Microsoft cryptographic service provider (CSP) name specified in column master key path: '{0}'. Use the following format for a key stored in a Microsoft cryptographic service provider (CSP): <CSP Provider Name>{1}<Key Identifier>.. - /// + // Looks up a localized string similar to Empty Microsoft cryptographic service provider (CSP) name specified in column master key path: '{0}'. Use the following format for a key stored in a Microsoft cryptographic service provider (CSP): <CSP Provider Name>{1}<Key Identifier>.. internal static string TCE_EmptyCspName { get { return ResourceManager.GetString("TCE_EmptyCspName", resourceCulture); } } - /// - /// Looks up a localized string similar to Internal error. Empty Microsoft cryptographic service provider (CSP) name specified in column master key path: '{0}'. Use the following format for a key stored in a Microsoft cryptographic service provider (CSP): <CSP Provider Name>{1}<Key Identifier>.. - /// + // Looks up a localized string similar to Internal error. Empty Microsoft cryptographic service provider (CSP) name specified in column master key path: '{0}'. Use the following format for a key stored in a Microsoft cryptographic service provider (CSP): <CSP Provider Name>{1}<Key Identifier>.. internal static string TCE_EmptyCspNameSysErr { get { return ResourceManager.GetString("TCE_EmptyCspNameSysErr", resourceCulture); } } - /// - /// Looks up a localized string similar to Internal error. Empty encrypted column encryption key specified.. - /// + // Looks up a localized string similar to Internal error. Empty encrypted column encryption key specified.. internal static string TCE_EmptyEncryptedColumnEncryptionKey { get { return ResourceManager.GetString("TCE_EmptyEncryptedColumnEncryptionKey", resourceCulture); } } - /// - /// Looks up a localized string similar to Invalid key store provider name specified. Key store provider names cannot be null or empty.. - /// + // Looks up a localized string similar to Invalid key store provider name specified. Key store provider names cannot be null or empty.. internal static string TCE_EmptyProviderName { get { return ResourceManager.GetString("TCE_EmptyProviderName", resourceCulture); } } - /// - /// Looks up a localized string similar to You have specified the enclave attestation URL in the connection string, but the SQL Server instance in use does not support enclave based computations.. - /// + // Looks up a localized string similar to You have specified the enclave attestation URL in the connection string, but the SQL Server instance in use does not support enclave based computations.. internal static string TCE_EnclaveComputationsNotSupported { get { return ResourceManager.GetString("TCE_EnclaveComputationsNotSupported", resourceCulture); } } - /// - /// Looks up a localized string similar to No enclave provider found for enclave type '{0}'. Please specify the provider in the application configuration.. - /// + // Looks up a localized string similar to No enclave provider found for enclave type '{0}'. Please specify the provider in the application configuration.. internal static string TCE_EnclaveProviderNotFound { get { return ResourceManager.GetString("TCE_EnclaveProviderNotFound", resourceCulture); } } - /// - /// Looks up a localized string similar to Specifies an attestation protocol for its corresponding enclave attestation service. - /// + // Looks up a localized string similar to Specifies an attestation protocol for its corresponding enclave attestation service. internal static string TCE_DbConnectionString_AttestationProtocol { get { return ResourceManager.GetString("TCE_DbConnectionString_AttestationProtocol", resourceCulture); } } - /// - /// Looks up a localized string similar to The enclave type '{0}' returned from the server is not supported. - /// + // Looks up a localized string similar to The enclave type '{0}' returned from the server is not supported. internal static string TCE_EnclaveTypeNotSupported { get { return ResourceManager.GetString("TCE_EnclaveTypeNotSupported", resourceCulture); } } - /// - /// Looks up a localized string similar to Failed to initialize connection. The attestation protocol '{0}' does not support the enclave type '{1}'. - /// + // Looks up a localized string similar to Failed to initialize connection. The attestation protocol '{0}' does not support the enclave type '{1}'. internal static string TCE_AttestationProtocolNotSupportEnclaveType { get { return ResourceManager.GetString("TCE_AttestationProtocolNotSupportEnclaveType", resourceCulture); } } - /// - /// Looks up a localized string similar to Error occured when generating enclave package. Attestation Protocol has not been specified in the connection string, but the query requires enclave computations. - /// + // Looks up a localized string similar to Error occured when generating enclave package. Attestation Protocol has not been specified in the connection string, but the query requires enclave computations. internal static string TCE_AttestationProtocolNotSpecifiedForGeneratingEnclavePackage { get { return ResourceManager.GetString("TCE_AttestationProtocolNotSpecifiedForGeneratingEnclavePackage", resourceCulture); } } - /// - /// Looks up a localized string similar to Executing a query requires enclave computations, but the application configuration is missing the enclave provider section.. - /// + // Looks up a localized string similar to Executing a query requires enclave computations, but the application configuration is missing the enclave provider section.. internal static string TCE_EnclaveProvidersNotConfiguredForEnclaveBasedQuery { get { return ResourceManager.GetString("TCE_EnclaveProvidersNotConfiguredForEnclaveBasedQuery", resourceCulture); } } - /// - /// Looks up a localized string similar to You have specified the enclave attestation URL in the connection string, but the SQL Server instance did not return an enclave type. Please make sure the enclave type is correctly configured in your instance.. - /// + // Looks up a localized string similar to You have specified the enclave attestation URL in the connection string, but the SQL Server instance did not return an enclave type. Please make sure the enclave type is correctly configured in your instance.. internal static string TCE_EnclaveTypeNotReturned { get { return ResourceManager.GetString("TCE_EnclaveTypeNotReturned", resourceCulture); } } - /// - /// Looks up a localized string similar to Internal Error. Enclave type received from SQL Server is null or empty when executing a query requiring enclave computations.. - /// + // Looks up a localized string similar to Internal Error. Enclave type received from SQL Server is null or empty when executing a query requiring enclave computations.. internal static string TCE_EnclaveTypeNullForEnclaveBasedQuery { get { return ResourceManager.GetString("TCE_EnclaveTypeNullForEnclaveBasedQuery", resourceCulture); } } - /// - /// Looks up a localized string similar to Error encountered while generating package to be sent to enclave. Error message: {0}. - /// + // Looks up a localized string similar to Error encountered while generating package to be sent to enclave. Error message: {0}. internal static string TCE_ExceptionWhenGeneratingEnclavePackage { get { return ResourceManager.GetString("TCE_ExceptionWhenGeneratingEnclavePackage", resourceCulture); } } - /// - /// Looks up a localized string similar to Internal Error. Failed to encrypt byte package to be sent to the enclave. Error Message: {0}. - /// + // Looks up a localized string similar to Internal Error. Failed to encrypt byte package to be sent to the enclave. Error Message: {0}. internal static string TCE_FailedToEncryptRegisterRulesBytePackage { get { return ResourceManager.GetString("TCE_FailedToEncryptRegisterRulesBytePackage", resourceCulture); } } - /// - /// Looks up a localized string similar to Internal Error. The buffer specified by argument '{0}' for method '{1}.{2}' has insufficient space.. - /// + // Looks up a localized string similar to Internal Error. The buffer specified by argument '{0}' for method '{1}.{2}' has insufficient space.. internal static string TCE_InsufficientBuffer { get { return ResourceManager.GetString("TCE_InsufficientBuffer", resourceCulture); } } - /// - /// Looks up a localized string similar to The specified ciphertext's encryption algorithm version '{0}' does not match the expected encryption algorithm version '{1}'.. - /// + // Looks up a localized string similar to The specified ciphertext's encryption algorithm version '{0}' does not match the expected encryption algorithm version '{1}'.. internal static string TCE_InvalidAlgorithmVersion { get { return ResourceManager.GetString("TCE_InvalidAlgorithmVersion", resourceCulture); } } - /// - /// Looks up a localized string similar to Specified encrypted column encryption key contains an invalid encryption algorithm version '{0}'. Expected version is '{1}'.. - /// + // Looks up a localized string similar to Specified encrypted column encryption key contains an invalid encryption algorithm version '{0}'. Expected version is '{1}'.. internal static string TCE_InvalidAlgorithmVersionInEncryptedCEK { get { return ResourceManager.GetString("TCE_InvalidAlgorithmVersionInEncryptedCEK", resourceCulture); } } - /// - /// Looks up a localized string similar to Invalid attestation parameters specified by the enclave provider for enclave type '{0}'. Error occurred when converting the value '{1}' of parameter '{2}' to unsigned int. Error Message: {3}. - /// + // Looks up a localized string similar to Invalid attestation parameters specified by the enclave provider for enclave type '{0}'. Error occurred when converting the value '{1}' of parameter '{2}' to unsigned int. Error Message: {3}. internal static string TCE_InvalidAttestationParameterUnableToConvertToUnsignedInt { get { return ResourceManager.GetString("TCE_InvalidAttestationParameterUnableToConvertToUnsignedInt", resourceCulture); } } - /// - /// Looks up a localized string similar to Specified ciphertext has an invalid authentication tag.. - /// + // Looks up a localized string similar to Specified ciphertext has an invalid authentication tag.. internal static string TCE_InvalidAuthenticationTag { get { return ResourceManager.GetString("TCE_InvalidAuthenticationTag", resourceCulture); } } - /// - /// Looks up a localized string similar to Invalid certificate location '{0}' in certificate path '{1}'. Use the following format: <certificate location>{4}<certificate store>{4}<certificate thumbprint>, where <certificate location> is either '{2}' or '{3}'.. - /// + // Looks up a localized string similar to Invalid certificate location '{0}' in certificate path '{1}'. Use the following format: <certificate location>{4}<certificate store>{4}<certificate thumbprint>, where <certificate location> is either '{2}' or '{3}'.. internal static string TCE_InvalidCertificateLocation { get { return ResourceManager.GetString("TCE_InvalidCertificateLocation", resourceCulture); } } - /// - /// Looks up a localized string similar to Internal error. Invalid certificate location '{0}' in certificate path '{1}'. Use the following format: <certificate location>{4}<certificate store>{4}<certificate thumbprint>, where <certificate location> is either '{2}' or '{3}'.. - /// + // Looks up a localized string similar to Internal error. Invalid certificate location '{0}' in certificate path '{1}'. Use the following format: <certificate location>{4}<certificate store>{4}<certificate thumbprint>, where <certificate location> is either '{2}' or '{3}'.. internal static string TCE_InvalidCertificateLocationSysErr { get { return ResourceManager.GetString("TCE_InvalidCertificateLocationSysErr", resourceCulture); } } - /// - /// Looks up a localized string similar to Invalid certificate path: '{0}'. Use the following format: <certificate location>{3}<certificate store>{3}<certificate thumbprint>, where <certificate location> is either '{1}' or '{2}'.. - /// + // Looks up a localized string similar to Invalid certificate path: '{0}'. Use the following format: <certificate location>{3}<certificate store>{3}<certificate thumbprint>, where <certificate location> is either '{1}' or '{2}'.. internal static string TCE_InvalidCertificatePath { get { return ResourceManager.GetString("TCE_InvalidCertificatePath", resourceCulture); } } - /// - /// Looks up a localized string similar to Internal error. Invalid certificate path: '{0}'. Use the following format: <certificate location>{3}<certificate store>{3}<certificate thumbprint>, where <certificate location> is either '{1}' or '{2}'.. - /// + // Looks up a localized string similar to Internal error. Invalid certificate path: '{0}'. Use the following format: <certificate location>{3}<certificate store>{3}<certificate thumbprint>, where <certificate location> is either '{1}' or '{2}'.. internal static string TCE_InvalidCertificatePathSysErr { get { return ResourceManager.GetString("TCE_InvalidCertificatePathSysErr", resourceCulture); } } - /// - /// Looks up a localized string similar to The specified encrypted column encryption key signature does not match the signature computed with the column master key (certificate) in '{0}'. The encrypted column encryption key may be corrupt, or the specified path may be incorrect.. - /// + // Looks up a localized string similar to The specified encrypted column encryption key signature does not match the signature computed with the column master key (certificate) in '{0}'. The encrypted column encryption key may be corrupt, or the specified path may be incorrect.. internal static string TCE_InvalidCertificateSignature { get { return ResourceManager.GetString("TCE_InvalidCertificateSignature", resourceCulture); } } - /// - /// Looks up a localized string similar to Invalid certificate store '{0}' specified in certificate path '{1}'. Expected value: '{2}'.. - /// + // Looks up a localized string similar to Invalid certificate store '{0}' specified in certificate path '{1}'. Expected value: '{2}'.. internal static string TCE_InvalidCertificateStore { get { return ResourceManager.GetString("TCE_InvalidCertificateStore", resourceCulture); } } - /// - /// Looks up a localized string similar to Internal error. Invalid certificate store '{0}' specified in certificate path '{1}'. Expected value: '{2}'.. - /// + // Looks up a localized string similar to Internal error. Invalid certificate store '{0}' specified in certificate path '{1}'. Expected value: '{2}'.. internal static string TCE_InvalidCertificateStoreSysErr { get { return ResourceManager.GetString("TCE_InvalidCertificateStoreSysErr", resourceCulture); } } - /// - /// Looks up a localized string similar to The specified encrypted column encryption key's ciphertext length: {0} does not match the ciphertext length: {1} when using column master key (certificate) in '{2}'. The encrypted column encryption key may be corrupt, or the specified certificate path may be incorrect.. - /// + // Looks up a localized string similar to The specified encrypted column encryption key's ciphertext length: {0} does not match the ciphertext length: {1} when using column master key (certificate) in '{2}'. The encrypted column encryption key may be corrupt, or the specified certificate path may be incorrect.. internal static string TCE_InvalidCiphertextLengthInEncryptedCEK { get { return ResourceManager.GetString("TCE_InvalidCiphertextLengthInEncryptedCEK", resourceCulture); } } - /// - /// Looks up a localized string similar to The specified encrypted column encryption key's ciphertext length: {0} does not match the ciphertext length: {1} when using column master key (asymmetric key) in '{2}'. The encrypted column encryption key may be corrupt, or the specified Microsoft Cryptography API: Next Generation (CNG) provider path may be incorrect.. - /// + // Looks up a localized string similar to The specified encrypted column encryption key's ciphertext length: {0} does not match the ciphertext length: {1} when using column master key (asymmetric key) in '{2}'. The encrypted column encryption key may be corrupt, or the specified Microsoft Cryptography API: Next Generation (CNG) provider path may be incorrect.. internal static string TCE_InvalidCiphertextLengthInEncryptedCEKCng { get { return ResourceManager.GetString("TCE_InvalidCiphertextLengthInEncryptedCEKCng", resourceCulture); } } - /// - /// Looks up a localized string similar to The specified encrypted column encryption key's ciphertext length: {0} does not match the ciphertext length: {1} when using column master key (asymmetric key) in '{2}'. The encrypted column encryption key may be corrupt, or the specified Microsoft Cryptographic Service provider (CSP) path may be incorrect.. - /// + // Looks up a localized string similar to The specified encrypted column encryption key's ciphertext length: {0} does not match the ciphertext length: {1} when using column master key (asymmetric key) in '{2}'. The encrypted column encryption key may be corrupt, or the specified Microsoft Cryptographic Service provider (CSP) path may be incorrect.. internal static string TCE_InvalidCiphertextLengthInEncryptedCEKCsp { get { return ResourceManager.GetString("TCE_InvalidCiphertextLengthInEncryptedCEKCsp", resourceCulture); } } - /// - /// Looks up a localized string similar to Specified ciphertext has an invalid size of {0} bytes, which is below the minimum {1} bytes required for decryption.. - /// + // Looks up a localized string similar to Specified ciphertext has an invalid size of {0} bytes, which is below the minimum {1} bytes required for decryption.. internal static string TCE_InvalidCipherTextSize { get { return ResourceManager.GetString("TCE_InvalidCipherTextSize", resourceCulture); } } - /// - /// Looks up a localized string similar to An error occurred while opening the Microsoft Cryptography API: Next Generation (CNG) key: '{0}'. Verify that the CNG provider name '{1}' is valid, installed on the machine, and the key '{2}' exists.. - /// + // Looks up a localized string similar to An error occurred while opening the Microsoft Cryptography API: Next Generation (CNG) key: '{0}'. Verify that the CNG provider name '{1}' is valid, installed on the machine, and the key '{2}' exists.. internal static string TCE_InvalidCngKey { get { return ResourceManager.GetString("TCE_InvalidCngKey", resourceCulture); } } - /// - /// Looks up a localized string similar to Internal error. An error occurred while opening the Microsoft Cryptography API: Next Generation (CNG) key: '{0}'. Verify that the CNG provider name '{1}' is valid, installed on the machine, and the key '{2}' exists.. - /// + // Looks up a localized string similar to Internal error. An error occurred while opening the Microsoft Cryptography API: Next Generation (CNG) key: '{0}'. Verify that the CNG provider name '{1}' is valid, installed on the machine, and the key '{2}' exists.. internal static string TCE_InvalidCngKeySysErr { get { return ResourceManager.GetString("TCE_InvalidCngKeySysErr", resourceCulture); } } - /// - /// Looks up a localized string similar to Invalid column master key path: '{0}'. Use the following format for a key stored in a Microsoft Cryptography API: Next Generation (CNG) provider: <CNG Provider Name>{1}<Key Identifier>.. - /// + // Looks up a localized string similar to Invalid column master key path: '{0}'. Use the following format for a key stored in a Microsoft Cryptography API: Next Generation (CNG) provider: <CNG Provider Name>{1}<Key Identifier>.. internal static string TCE_InvalidCngPath { get { return ResourceManager.GetString("TCE_InvalidCngPath", resourceCulture); } } - - /// - /// Looks up a localized string similar to Internal error. Invalid column master key path: '{0}'. Use the following format for a key stored in a Microsoft Cryptography API: Next Generation (CNG) provider: <CNG Provider Name>{1}<Key Identifier>.. - /// + + // Looks up a localized string similar to Internal error. Invalid column master key path: '{0}'. Use the following format for a key stored in a Microsoft Cryptography API: Next Generation (CNG) provider: <CNG Provider Name>{1}<Key Identifier>.. internal static string TCE_InvalidCngPathSysErr { get { return ResourceManager.GetString("TCE_InvalidCngPathSysErr", resourceCulture); } } - /// - /// Looks up a localized string similar to Invalid key identifier: '{0}'. Verify that the key identifier in column master key path: '{1}' is valid and exists in the CSP.. - /// + // Looks up a localized string similar to Invalid key identifier: '{0}'. Verify that the key identifier in column master key path: '{1}' is valid and exists in the CSP.. internal static string TCE_InvalidCspKeyId { get { return ResourceManager.GetString("TCE_InvalidCspKeyId", resourceCulture); } } - /// - /// Looks up a localized string similar to Internal error. Invalid key identifier: '{0}'. Verify that the key identifier in column master key path: '{1}' is valid and exists in the CSP.. - /// + // Looks up a localized string similar to Internal error. Invalid key identifier: '{0}'. Verify that the key identifier in column master key path: '{1}' is valid and exists in the CSP.. internal static string TCE_InvalidCspKeyIdSysErr { get { return ResourceManager.GetString("TCE_InvalidCspKeyIdSysErr", resourceCulture); } } - /// - /// Looks up a localized string similar to Invalid Microsoft cryptographic service provider (CSP) name: '{0}'. Verify that the CSP provider name in column master key path: '{1}' is valid and installed on the machine.. - /// + // Looks up a localized string similar to Invalid Microsoft cryptographic service provider (CSP) name: '{0}'. Verify that the CSP provider name in column master key path: '{1}' is valid and installed on the machine.. internal static string TCE_InvalidCspName { get { return ResourceManager.GetString("TCE_InvalidCspName", resourceCulture); } } - /// - /// Looks up a localized string similar to Internal error. Invalid Microsoft cryptographic service provider (CSP) name: '{0}'. Verify that the CSP provider name in column master key path: '{1}' is valid and installed on the machine.. - /// + // Looks up a localized string similar to Internal error. Invalid Microsoft cryptographic service provider (CSP) name: '{0}'. Verify that the CSP provider name in column master key path: '{1}' is valid and installed on the machine.. internal static string TCE_InvalidCspNameSysErr { get { return ResourceManager.GetString("TCE_InvalidCspNameSysErr", resourceCulture); } } - /// - /// Looks up a localized string similar to Invalid column master key path: '{0}'. Use the following format for a key stored in a Microsoft cryptographic service provider (CSP): <CSP Provider Name>{1}<Key Identifier>.. - /// + // Looks up a localized string similar to Invalid column master key path: '{0}'. Use the following format for a key stored in a Microsoft cryptographic service provider (CSP): <CSP Provider Name>{1}<Key Identifier>.. internal static string TCE_InvalidCspPath { get { return ResourceManager.GetString("TCE_InvalidCspPath", resourceCulture); } } - /// - /// Looks up a localized string similar to Internal error. Invalid column master key path: '{0}'. Use the following format for a key stored in a Microsoft cryptographic service provider (CSP): <CSP Provider Name>{1}<Key Identifier>.. - /// + // Looks up a localized string similar to Internal error. Invalid column master key path: '{0}'. Use the following format for a key stored in a Microsoft cryptographic service provider (CSP): <CSP Provider Name>{1}<Key Identifier>.. internal static string TCE_InvalidCspPathSysErr { get { return ResourceManager.GetString("TCE_InvalidCspPathSysErr", resourceCulture); } } - /// - /// Looks up a localized string similar to Invalid key store provider name '{0}'. '{1}' prefix is reserved for system key store providers.. - /// + // Looks up a localized string similar to Invalid key store provider name '{0}'. '{1}' prefix is reserved for system key store providers.. internal static string TCE_InvalidCustomKeyStoreProviderName { get { return ResourceManager.GetString("TCE_InvalidCustomKeyStoreProviderName", resourceCulture); } } - /// - /// Looks up a localized string similar to Internal Error. The given database id '{0}' is not valid. Error occurred when converting the database id to unsigned int. Error Message: {1}. - /// + // Looks up a localized string similar to Internal Error. The given database id '{0}' is not valid. Error occurred when converting the database id to unsigned int. Error Message: {1}. internal static string TCE_InvalidDatabaseIdUnableToCastToUnsignedInt { get { return ResourceManager.GetString("TCE_InvalidDatabaseIdUnableToCastToUnsignedInt", resourceCulture); } } - /// - /// Looks up a localized string similar to Internal error. Error occurred when populating enclave metadata. The referenced column encryption key ordinal '{0}' is missing in the encryption metadata returned by SQL Server. Max ordinal is '{1}'.. - /// + // Looks up a localized string similar to Internal error. Error occurred when populating enclave metadata. The referenced column encryption key ordinal '{0}' is missing in the encryption metadata returned by SQL Server. Max ordinal is '{1}'.. internal static string TCE_InvalidEncryptionKeyOrdinalEnclaveMetadata { get { return ResourceManager.GetString("TCE_InvalidEncryptionKeyOrdinalEnclaveMetadata", resourceCulture); } } - /// - /// Looks up a localized string similar to Internal error. Error occurred when populating parameter metadata. The referenced column encryption key ordinal '{0}' is missing in the encryption metadata returned by SQL Server. Max ordinal is '{1}'.. - /// + // Looks up a localized string similar to Internal error. Error occurred when populating parameter metadata. The referenced column encryption key ordinal '{0}' is missing in the encryption metadata returned by SQL Server. Max ordinal is '{1}'.. internal static string TCE_InvalidEncryptionKeyOrdinalParameterMetadata { get { return ResourceManager.GetString("TCE_InvalidEncryptionKeyOrdinalParameterMetadata", resourceCulture); } } - /// - /// Looks up a localized string similar to Encryption type '{1}' specified for the column in the database is either invalid or corrupted. Valid encryption types for algorithm '{0}' are: {2}.. - /// + // Looks up a localized string similar to Encryption type '{1}' specified for the column in the database is either invalid or corrupted. Valid encryption types for algorithm '{0}' are: {2}.. internal static string TCE_InvalidEncryptionType { get { return ResourceManager.GetString("TCE_InvalidEncryptionType", resourceCulture); } } - /// - /// Looks up a localized string similar to Invalid key encryption algorithm specified: '{0}'. Expected value: '{1}'.. - /// + // Looks up a localized string similar to Invalid key encryption algorithm specified: '{0}'. Expected value: '{1}'.. internal static string TCE_InvalidKeyEncryptionAlgorithm { get { return ResourceManager.GetString("TCE_InvalidKeyEncryptionAlgorithm", resourceCulture); } } - /// - /// Looks up a localized string similar to Internal error. Invalid key encryption algorithm specified: '{0}'. Expected value: '{1}'.. - /// + // Looks up a localized string similar to Internal error. Invalid key encryption algorithm specified: '{0}'. Expected value: '{1}'.. internal static string TCE_InvalidKeyEncryptionAlgorithmSysErr { get { return ResourceManager.GetString("TCE_InvalidKeyEncryptionAlgorithmSysErr", resourceCulture); } } - /// - /// Looks up a localized string similar to Internal Error. The given key id '{0}' is not valid. Error occurred when converting the key id to unsigned short. Error Message: {1}. - /// + // Looks up a localized string similar to Internal Error. The given key id '{0}' is not valid. Error occurred when converting the key id to unsigned short. Error Message: {1}. internal static string TCE_InvalidKeyIdUnableToCastToUnsignedShort { get { return ResourceManager.GetString("TCE_InvalidKeyIdUnableToCastToUnsignedShort", resourceCulture); } } - /// - /// Looks up a localized string similar to The column encryption key has been successfully decrypted but it's length: {1} does not match the length: {2} for algorithm '{0}'. Verify the encrypted value of the column encryption key in the database.. - /// + // Looks up a localized string similar to The column encryption key has been successfully decrypted but it's length: {1} does not match the length: {2} for algorithm '{0}'. Verify the encrypted value of the column encryption key in the database.. internal static string TCE_InvalidKeySize { get { return ResourceManager.GetString("TCE_InvalidKeySize", resourceCulture); } } - /// - /// Looks up a localized string similar to Invalid key store provider name: '{0}'. A key store provider name must denote either a system key store provider or a registered custom key store provider. Valid system key store provider names are: {1}. Valid (currently registered) custom key store provider names are: {2}. Please verify key store provider information in column master key definitions in the database, and verify all custom key store providers used in your application are registered properly.. - /// + // Looks up a localized string similar to Invalid key store provider name: '{0}'. A key store provider name must denote either a system key store provider or a registered custom key store provider. Valid system key store provider names are: {1}. Valid (currently registered) custom key store provider names are: {2}. Please verify key store provider information in column master key definitions in the database, and verify all custom key store providers used in your application are registered properly.. internal static string TCE_InvalidKeyStoreProviderName { get { return ResourceManager.GetString("TCE_InvalidKeyStoreProviderName", resourceCulture); } } - /// - /// Looks up a localized string similar to The specified encrypted column encryption key signature does not match the signature computed with the column master key (asymmetric key) in '{0}'. The encrypted column encryption key may be corrupt, or the specified path may be incorrect.. - /// + // Looks up a localized string similar to The specified encrypted column encryption key signature does not match the signature computed with the column master key (asymmetric key) in '{0}'. The encrypted column encryption key may be corrupt, or the specified path may be incorrect.. internal static string TCE_InvalidSignature { get { return ResourceManager.GetString("TCE_InvalidSignature", resourceCulture); } } - /// - /// Looks up a localized string similar to The specified encrypted column encryption key's signature length: {0} does not match the signature length: {1} when using column master key (certificate) in '{2}'. The encrypted column encryption key may be corrupt, or the specified certificate path may be incorrect.. - /// + // Looks up a localized string similar to The specified encrypted column encryption key's signature length: {0} does not match the signature length: {1} when using column master key (certificate) in '{2}'. The encrypted column encryption key may be corrupt, or the specified certificate path may be incorrect.. internal static string TCE_InvalidSignatureInEncryptedCEK { get { return ResourceManager.GetString("TCE_InvalidSignatureInEncryptedCEK", resourceCulture); } } - /// - /// Looks up a localized string similar to The specified encrypted column encryption key's signature length: {0} does not match the signature length: {1} when using column master key (asymmetric key) in '{2}'. The encrypted column encryption key may be corrupt, or the specified Microsoft Cryptography API: Next Generation (CNG) provider path may be incorrect.. - /// + // Looks up a localized string similar to The specified encrypted column encryption key's signature length: {0} does not match the signature length: {1} when using column master key (asymmetric key) in '{2}'. The encrypted column encryption key may be corrupt, or the specified Microsoft Cryptography API: Next Generation (CNG) provider path may be incorrect.. internal static string TCE_InvalidSignatureInEncryptedCEKCng { get { return ResourceManager.GetString("TCE_InvalidSignatureInEncryptedCEKCng", resourceCulture); } } - /// - /// Looks up a localized string similar to The specified encrypted column encryption key's signature length: {0} does not match the signature length: {1} when using column master key (asymmetric key) in '{2}'. The encrypted column encryption key may be corrupt, or the specified Microsoft cryptographic service provider (CSP) path may be incorrect.. - /// + // Looks up a localized string similar to The specified encrypted column encryption key's signature length: {0} does not match the signature length: {1} when using column master key (asymmetric key) in '{2}'. The encrypted column encryption key may be corrupt, or the specified Microsoft cryptographic service provider (CSP) path may be incorrect.. internal static string TCE_InvalidSignatureInEncryptedCEKCsp { get { return ResourceManager.GetString("TCE_InvalidSignatureInEncryptedCEKCsp", resourceCulture); 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 ff18606b42..897cc77093 100644 --- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs @@ -787,22 +787,16 @@ public enum SqlConnectionColumnEncryptionSetting Enabled = 1, } - /// - /// To add include file for docs - /// + /// public enum SqlConnectionAttestationProtocol { - /// - /// To add include file for docs - /// + /// NotSpecified = 0, - /// - /// To add include file for docs - /// + + /// AAS = 1, - /// - /// To add include file for docs - /// + + /// HGS = 3 } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs index 2ffe98f1fd..066d33be25 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs @@ -46,7 +46,6 @@ public EnclaveDiffieHellmanInfo(byte[] payload) internal enum EnclaveType { - None = 0, Vbs = 1, diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs index 6e3d7024e8..c5c7c17a59 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs @@ -11,9 +11,6 @@ namespace Microsoft.Data.SqlClient { - /// - /// A delegate for communicating with secure enclave - /// internal class EnclaveDelegate { @@ -35,16 +32,6 @@ internal class EnclaveDelegate private EnclaveDelegate() { } - /// - /// Generate the byte package that needs to be sent to the enclave - /// - /// attestation protocol - /// Keys to be sent to enclave - /// - /// enclave type - /// server name - /// url for attestation endpoint - /// internal EnclavePackage GenerateEnclavePackage(SqlConnectionAttestationProtocol attestationProtocol, Dictionary keysTobeSentToEnclave, string queryText, string enclaveType, string serverName, string enclaveAttestationUrl) { @@ -153,15 +140,6 @@ private byte[] GetUintBytes(string enclaveType, int intValue, string variableNam } } - /// - /// Create a new enclave session - /// - /// attestation protocol - /// enclave type - /// servername - /// attestation url for attestation service endpoint - /// attestation info from SQL Server - /// attestation parameters internal void CreateEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, string serverName, string attestationUrl, byte[] attestationInfo, SqlEnclaveAttestationParameters attestationParameters) { @@ -233,12 +211,6 @@ private string ConvertAttestationProtocolToString(SqlConnectionAttestationProtoc } } - /// - /// Decrypt the keys that need to be sent to the enclave - /// - /// Keys that need to sent to the enclave - /// - /// private List GetDecryptedKeysToBeSentToEnclave(Dictionary keysTobeSentToEnclave, string serverName) { List decryptedKeysToBeSentToEnclave = new List(); @@ -267,13 +239,6 @@ private List GetDecryptedKeysToBeSentToEnclave(Dictiona return decryptedKeysToBeSentToEnclave; } - /// - /// Generate a byte package consisting of decrypted keys and some headers expected by the enclave - /// - /// counter to avoid replay attacks - /// - /// - /// private byte[] GenerateBytePackageForKeys(long enclaveSessionCounter, byte[] queryStringHashBytes, List keys) { @@ -311,13 +276,6 @@ private byte[] GenerateBytePackageForKeys(long enclaveSessionCounter, byte[] que return bytePackage; } - /// - /// Encrypt the byte package containing keys with the session key - /// - /// byte package containing keys - /// session key used to encrypt the package - /// server hosting the enclave - /// private byte[] EncryptBytePackage(byte[] bytePackage, byte[] sessionKey, string serverName) { if (sessionKey == null) @@ -340,11 +298,6 @@ private byte[] EncryptBytePackage(byte[] bytePackage, byte[] sessionKey, string } } - /// - /// Combine the array of given byte arrays into one - /// - /// byte arrays to be combined - /// private byte[] CombineByteArrays(byte[][] byteArraysToCombine) { byte[] combinedArray = new byte[byteArraysToCombine.Sum(ba => ba.Length)]; @@ -385,28 +338,17 @@ private byte[] ComputeQueryStringHash(string queryString) return hash; } - /// - /// Exception when executing a enclave based Always Encrypted query - /// internal class RetriableEnclaveQueryExecutionException : Exception { internal RetriableEnclaveQueryExecutionException(string message, Exception innerException) : base(message, innerException) { } } - /// - /// Class encapsulating necessary information about the byte package that needs to be sent to the enclave - /// internal class EnclavePackage { public SqlEnclaveSession EnclaveSession { get; } public byte[] EnclavePackageBytes { get; } - /// - /// Constructor - /// - /// byte package to be sent to enclave - /// enclave session to be used internal EnclavePackage(byte[] enclavePackageBytes, SqlEnclaveSession enclaveSession) { EnclavePackageBytes = enclavePackageBytes; 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 40d6e0d87a..f1a2c83409 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 @@ -45,39 +45,33 @@ public sealed class SqlCommand : DbCommand, ICloneable private UpdateRowSource _updatedRowSource = UpdateRowSource.Both; private bool _designTimeInvisible; - /// - /// Indicates if the column encryption setting was set at-least once in the batch rpc mode, when using AddBatchCommand. - /// + + // Indicates if the column encryption setting was set at-least once in the batch rpc mode, when using AddBatchCommand. + private bool _wasBatchModeColumnEncryptionSettingSetOnce; - /// - /// Column Encryption Override. Defaults to SqlConnectionSetting, in which case - /// it will be Enabled if SqlConnectionOptions.IsColumnEncryptionSettingEnabled = true, Disabled if false. - /// This may also be used to set other behavior which overrides connection level setting. - /// + + // Column Encryption Override. Defaults to SqlConnectionSetting, in which case + // it will be Enabled if SqlConnectionOptions.IsColumnEncryptionSettingEnabled = true, Disabled if false. + // This may also be used to set other behavior which overrides connection level setting. + private SqlCommandColumnEncryptionSetting _columnEncryptionSetting = SqlCommandColumnEncryptionSetting.UseConnectionSetting; internal SqlDependency _sqlDep; #if DEBUG - /// - /// Force the client to sleep during sp_describe_parameter_encryption in the function TryFetchInputParameterEncryptionInfo. - /// + + // Force the client to sleep during sp_describe_parameter_encryption in the function TryFetchInputParameterEncryptionInfo. private static bool _sleepDuringTryFetchInputParameterEncryptionInfo = false; - /// - /// Force the client to sleep during sp_describe_parameter_encryption in the function RunExecuteReaderTds. - /// + + // Force the client to sleep during sp_describe_parameter_encryption in the function RunExecuteReaderTds. private static bool _sleepDuringRunExecuteReaderTdsForSpDescribeParameterEncryption = false; - /// - /// Force the client to sleep during sp_describe_parameter_encryption after ReadDescribeEncryptionParameterResults. - /// + // Force the client to sleep during sp_describe_parameter_encryption after ReadDescribeEncryptionParameterResults. private static bool _sleepAfterReadDescribeEncryptionParameterResults = false; - /// - /// Internal flag for testing purposes that forces all queries to internally end async calls. - /// + // Internal flag for testing purposes that forces all queries to internally end async calls. private static bool _forceInternalEndQuery = false; #endif @@ -140,12 +134,11 @@ internal bool InPrepare } } - /// - /// Return if column encryption setting is enabled. - /// The order in the below if is important since _activeConnection.Parser can throw if the - /// underlying tds connection is closed and we don't want to change the behavior for folks - /// not trying to use transparent parameter encryption i.e. who don't use (SqlCommandColumnEncryptionSetting.Enabled or _activeConnection.IsColumnEncryptionSettingEnabled) here. - /// + + // Return if column encryption setting is enabled. + // The order in the below if is important since _activeConnection.Parser can throw if the + // underlying tds connection is closed and we don't want to change the behavior for folks + // not trying to use transparent parameter encryption i.e. who don't use (SqlCommandColumnEncryptionSetting.Enabled or _activeConnection.IsColumnEncryptionSettingEnabled) here. internal bool IsColumnEncryptionEnabled { get @@ -296,21 +289,16 @@ private CachedAsyncState cachedAsyncState private List _parameterCollectionList; private int _currentlyExecutingBatch; - /// - /// This variable is used to keep track of which RPC batch's results are being read when reading the results of - /// describe parameter encryption RPC requests in BatchRPCMode. - /// + + // This variable is used to keep track of which RPC batch's results are being read when reading the results of + // describe parameter encryption RPC requests in BatchRPCMode. private int _currentlyExecutingDescribeParameterEncryptionRPC; - /// - /// A flag to indicate if we have in-progress describe parameter encryption RPC requests. - /// Reset to false when completed. - /// + // A flag to indicate if we have in-progress describe parameter encryption RPC requests. + // Reset to false when completed. private bool _isDescribeParameterEncryptionRPCCurrentlyInProgress; - /// - /// Return the flag that indicates if describe parameter encryption RPC requests are in-progress. - /// + // Return the flag that indicates if describe parameter encryption RPC requests are in-progress. internal bool IsDescribeParameterEncryptionRPCCurrentlyInProgress { get @@ -319,14 +307,10 @@ internal bool IsDescribeParameterEncryptionRPCCurrentlyInProgress } } - /// - /// A flag to indicate if EndExecute was already initiated by the Begin call. - /// + // A flag to indicate if EndExecute was already initiated by the Begin call. private volatile bool _internalEndExecuteInitiated; - /// - /// A flag to indicate whether we postponed caching the query metadata for this command. - /// + // A flag to indicate whether we postponed caching the query metadata for this command. internal bool CachingQueryMetadataPostponed { get; set; } // @@ -3844,11 +3828,9 @@ private void RunExecuteNonQuerySmi(bool sendToPipe) } } - /// - /// Resets the encryption related state of the command object and each of the parameters. - /// BatchRPC doesn't need special handling to cleanup the state of each RPC object and its parameters since a new RPC object and - /// parameters are generated on every execution. - /// + // Resets the encryption related state of the command object and each of the parameters. + // BatchRPC doesn't need special handling to cleanup the state of each RPC object and its parameters since a new RPC object and + // parameters are generated on every execution. private void ResetEncryptionState() { // First reset the command level state. @@ -3876,9 +3858,7 @@ private void ResetEncryptionState() enclaveAttestationParameters = null; } - /// - /// Steps to be executed in the Prepare Transparent Encryption finally block. - /// + // Steps to be executed in the Prepare Transparent Encryption finally block. private void PrepareTransparentEncryptionFinallyBlock(bool closeDataReader, bool clearDataStructures, bool decrementAsyncCount, @@ -3917,20 +3897,8 @@ private void PrepareTransparentEncryptionFinallyBlock(bool closeDataReader, } } - /// - /// Executes the reader after checking to see if we need to encrypt input parameters and then encrypting it if required. - /// TryFetchInputParameterEncryptionInfo() -> ReadDescribeEncryptionParameterResults()-> EncryptInputParameters() ->RunExecuteReaderTds() - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// + // Executes the reader after checking to see if we need to encrypt input parameters and then encrypting it if required. + // TryFetchInputParameterEncryptionInfo() -> ReadDescribeEncryptionParameterResults()-> EncryptInputParameters() ->RunExecuteReaderTds() private void PrepareForTransparentEncryption(CommandBehavior cmdBehavior, bool returnStream, bool async, int timeout, TaskCompletionSource completion, out Task returnTask, bool asyncWrite, out bool usedCache, bool inRetry) { // Fetch reader with input params @@ -4249,17 +4217,8 @@ private void PrepareForTransparentEncryption(CommandBehavior cmdBehavior, bool r } } - /// - /// Executes an RPC to fetch param encryption info from SQL Engine. If this method is not done writing - /// the request to wire, it'll set the "task" parameter which can be used to create continuations. - /// - /// - /// - /// - /// - /// - /// - /// + // Executes an RPC to fetch param encryption info from SQL Engine. If this method is not done writing + // the request to wire, it'll set the "task" parameter which can be used to create continuations. private SqlDataReader TryFetchInputParameterEncryptionInfo(int timeout, bool async, bool asyncWrite, @@ -4393,11 +4352,7 @@ private SqlDataReader TryFetchInputParameterEncryptionInfo(int timeout, } } - /// - /// Constructs a SqlParameter with a given string value - /// - /// - /// + // Constructs a SqlParameter with a given string value private SqlParameter GetSqlParameterWithQueryText(string queryText) { SqlParameter sqlParam = new SqlParameter(null, ((queryText.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT) ? SqlDbType.NVarChar : SqlDbType.NText, queryText.Length); @@ -4406,14 +4361,9 @@ private SqlParameter GetSqlParameterWithQueryText(string queryText) return sqlParam; } - /// - /// Constructs the sp_describe_parameter_encryption request with the values from the original RPC call. - /// Prototype for <sp_describe_parameter_encryption> is - /// exec sp_describe_parameter_encryption @tsql=N'[SQL Statement]', @params=N'@p1 varbinary(256)' - /// - /// - /// - /// + // Constructs the sp_describe_parameter_encryption request with the values from the original RPC call. + // Prototype for <sp_describe_parameter_encryption> is + // exec sp_describe_parameter_encryption @tsql=N'[SQL Statement]', @params=N'@p1 varbinary(256)' private void PrepareDescribeParameterEncryptionRequest(_SqlRPC originalRpcRequest, ref _SqlRPC describeParameterEncryptionRequest, byte[] attestationParameters = null) { Debug.Assert(originalRpcRequest != null); @@ -4524,11 +4474,7 @@ private void PrepareDescribeParameterEncryptionRequest(_SqlRPC originalRpcReques } } - /// - /// Read the output of sp_describe_parameter_encryption - /// - /// Resultset from calling to sp_describe_parameter_encryption - /// Readonly dictionary with the map of parameter encryption rpc requests with the corresponding original rpc requests. + // Read the output of sp_describe_parameter_encryption private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDictionary<_SqlRPC, _SqlRPC> describeParameterEncryptionRpcOriginalRpcMap) { _SqlRPC rpc = null; @@ -5027,21 +4973,7 @@ internal SqlDataReader RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior } } - /// - /// RunExecuteReaderTds after Transparent Parameter Encryption is complete. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// + // RunExecuteReaderTds after Transparent Parameter Encryption is complete. private SqlDataReader RunExecuteReaderTdsWithTransparentParameterEncryption(CommandBehavior cmdBehavior, RunBehavior runBehavior, bool returnStream, @@ -5925,10 +5857,8 @@ private void PutStateObject() } } - /// - /// IMPORTANT NOTE: This is created as a copy of OnDoneProc below for Transparent Column Encryption improvement - /// as there is not much time, to address regressions. Will revisit removing the duplication, when we have time again. - /// + // IMPORTANT NOTE: This is created as a copy of OnDoneProc below for Transparent Column Encryption improvement + // as there is not much time, to address regressions. Will revisit removing the duplication, when we have time again. internal void OnDoneDescribeParameterEncryptionProc(TdsParserStateObject stateObj) { // called per rpc batch complete @@ -5965,10 +5895,8 @@ internal void OnDoneDescribeParameterEncryptionProc(TdsParserStateObject stateOb } } - /// - /// IMPORTANT NOTE: There is a copy of this function above in OnDoneDescribeParameterEncryptionProc. - /// Please consider the changes being done in this function for the above function as well. - /// + // IMPORTANT NOTE: There is a copy of this function above in OnDoneDescribeParameterEncryptionProc. + // Please consider the changes being done in this function for the above function as well. internal void OnDoneProc() { // called per rpc batch complete if (BatchRPCMode) @@ -6663,14 +6591,9 @@ private void BuildExecuteSql(CommandBehavior behavior, string commandText, SqlPa } } - /// - /// This function constructs a string parameter containing the exec statement in the following format - /// N'EXEC sp_name @param1=@param1, @param1=@param2, ..., @paramN=@paramN' - /// TODO: Need to handle return values. - /// - /// Stored procedure name - /// SqlParameter list - /// A string SqlParameter containing the constructed sql statement value + // This function constructs a string parameter containing the exec statement in the following format + // N'EXEC sp_name @param1=@param1, @param1=@param2, ..., @paramN=@paramN' + // TODO: Need to handle return values. private SqlParameter BuildStoredProcedureStatementForColumnEncryption(string storedProcedureName, SqlParameter[] parameters) { Debug.Assert(CommandType == CommandType.StoredProcedure, "BuildStoredProcedureStatementForColumnEncryption() should only be called for stored procedures"); @@ -7083,10 +7006,8 @@ internal bool IsDirty } } - /// - /// Get or set the number of records affected by SpDescribeParameterEncryption. - /// The below line is used only for debug asserts and not exposed publicly or impacts functionality otherwise. - /// + // Get or set the number of records affected by SpDescribeParameterEncryption. + // The below line is used only for debug asserts and not exposed publicly or impacts functionality otherwise. internal int RowsAffectedByDescribeParameterEncryption { get @@ -7153,9 +7074,7 @@ internal bool BatchRPCMode } } - /// - /// Clear the state in sqlcommand related to describe parameter encryption RPC requests. - /// + // Clear the state in sqlcommand related to describe parameter encryption RPC requests. private void ClearDescribeParameterEncryptionRequests() { _sqlRPCParameterEncryptionReqArray = null; @@ -7179,10 +7098,8 @@ internal void ClearBatchCommand() _currentlyExecutingBatch = 0; } - /// - /// Set the column encryption setting to the new one. - /// Do not allow conflicting column encryption settings. - /// + // Set the column encryption setting to the new one. + // Do not allow conflicting column encryption settings. private void SetColumnEncryptionSetting(SqlCommandColumnEncryptionSetting newColumnEncryptionSetting) { if (!this._wasBatchModeColumnEncryptionSettingSetOnce) @@ -7460,12 +7377,7 @@ private void WriteBeginExecuteEvent() } } - /// - /// Writes and end execute event in Event Source. - /// - /// True if SQL command finished successfully, otherwise false. - /// Gets a number that identifies the type of error. - /// True if SQL command was executed synchronously, otherwise false. + // Writes and end execute event in Event Source. private void WriteEndExecuteEvent(bool success, int? sqlExceptionNumber, bool synchronous) { if (SqlEventSource.Log.IsEnabled()) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionString.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionString.cs index 9249f01995..3e9f4ff42b 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionString.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionString.cs @@ -971,10 +971,7 @@ internal SqlAuthenticationMethod ConvertValueToAuthenticationType() } } - /// - /// Convert the value to SqlConnectionColumnEncryptionSetting. - /// - /// + // Convert the value to SqlConnectionColumnEncryptionSetting. internal SqlConnectionColumnEncryptionSetting ConvertValueToColumnEncryptionSetting() { object value = base.Parsetable[KEY.ColumnEncryptionSetting]; 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 0866911220..8661bc70e7 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 @@ -1037,24 +1037,16 @@ public enum SqlCommandColumnEncryptionSetting Disabled, } - /// - /// To add include file for docs - /// + /// public enum SqlConnectionAttestationProtocol { - /// - /// If the attestation protocol is not specified. Use this as default value. - /// + /// NotSpecified = 0, - /// - /// Attestation portocol for Azure Attestation Service - /// + /// AAS = 1, - /// - /// Attestation protocol for Host Guardian Service - /// + /// HGS = 3 } diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/Utility.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/Utility.cs index 22779b63c2..f413244e1a 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/Utility.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/Utility.cs @@ -439,11 +439,6 @@ public override void WriteLine(string message) } } - /// - /// String to Byte array conversion. - /// - /// - /// internal static byte[] StringToByteArray(string hex) { Assert.True(!string.IsNullOrWhiteSpace(hex)); From 7cde9968c8913947f9a7bf0d0f855922eb36591c Mon Sep 17 00:00:00 2001 From: Karina Zhou Date: Wed, 30 Oct 2019 22:30:25 -0700 Subject: [PATCH 44/63] Fix according to comments and modify conversion tests --- .../Data/Common/DbConnectionStringCommon.cs | 17 +- .../SqlClient/EnclaveDelegate.NetCoreApp.cs | 8 +- .../SqlClient/EnclaveDelegate.NetStandard.cs | 2 +- .../Microsoft/Data/SqlClient/SqlCommand.cs | 2 +- .../Data/SqlClient/EnclaveDelegate.cs | 7 +- .../Microsoft/Data/SqlClient/SqlCommand.cs | 2 +- .../AlwaysEncrypted/ConversionTests.cs | 299 ++++++++++-------- .../AlwaysEncrypted/CspProviderExt.cs | 1 - 8 files changed, 184 insertions(+), 154 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs index 1857d76064..579bce6277 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs @@ -230,24 +230,20 @@ internal static string ColumnEncryptionSettingToString(SqlConnectionColumnEncryp /// internal static bool TryConvertToAttestationProtocol(string value, out SqlConnectionAttestationProtocol result) { - bool isSuccess = false; - if (StringComparer.InvariantCultureIgnoreCase.Equals(value, AttestationProtocolHGS)) { result = SqlConnectionAttestationProtocol.HGS; - isSuccess = true; + return true; } else if (StringComparer.InvariantCultureIgnoreCase.Equals(value, AttestationProtocolAAS)) { result = SqlConnectionAttestationProtocol.AAS; - isSuccess = true; + return true; } else - { - result = DbConnectionStringDefaults.AttestationProtocol; - } - return isSuccess; + result = DbConnectionStringDefaults.AttestationProtocol; + return false; } internal static bool IsValidAttestationProtocol(SqlConnectionAttestationProtocol value) @@ -286,11 +282,6 @@ internal static SqlConnectionAttestationProtocol ConvertToAttestationProtocol(st if (null != sValue) { - if (TryConvertToAttestationProtocol(sValue, out result)) - { - return result; - } - // try again after remove leading & trailing whitespaces. sValue = sValue.Trim(); if (TryConvertToAttestationProtocol(sValue, out result)) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetCoreApp.cs index 9a5cbf62a1..4de8b6c5e0 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetCoreApp.cs @@ -128,7 +128,7 @@ internal void InvalidateEnclaveSession(SqlConnectionAttestationProtocol attestat } - internal SqlEnclaveAttestationParameters GetAttestationParameters(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, string serverName, string enclaveAttestationUrl) + internal SqlEnclaveAttestationParameters GetAttestationParameters(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType) { SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = GetEnclaveProvider(attestationProtocol, enclaveType); return sqlColumnEncryptionEnclaveProvider.GetAttestationParameters(); @@ -188,16 +188,14 @@ internal void GetEnclaveSession(SqlConnectionAttestationProtocol attestationProt GetEnclaveSession(attestationProtocol, enclaveType, serverName, enclaveAttestationUrl, out sqlEnclaveSession, out counter, throwIfNull: false); } - // kz to move to netcoreapp private void GetEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, string serverName, string enclaveAttestationUrl, out SqlEnclaveSession sqlEnclaveSession, out long counter, bool throwIfNull) { SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = GetEnclaveProvider(attestationProtocol, enclaveType); sqlColumnEncryptionEnclaveProvider.GetEnclaveSession(serverName, enclaveAttestationUrl, out sqlEnclaveSession, out counter); - if (throwIfNull) + if (throwIfNull && sqlEnclaveSession == null) { - if (sqlEnclaveSession == null) - throw SQL.NullEnclaveSessionDuringQueryExecution(enclaveType, enclaveAttestationUrl); + throw SQL.NullEnclaveSessionDuringQueryExecution(enclaveType, enclaveAttestationUrl); } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetStandard.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetStandard.cs index 91f536637d..8d1ebc9fb8 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetStandard.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetStandard.cs @@ -48,7 +48,7 @@ internal void InvalidateEnclaveSession(SqlConnectionAttestationProtocol attestat throw new PlatformNotSupportedException(); } - internal SqlEnclaveAttestationParameters GetAttestationParameters(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, string serverName, string enclaveAttestationUrl) + internal SqlEnclaveAttestationParameters GetAttestationParameters(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType) { throw new PlatformNotSupportedException(); } 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 17eb88d565..4bb1f6c698 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 @@ -3357,7 +3357,7 @@ private SqlDataReader TryFetchInputParameterEncryptionInfo(int timeout, EnclaveDelegate.Instance.GetEnclaveSession(attestationProtocol, enclaveType, dataSource, enclaveAttestationUrl, out sqlEnclaveSession); if (sqlEnclaveSession == null) { - enclaveAttestationParameters = EnclaveDelegate.Instance.GetAttestationParameters(attestationProtocol, enclaveType, dataSource, enclaveAttestationUrl); + enclaveAttestationParameters = EnclaveDelegate.Instance.GetAttestationParameters(attestationProtocol, enclaveType); serializedAttestatationParameters = EnclaveDelegate.Instance.GetSerializedAttestationParameters(enclaveAttestationParameters, enclaveType); } } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs index 6e3d7024e8..de1c584f44 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs @@ -86,14 +86,13 @@ private void GetEnclaveSession(SqlConnectionAttestationProtocol attestationProto SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = GetEnclaveProvider(attestationProtocol, enclaveType); sqlColumnEncryptionEnclaveProvider.GetEnclaveSession(serverName, enclaveAttestationUrl, out sqlEnclaveSession, out counter); - if (throwIfNull) + if (throwIfNull && sqlEnclaveSession == null) { - if (sqlEnclaveSession == null) - throw SQL.NullEnclaveSessionDuringQueryExecution(enclaveType, enclaveAttestationUrl); + throw SQL.NullEnclaveSessionDuringQueryExecution(enclaveType, enclaveAttestationUrl); } } - internal SqlEnclaveAttestationParameters GetAttestationParameters(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, string serverName, string enclaveAttestationUrl) + internal SqlEnclaveAttestationParameters GetAttestationParameters(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType) { SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = GetEnclaveProvider(attestationProtocol, enclaveType); return sqlColumnEncryptionEnclaveProvider.GetAttestationParameters(); 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 40d6e0d87a..3f0c3d8cdc 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 @@ -4282,7 +4282,7 @@ private SqlDataReader TryFetchInputParameterEncryptionInfo(int timeout, EnclaveDelegate.Instance.GetEnclaveSession(attestationProtocol, enclaveType, dataSource, enclaveAttestationUrl, out sqlEnclaveSession); if (sqlEnclaveSession == null) { - this.enclaveAttestationParameters = EnclaveDelegate.Instance.GetAttestationParameters(attestationProtocol, enclaveType, dataSource, enclaveAttestationUrl); + this.enclaveAttestationParameters = EnclaveDelegate.Instance.GetAttestationParameters(attestationProtocol, enclaveType); serializedAttestatationParameters = EnclaveDelegate.Instance.GetSerializedAttestationParameters(this.enclaveAttestationParameters, enclaveType); } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ConversionTests.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ConversionTests.cs index d761d6fc8c..806a849b5e 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ConversionTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ConversionTests.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information.using System; using System; +using System.Collections; using System.Collections.Generic; using System.Data; using System.Data.SqlTypes; @@ -63,43 +64,19 @@ public ConversionTests() certStoreProvider); databaseObjects.Add(columnEncryptionKey); - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionString)) + foreach(string connectionStr in DataTestUtility.AEConnStringsSetup) { - sqlConnection.Open(); - databaseObjects.ForEach(o => o.Create(sqlConnection)); + using (SqlConnection sqlConnection = new SqlConnection(connectionStr)) + { + sqlConnection.Open(); + databaseObjects.ForEach(o => o.Create(sqlConnection)); + } } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [InlineData(SqlDbType.SmallMoney, SqlDbType.Money)] - [InlineData(SqlDbType.Bit, SqlDbType.TinyInt)] - [InlineData(SqlDbType.Bit, SqlDbType.SmallInt)] - [InlineData(SqlDbType.Bit, SqlDbType.Int)] - [InlineData(SqlDbType.Bit, SqlDbType.BigInt)] - [InlineData(SqlDbType.TinyInt, SqlDbType.SmallInt)] - [InlineData(SqlDbType.TinyInt, SqlDbType.Int)] - [InlineData(SqlDbType.TinyInt, SqlDbType.BigInt)] - [InlineData(SqlDbType.SmallInt, SqlDbType.Int)] - [InlineData(SqlDbType.SmallInt, SqlDbType.BigInt)] - [InlineData(SqlDbType.Int, SqlDbType.BigInt)] - [InlineData(SqlDbType.Binary, SqlDbType.Binary)] - [InlineData(SqlDbType.Binary, SqlDbType.VarBinary)] - [InlineData(SqlDbType.VarBinary, SqlDbType.Binary)] - [InlineData(SqlDbType.VarBinary, SqlDbType.VarBinary)] - [InlineData(SqlDbType.Char, SqlDbType.Char)] - [InlineData(SqlDbType.Char, SqlDbType.VarChar)] // padding whitespace issue, trimEnd for now - [InlineData(SqlDbType.VarChar, SqlDbType.Char)] - [InlineData(SqlDbType.VarChar, SqlDbType.VarChar)] - [InlineData(SqlDbType.NChar, SqlDbType.NChar)] - [InlineData(SqlDbType.NChar, SqlDbType.NVarChar)] - [InlineData(SqlDbType.NVarChar, SqlDbType.NChar)] - [InlineData(SqlDbType.NVarChar, SqlDbType.NVarChar)] - [InlineData(SqlDbType.Time, SqlDbType.Time)] - [InlineData(SqlDbType.DateTime2, SqlDbType.DateTime2)] - [InlineData(SqlDbType.DateTimeOffset, SqlDbType.DateTimeOffset)] - [InlineData(SqlDbType.Float, SqlDbType.Float)] - [InlineData(SqlDbType.Real, SqlDbType.Real)] - public void ConversionSmallerToLargerInsertAndSelect(SqlDbType smallDbType, SqlDbType largeDbType) + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ClassData(typeof(ConversionSmallerToLargerInsertAndSelectData))] + public void ConversionSmallerToLargerInsertAndSelect(string connString, SqlDbType smallDbType, SqlDbType largeDbType) { ColumnMetaData largeColumnInfo = new ColumnMetaData(largeDbType, 0, 1, 1, false); ColumnMetaData smallColumnInfo = new ColumnMetaData(smallDbType, 0, 1, 1, false); @@ -112,21 +89,21 @@ public void ConversionSmallerToLargerInsertAndSelect(SqlDbType smallDbType, SqlD string unencryptedTableName = DatabaseHelper.GenerateUniqueName("unencrypted"); // Create the encrypted and unencrypted table with the proper column types. - CreateTable(largeColumnInfo, encryptedTableName, isEncrypted: true); - CreateTable(largeColumnInfo, unencryptedTableName, isEncrypted: false); + CreateTable(connString, largeColumnInfo, encryptedTableName, isEncrypted: true); + CreateTable(connString, largeColumnInfo, unencryptedTableName, isEncrypted: false); // Insert data using the smaller type to the tables with the large type. - object[] rawValues = PopulateTablesAndReturnRandomValue(encryptedTableName, unencryptedTableName, smallColumnInfo); + object[] rawValues = PopulateTablesAndReturnRandomValue(connString, encryptedTableName, unencryptedTableName, smallColumnInfo); // Keep the values from unencryptedTable other than the rawValues to perform a select later for DateTime2 and DateTimeOffset. - object[] valuesToSelect = RetriveDataFromDatabase(unencryptedTableName); + object[] valuesToSelect = RetriveDataFromDatabase(connString, unencryptedTableName); // Now read back everything and make sure the values and types are identical. - CompareTables(encryptedTableName, unencryptedTableName); + CompareTables(connString, encryptedTableName, unencryptedTableName); // Now send a query with a predicate using the larger type and confirm that the row that was inserted with the smaller type can still be found. - using (SqlConnection sqlConnectionEncrypted = new SqlConnection(DataTestUtility.TCPConnectionString)) - using (SqlConnection sqlConnectionUnencrypted = new SqlConnection(DataTestUtility.TCPConnectionString)) + using (SqlConnection sqlConnectionEncrypted = new SqlConnection(connString)) + using (SqlConnection sqlConnectionUnencrypted = new SqlConnection(connString)) { sqlConnectionEncrypted.Open(); sqlConnectionUnencrypted.Open(); @@ -186,36 +163,9 @@ public void ConversionSmallerToLargerInsertAndSelect(SqlDbType smallDbType, SqlD } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [InlineData(SqlDbType.SmallMoney, SqlDbType.Money)] - [InlineData(SqlDbType.Bit, SqlDbType.TinyInt)] - [InlineData(SqlDbType.Bit, SqlDbType.SmallInt)] - [InlineData(SqlDbType.Bit, SqlDbType.Int)] - [InlineData(SqlDbType.Bit, SqlDbType.BigInt)] - [InlineData(SqlDbType.TinyInt, SqlDbType.SmallInt)] - [InlineData(SqlDbType.TinyInt, SqlDbType.Int)] - [InlineData(SqlDbType.TinyInt, SqlDbType.BigInt)] - [InlineData(SqlDbType.SmallInt, SqlDbType.Int)] - [InlineData(SqlDbType.SmallInt, SqlDbType.BigInt)] - [InlineData(SqlDbType.Int, SqlDbType.BigInt)] - [InlineData(SqlDbType.Binary, SqlDbType.Binary)] - [InlineData(SqlDbType.Binary, SqlDbType.VarBinary)] - [InlineData(SqlDbType.VarBinary, SqlDbType.Binary)] - [InlineData(SqlDbType.VarBinary, SqlDbType.VarBinary)] - [InlineData(SqlDbType.Char, SqlDbType.Char)] // padding whitespace issue - [InlineData(SqlDbType.Char, SqlDbType.VarChar)] // padding whitespace issue - [InlineData(SqlDbType.VarChar, SqlDbType.Char)] - [InlineData(SqlDbType.VarChar, SqlDbType.VarChar)] - [InlineData(SqlDbType.NChar, SqlDbType.NChar)] - [InlineData(SqlDbType.NChar, SqlDbType.NVarChar)] - [InlineData(SqlDbType.NVarChar, SqlDbType.NChar)] - [InlineData(SqlDbType.NVarChar, SqlDbType.NVarChar)] - [InlineData(SqlDbType.Time, SqlDbType.Time)] - [InlineData(SqlDbType.DateTime2, SqlDbType.DateTime2)] - [InlineData(SqlDbType.DateTimeOffset, SqlDbType.DateTimeOffset)] - [InlineData(SqlDbType.Float, SqlDbType.Float)] - [InlineData(SqlDbType.Real, SqlDbType.Real)] - public void ConversionSmallerToLargerInsertAndSelectBulk(SqlDbType smallDbType, SqlDbType largeDbType) + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ClassData(typeof(ConversionSmallerToLargerInsertAndSelectBulkData))] + public void ConversionSmallerToLargerInsertAndSelectBulk(string connString, SqlDbType smallDbType, SqlDbType largeDbType) { ColumnMetaData largeColumnInfo = new ColumnMetaData(largeDbType, 0, 1, 1, false); ColumnMetaData smallColumnInfo = new ColumnMetaData(smallDbType, 0, 1, 1, false); @@ -228,27 +178,27 @@ public void ConversionSmallerToLargerInsertAndSelectBulk(SqlDbType smallDbType, string witnessTableName = DatabaseHelper.GenerateUniqueName("large_type_pt"); // Create the encrypted and unencrypted table with the proper column types. - CreateTable(smallColumnInfo, originTableName, isEncrypted: false); - CreateTable(largeColumnInfo, targetTableName, isEncrypted: true); - CreateTable(largeColumnInfo, witnessTableName, isEncrypted: false); + CreateTable(connString, smallColumnInfo, originTableName, isEncrypted: false); + CreateTable(connString, largeColumnInfo, targetTableName, isEncrypted: true); + CreateTable(connString, largeColumnInfo, witnessTableName, isEncrypted: false); // Insert data using the smaller type to the tables with the large type. // Also keep the values on the side to perform a select later. - object[] rawValues = PopulateTablesAndReturnRandomValuePlaintextOnly(originTableName, smallColumnInfo); + object[] rawValues = PopulateTablesAndReturnRandomValuePlaintextOnly(connString, originTableName, smallColumnInfo); // Keep the values from originTable other than the rawValues to perform a select later for DateTime2 and DateTimeOffset. - object[] valuesToSelect = RetriveDataFromDatabase(originTableName); + object[] valuesToSelect = RetriveDataFromDatabase(connString, originTableName); // populate the witness table & the target table using bulk insert - portDataToTablePairViaBulkCopy(originTableName, SqlConnectionColumnEncryptionSetting.Disabled, targetTableName, SqlConnectionColumnEncryptionSetting.Enabled); - portDataToTablePairViaBulkCopy(originTableName, SqlConnectionColumnEncryptionSetting.Disabled, witnessTableName, SqlConnectionColumnEncryptionSetting.Disabled); + portDataToTablePairViaBulkCopy(connString, originTableName, SqlConnectionColumnEncryptionSetting.Disabled, targetTableName, SqlConnectionColumnEncryptionSetting.Enabled); + portDataToTablePairViaBulkCopy(connString, originTableName, SqlConnectionColumnEncryptionSetting.Disabled, witnessTableName, SqlConnectionColumnEncryptionSetting.Disabled); // Now read back everything and make sure the values and types are identical. - CompareTables(targetTableName, witnessTableName); + CompareTables(connString, targetTableName, witnessTableName); // Now send a query with a predicate using the larger type and confirm that the row that was inserted with the smaller type can still be found. - using (SqlConnection sqlConnectionEncrypted = new SqlConnection(DataTestUtility.TCPConnectionString)) - using (SqlConnection sqlConnectionUnencrypted = new SqlConnection(DataTestUtility.TCPConnectionString)) + using (SqlConnection sqlConnectionEncrypted = new SqlConnection(connString)) + using (SqlConnection sqlConnectionUnencrypted = new SqlConnection(connString)) { sqlConnectionEncrypted.Open(); sqlConnectionUnencrypted.Open(); @@ -312,31 +262,9 @@ smallColumnInfo.ColumnType is SqlDbType.Char || } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - [InlineData(SqlDbType.BigInt)] - [InlineData(SqlDbType.Binary)] - [InlineData(SqlDbType.Bit)] - [InlineData(SqlDbType.Char)] - [InlineData(SqlDbType.Date)] - [InlineData(SqlDbType.DateTime)] - [InlineData(SqlDbType.DateTime2)] - [InlineData(SqlDbType.DateTimeOffset)] - [InlineData(SqlDbType.Decimal)] - [InlineData(SqlDbType.Float)] - [InlineData(SqlDbType.Int)] - [InlineData(SqlDbType.Money)] - [InlineData(SqlDbType.NChar)] - [InlineData(SqlDbType.NVarChar)] - [InlineData(SqlDbType.Real)] - [InlineData(SqlDbType.SmallDateTime)] - [InlineData(SqlDbType.SmallInt)] - [InlineData(SqlDbType.SmallMoney)] - [InlineData(SqlDbType.Time)] - [InlineData(SqlDbType.TinyInt)] - [InlineData(SqlDbType.UniqueIdentifier)] - [InlineData(SqlDbType.VarBinary)] - [InlineData(SqlDbType.VarChar)] - public void TestOutOfRangeValues(SqlDbType currentDbType) + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ClassData(typeof(TestOutOfRangeValuesData))] + public void TestOutOfRangeValues(string connString, SqlDbType currentDbType) { ColumnMetaData currentColumnInfo = new ColumnMetaData(currentDbType, 0, 1, 1, false); ColumnMetaData dummyColumnInfo = null; @@ -349,15 +277,15 @@ public void TestOutOfRangeValues(SqlDbType currentDbType) string unencryptedTableName = DatabaseHelper.GenerateUniqueName("unencrypted"); // Create the encrypted and unencrypted table with the proper column types. - CreateTable(currentColumnInfo, encryptedTableName, isEncrypted: true); - CreateTable(currentColumnInfo, unencryptedTableName, isEncrypted: false); + CreateTable(connString, currentColumnInfo, encryptedTableName, isEncrypted: true); + CreateTable(connString, currentColumnInfo, unencryptedTableName, isEncrypted: false); // Generate a list of out of range values, indicating which should fail and which shouldn't. List valueList = GenerateOutOfRangeValuesForType(currentDbType, currentColumnInfo.ColumnSize, currentColumnInfo.Precision, currentColumnInfo.Scale); Assert.True(valueList.Count != 0, "Test bug, the list is empty!"); - using (SqlConnection sqlConnectionEncrypted = new SqlConnection(DataTestUtility.TCPConnectionString)) - using (SqlConnection sqlConnectionUnencrypted = new SqlConnection(DataTestUtility.TCPConnectionString)) + using (SqlConnection sqlConnectionEncrypted = new SqlConnection(connString)) + using (SqlConnection sqlConnectionUnencrypted = new SqlConnection(connString)) { sqlConnectionEncrypted.Open(); sqlConnectionUnencrypted.Open(); @@ -393,7 +321,7 @@ public void TestOutOfRangeValues(SqlDbType currentDbType) } - CompareTables(encryptedTableName, unencryptedTableName); + CompareTables(connString, encryptedTableName, unencryptedTableName); } finally { @@ -807,11 +735,11 @@ private bool TypeHasPrecision(SqlDbType type) /// /// /// - private object[] PopulateTablesAndReturnRandomValue(string encryptedTableName, string unencryptedTableName, ColumnMetaData columnInfo) + private object[] PopulateTablesAndReturnRandomValue(string connString, string encryptedTableName, string unencryptedTableName, ColumnMetaData columnInfo) { object[] valueArray = new object[NumberOfRows]; - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionString)) + using (SqlConnection sqlConnection = new SqlConnection(connString)) { sqlConnection.Open(); @@ -856,11 +784,11 @@ private object[] PopulateTablesAndReturnRandomValue(string encryptedTableName, s /// /// /// - private object[] PopulateTablesAndReturnRandomValuePlaintextOnly(string unencryptedTableName, ColumnMetaData columnInfo) + private object[] PopulateTablesAndReturnRandomValuePlaintextOnly(string connSting, string unencryptedTableName, ColumnMetaData columnInfo) { object[] valueArray = new object[NumberOfRows]; - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionString)) + using (SqlConnection sqlConnection = new SqlConnection(connSting)) { sqlConnection.Open(); @@ -893,9 +821,9 @@ private object[] PopulateTablesAndReturnRandomValuePlaintextOnly(string unencryp /// /// /// - private void portDataToTablePairViaBulkCopy(string sourceName, SqlConnectionColumnEncryptionSetting sourceConnectionFlag, string targetName, SqlConnectionColumnEncryptionSetting targetConnectionFlag) + private void portDataToTablePairViaBulkCopy(string connString, string sourceName, SqlConnectionColumnEncryptionSetting sourceConnectionFlag, string targetName, SqlConnectionColumnEncryptionSetting targetConnectionFlag) { - SqlConnectionStringBuilder strbld = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString); + SqlConnectionStringBuilder strbld = new SqlConnectionStringBuilder(connString); strbld.ColumnEncryptionSetting = sourceConnectionFlag; using (SqlConnection sourceConnection = new SqlConnection(strbld.ToString())) @@ -934,12 +862,12 @@ private void portDataToTablePairViaBulkCopy(string sourceName, SqlConnectionColu /// /// /// - private object[] RetriveDataFromDatabase(string unencryptedTableName) + private object[] RetriveDataFromDatabase(string connString, string unencryptedTableName) { object[] valueArray = new object[NumberOfRows]; int index = 0; - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionString)) + using (SqlConnection sqlConnection = new SqlConnection(connString)) { sqlConnection.Open(); @@ -968,10 +896,10 @@ private object[] RetriveDataFromDatabase(string unencryptedTableName) /// /// /// - private void CompareTables(string encryptedTableName, string unencryptedTableName) + private void CompareTables(string connString, string encryptedTableName, string unencryptedTableName) { - using (SqlConnection sqlConnectionEncrypted = new SqlConnection(DataTestUtility.TCPConnectionString)) - using (SqlConnection sqlConnectionUnencrypted = new SqlConnection(DataTestUtility.TCPConnectionString)) + using (SqlConnection sqlConnectionEncrypted = new SqlConnection(connString)) + using (SqlConnection sqlConnectionUnencrypted = new SqlConnection(connString)) { sqlConnectionEncrypted.Open(); sqlConnectionUnencrypted.Open(); @@ -1243,7 +1171,7 @@ private object GenerateRandomValue(ColumnMetaData columnInfo) /// /// /// - private void CreateTable(ColumnMetaData columnMeta, string tableName, bool isEncrypted) + private void CreateTable(string connString, ColumnMetaData columnMeta, string tableName, bool isEncrypted) { string columnType = columnMeta.ColumnType.ToString().ToLower(); string columnInfo = ""; @@ -1345,13 +1273,14 @@ private void CreateTable(ColumnMetaData columnMeta, string tableName, bool isEnc } string sql; + string encryptionType = DataTestUtility.EnclaveEnabled ? "RANDOMIZED" : "DETERMINISTIC"; if (isEncrypted) { sql = $@"CREATE TABLE [dbo].[{tableName}] ( [{IdentityColumnName}] int IDENTITY(1,1), - [{FirstColumnName}] {columnInfo} ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{columnEncryptionKey.Name}], ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = '{ColumnEncryptionAlgorithmName}'), + [{FirstColumnName}] {columnInfo} ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{columnEncryptionKey.Name}], ENCRYPTION_TYPE = {encryptionType}, ALGORITHM = '{ColumnEncryptionAlgorithmName}'), )"; } else @@ -1363,7 +1292,7 @@ private void CreateTable(ColumnMetaData columnMeta, string tableName, bool isEnc )"; } - using (SqlConnection sqlConn = new SqlConnection(DataTestUtility.TCPConnectionString)) + using (SqlConnection sqlConn = new SqlConnection(connString)) { sqlConn.Open(); @@ -1411,19 +1340,133 @@ private void SetParamSizeScalePrecision(ref SqlParameter param, ColumnMetaData c } } - public void Dispose() { databaseObjects.Reverse(); - using (SqlConnection sqlConnection = new SqlConnection(DataTestUtility.TCPConnectionString)) + foreach(string connectionStr in DataTestUtility.AEConnStringsSetup) { - sqlConnection.Open(); - databaseObjects.ForEach(o => o.Drop(sqlConnection)); + using (SqlConnection sqlConnection = new SqlConnection(connectionStr)) + { + sqlConnection.Open(); + databaseObjects.ForEach(o => o.Drop(sqlConnection)); + } } } + } + public class ConversionSmallerToLargerInsertAndSelectData : IEnumerable + { + public IEnumerator GetEnumerator() + { + foreach (string connStrAE in DataTestUtility.AEConnStrings) + { + yield return new object[] { connStrAE, SqlDbType.SmallMoney, SqlDbType.Money }; + yield return new object[] { connStrAE, SqlDbType.Bit, SqlDbType.TinyInt }; + yield return new object[] { connStrAE, SqlDbType.Bit, SqlDbType.SmallInt }; + yield return new object[] { connStrAE, SqlDbType.Bit, SqlDbType.Int }; + yield return new object[] { connStrAE, SqlDbType.Bit, SqlDbType.BigInt }; + yield return new object[] { connStrAE, SqlDbType.TinyInt, SqlDbType.SmallInt }; + yield return new object[] { connStrAE, SqlDbType.TinyInt, SqlDbType.Int }; + yield return new object[] { connStrAE, SqlDbType.TinyInt, SqlDbType.BigInt }; + yield return new object[] { connStrAE, SqlDbType.SmallInt, SqlDbType.Int }; + yield return new object[] { connStrAE, SqlDbType.SmallInt, SqlDbType.BigInt }; + yield return new object[] { connStrAE, SqlDbType.Int, SqlDbType.BigInt }; + yield return new object[] { connStrAE, SqlDbType.Binary, SqlDbType.Binary }; + yield return new object[] { connStrAE, SqlDbType.Binary, SqlDbType.VarBinary }; + yield return new object[] { connStrAE, SqlDbType.VarBinary, SqlDbType.Binary }; + yield return new object[] { connStrAE, SqlDbType.VarBinary, SqlDbType.VarBinary }; + yield return new object[] { connStrAE, SqlDbType.Char, SqlDbType.Char }; + yield return new object[] { connStrAE, SqlDbType.Char, SqlDbType.VarChar }; // padding whitespace issue, trimEnd for now + yield return new object[] { connStrAE, SqlDbType.VarChar, SqlDbType.Char }; + yield return new object[] { connStrAE, SqlDbType.VarChar, SqlDbType.VarChar }; + yield return new object[] { connStrAE, SqlDbType.NChar, SqlDbType.NChar }; + yield return new object[] { connStrAE, SqlDbType.NChar, SqlDbType.NVarChar }; + yield return new object[] { connStrAE, SqlDbType.NVarChar, SqlDbType.NChar }; + yield return new object[] { connStrAE, SqlDbType.NVarChar, SqlDbType.NVarChar }; + yield return new object[] { connStrAE, SqlDbType.Time, SqlDbType.Time }; + yield return new object[] { connStrAE, SqlDbType.DateTime2, SqlDbType.DateTime2 }; + yield return new object[] { connStrAE, SqlDbType.DateTimeOffset, SqlDbType.DateTimeOffset }; + yield return new object[] { connStrAE, SqlDbType.Float, SqlDbType.Float }; + yield return new object[] { connStrAE, SqlDbType.Real, SqlDbType.Real }; + } + } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + public class ConversionSmallerToLargerInsertAndSelectBulkData : IEnumerable + { + public IEnumerator GetEnumerator() + { + foreach (string connStrAE in DataTestUtility.AEConnStrings) + { + yield return new object[] { connStrAE, SqlDbType.SmallMoney, SqlDbType.Money }; + yield return new object[] { connStrAE, SqlDbType.Bit, SqlDbType.TinyInt }; + yield return new object[] { connStrAE, SqlDbType.Bit, SqlDbType.SmallInt }; + yield return new object[] { connStrAE, SqlDbType.Bit, SqlDbType.Int }; + yield return new object[] { connStrAE, SqlDbType.Bit, SqlDbType.BigInt }; + yield return new object[] { connStrAE, SqlDbType.TinyInt, SqlDbType.SmallInt }; + yield return new object[] { connStrAE, SqlDbType.TinyInt, SqlDbType.Int }; + yield return new object[] { connStrAE, SqlDbType.TinyInt, SqlDbType.BigInt }; + yield return new object[] { connStrAE, SqlDbType.SmallInt, SqlDbType.Int }; + yield return new object[] { connStrAE, SqlDbType.SmallInt, SqlDbType.BigInt }; + yield return new object[] { connStrAE, SqlDbType.Int, SqlDbType.BigInt }; + yield return new object[] { connStrAE, SqlDbType.Binary, SqlDbType.Binary }; + yield return new object[] { connStrAE, SqlDbType.Binary, SqlDbType.VarBinary }; + yield return new object[] { connStrAE, SqlDbType.VarBinary, SqlDbType.Binary }; + yield return new object[] { connStrAE, SqlDbType.VarBinary, SqlDbType.VarBinary }; + yield return new object[] { connStrAE, SqlDbType.Char, SqlDbType.Char }; // padding whitespace issue + yield return new object[] { connStrAE, SqlDbType.Char, SqlDbType.VarChar }; // padding whitespace issue + yield return new object[] { connStrAE, SqlDbType.VarChar, SqlDbType.Char }; + yield return new object[] { connStrAE, SqlDbType.VarChar, SqlDbType.VarChar }; + yield return new object[] { connStrAE, SqlDbType.NChar, SqlDbType.NChar }; + yield return new object[] { connStrAE, SqlDbType.NChar, SqlDbType.NVarChar }; + yield return new object[] { connStrAE, SqlDbType.NVarChar, SqlDbType.NChar }; + yield return new object[] { connStrAE, SqlDbType.NVarChar, SqlDbType.NVarChar }; + yield return new object[] { connStrAE, SqlDbType.Time, SqlDbType.Time }; + yield return new object[] { connStrAE, SqlDbType.DateTime2, SqlDbType.DateTime2 }; + yield return new object[] { connStrAE, SqlDbType.DateTimeOffset, SqlDbType.DateTimeOffset }; + yield return new object[] { connStrAE, SqlDbType.Float, SqlDbType.Float }; + yield return new object[] { connStrAE, SqlDbType.Real, SqlDbType.Real}; + } + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + + + public class TestOutOfRangeValuesData : IEnumerable + { + public IEnumerator GetEnumerator() + { + foreach (string connStrAE in DataTestUtility.AEConnStrings) + { + yield return new object[] { connStrAE, SqlDbType.BigInt }; + yield return new object[] { connStrAE, SqlDbType.Binary }; + yield return new object[] { connStrAE, SqlDbType.Bit }; + yield return new object[] { connStrAE, SqlDbType.Char }; + yield return new object[] { connStrAE, SqlDbType.Date }; + yield return new object[] { connStrAE, SqlDbType.DateTime }; + yield return new object[] { connStrAE, SqlDbType.DateTime2 }; + yield return new object[] { connStrAE, SqlDbType.DateTimeOffset }; + yield return new object[] { connStrAE, SqlDbType.Decimal }; + yield return new object[] { connStrAE, SqlDbType.Float }; + yield return new object[] { connStrAE, SqlDbType.Int }; + yield return new object[] { connStrAE, SqlDbType.Money }; + yield return new object[] { connStrAE, SqlDbType.NChar }; + yield return new object[] { connStrAE, SqlDbType.NVarChar }; + yield return new object[] { connStrAE, SqlDbType.Real }; + yield return new object[] { connStrAE, SqlDbType.SmallDateTime }; + yield return new object[] { connStrAE, SqlDbType.SmallInt }; + yield return new object[] { connStrAE, SqlDbType.SmallMoney }; + yield return new object[] { connStrAE, SqlDbType.Time }; + yield return new object[] { connStrAE, SqlDbType.TinyInt }; + yield return new object[] { connStrAE, SqlDbType.UniqueIdentifier }; + yield return new object[] { connStrAE, SqlDbType.VarBinary }; + yield return new object[] { connStrAE, SqlDbType.VarChar }; + } + } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/CspProviderExt.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/CspProviderExt.cs index 64802500aa..27c629e7de 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/CspProviderExt.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/CspProviderExt.cs @@ -17,7 +17,6 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted [PlatformSpecific(TestPlatforms.Windows)] public class CspProviderExt { - // [Fact(Skip="Run this in non-parallel mode")] or [ConditionalFact()] [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] [ClassData(typeof(AEConnectionStringProvider))] public void TestKeysFromCertificatesCreatedWithMultipleCryptoProviders(string connectionString) From 557e335f8474cde0bca021fcb86b1d7aa3025144 Mon Sep 17 00:00:00 2001 From: v-jizho2 Date: Thu, 31 Oct 2019 12:01:32 -0700 Subject: [PATCH 45/63] Update src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx Co-Authored-By: Yuki Wong --- src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx index 3cbf99b3c8..12526b32cc 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx @@ -1774,7 +1774,7 @@ Globalization Invariant Mode is not supported. - The validation of an attestation token failed. The token signature does not match the signature omputed using a public key retrieved from the attestation public key endpoint at '{0}'. Verify the DNS apping for the endpoint. If correct, contact Customer Support Services. + The validation of an attestation token failed. The token signature does not match the signature computed using a public key retrieved from the attestation public key endpoint at '{0}'. Verify the DNS mapping for the endpoint. If correct, contact Customer Support Services. Internal error occurred when retrying the download of the HGS root certificate after the initial request failed. Contact Customer Support Services. @@ -1848,4 +1848,4 @@ Error occured when generating enclave package. Attestation Protocol has not been specified in the connection string, but the query requires enclave computations. - \ No newline at end of file + From 571762ce3fa65db0ded17ab9a48d25d6446beb8c Mon Sep 17 00:00:00 2001 From: Karina Zhou Date: Thu, 31 Oct 2019 12:36:46 -0700 Subject: [PATCH 46/63] Resolve partial comments --- .../Data/Common/DbConnectionStringCommon.cs | 2 +- .../netcore/src/Resources/SR.resx | 4 +- .../netfx/src/Microsoft.Data.SqlClient.csproj | 37 +++++-------------- .../Data/Common/DbConnectionStringCommon.cs | 2 +- .../netfx/src/Resources/Strings.Designer.cs | 4 +- 5 files changed, 16 insertions(+), 33 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs index 7a38b916a4..c5205a1eda 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs @@ -601,7 +601,7 @@ internal static partial class DbConnectionStringDefaults internal static readonly SqlAuthenticationMethod Authentication = SqlAuthenticationMethod.NotSpecified; internal const SqlConnectionColumnEncryptionSetting ColumnEncryptionSetting = SqlConnectionColumnEncryptionSetting.Disabled; internal const string EnclaveAttestationUrl = ""; - internal static readonly SqlConnectionAttestationProtocol AttestationProtocol = SqlConnectionAttestationProtocol.NotSpecified; + internal const SqlConnectionAttestationProtocol AttestationProtocol = SqlConnectionAttestationProtocol.NotSpecified; } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx index 3cbf99b3c8..f40fc6129b 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx @@ -292,7 +292,7 @@ {0} requires the command to have a transaction when the connection assigned to the command is in a pending local transaction. The Transaction property of the command has not been initialized. - There is already an open DataReader associated with this Command which must be closed first. + There is already an open DataReader associated with this {0} which must be closed first. The method '{0}' cannot be called more than once for the same execution. @@ -631,7 +631,7 @@ The given ColumnName '{0}' does not match up with any column in data source. - String or binary data would be truncated. + String or binary data would be truncated in table '{0}', column '{1}'. Truncated value: '{2}'. Timeout Value '{0}' is less than 0. 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 976bac41c5..f6571605f5 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -158,16 +158,10 @@ - - Component - - - Component - + + - - Component - + @@ -176,9 +170,7 @@ - - Component - + @@ -260,9 +252,7 @@ - - Component - + @@ -321,17 +311,16 @@ - + True True - Strings.resx + $(ResxFileName).resx - + System ResXFileCodeGenerator - Strings.Designer.cs - Designer + $(ResxFileName).Designer.cs @@ -350,19 +339,13 @@ True - - - - 1.0.19235.1 + 1.1.0 3.0.8 - - 5.5.0 - 5.5.0 diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionStringCommon.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionStringCommon.cs index cb33b13cbd..2bd0ee1489 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionStringCommon.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionStringCommon.cs @@ -1011,7 +1011,7 @@ internal static class DbConnectionStringDefaults internal static readonly SqlAuthenticationMethod Authentication = SqlAuthenticationMethod.NotSpecified; internal static readonly SqlConnectionColumnEncryptionSetting ColumnEncryptionSetting = SqlConnectionColumnEncryptionSetting.Disabled; internal const string EnclaveAttestationUrl = ""; - internal static readonly SqlConnectionAttestationProtocol AttestationProtocol = SqlConnectionAttestationProtocol.NotSpecified; + internal const SqlConnectionAttestationProtocol AttestationProtocol = SqlConnectionAttestationProtocol.NotSpecified; internal const string Certificate = ""; internal const PoolBlockingPeriod PoolBlockingPeriod = SqlClient.PoolBlockingPeriod.Auto; } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs index 42908c4ba7..7013e800c6 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs @@ -19,7 +19,7 @@ namespace System { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Strings { @@ -7777,7 +7777,7 @@ internal static string OleDb_PropertyBadOption { /// Looks up a localized string similar to Failed to initialize the '{0}' property for one of the following reasons: /// The value data type was not the data type of the property or was not null. For example, the property was DBPROP_MEMORYUSAGE, which has a data type of Int32, and the data type was Int64. /// The value was not a valid value. For example, the property was DBPROP_MEMORYUSAGE and the value was negative. - /// The value was a valid value for the property and the provider supports the property as a settable property, but the provider does not suppo [rest of string was truncated]";. + /// The value was a valid value for the property and the provider supports the property as a settable property, but the provider does not su [rest of string was truncated]";. /// internal static string OleDb_PropertyBadValue { get { From 8269438512e9503c34c7a5081a0a35f14b365f02 Mon Sep 17 00:00:00 2001 From: Karina Zhou Date: Thu, 31 Oct 2019 12:46:14 -0700 Subject: [PATCH 47/63] revert docs directory --- .../netfx/ref/Microsoft.Data.SqlClient.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj index 9b3695035a..1425d80c32 100644 --- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj @@ -4,7 +4,7 @@ net46 $(ObjFolder)$(Configuration)\$(AssemblyName)\ref\ $(BinFolder)$(Configuration)\$(AssemblyName)\ref\ - $(OutputPath)\src\Microsoft.Data.SqlClient.xml + $(OutputPath)\Microsoft.Data.SqlClient.xml Framework $(BaseProduct) Debug;Release;net46-Release;net46-Debug From 1b8bcace0536452f675acec71cae7447ada81b3b Mon Sep 17 00:00:00 2001 From: David Engel Date: Thu, 31 Oct 2019 13:16:20 -0700 Subject: [PATCH 48/63] Reverting some comment doc changes --- .../Data/Common/DbConnectionStringCommon.cs | 52 +- .../SqlClient/EnclaveDelegate.NetCoreApp.cs | 22 + .../Microsoft/Data/SqlClient/SqlConnection.cs | 43 +- .../Data/SqlClient/SqlConnectionString.cs | 11 +- .../netcore/src/Resources/SR.Designer.cs | 930 ++++++++++++------ .../Data/SqlClient/EnclaveDelegate.cs | 60 +- .../Microsoft/Data/SqlClient/SqlCommand.cs | 262 +++-- .../Data/SqlClient/SqlConnectionString.cs | 5 +- .../AlwaysEncryptedTests/Utility.cs | 5 + 9 files changed, 975 insertions(+), 415 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs index c5205a1eda..d7317696ee 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs @@ -148,9 +148,18 @@ internal static bool TryConvertToApplicationIntent(string value, out Application } } + /// + /// Column Encryption Setting. + /// const string ColumnEncryptionSettingEnabledString = "Enabled"; const string ColumnEncryptionSettingDisabledString = "Disabled"; + /// + /// Convert a string value to the corresponding SqlConnectionColumnEncryptionSetting. + /// + /// + /// + /// internal static bool TryConvertToColumnEncryptionSetting(string value, out SqlConnectionColumnEncryptionSetting result) { bool isSuccess = false; @@ -173,12 +182,22 @@ internal static bool TryConvertToColumnEncryptionSetting(string value, out SqlCo return isSuccess; } + /// + /// Is it a valid connection level column encryption setting ? + /// + /// + /// internal static bool IsValidColumnEncryptionSetting(SqlConnectionColumnEncryptionSetting value) { Debug.Assert(Enum.GetNames(typeof(SqlConnectionColumnEncryptionSetting)).Length == 2, "SqlConnectionColumnEncryptionSetting enum has changed, update needed"); return value == SqlConnectionColumnEncryptionSetting.Enabled || value == SqlConnectionColumnEncryptionSetting.Disabled; } + /// + /// Convert connection level column encryption setting value to string. + /// + /// + /// internal static string ColumnEncryptionSettingToString(SqlConnectionColumnEncryptionSetting value) { Debug.Assert(IsValidColumnEncryptionSetting(value), "value is not a valid connection level column encryption setting."); @@ -197,9 +216,18 @@ internal static string ColumnEncryptionSettingToString(SqlConnectionColumnEncryp #region <> + /// + /// Attestation Protocol. + /// const string AttestationProtocolHGS = "HGS"; const string AttestationProtocolAAS = "AAS"; + /// + /// Convert a string value to the corresponding SqlConnectionAttestationProtocol + /// + /// + /// + /// internal static bool TryConvertToAttestationProtocol(string value, out SqlConnectionAttestationProtocol result) { if (StringComparer.InvariantCultureIgnoreCase.Equals(value, AttestationProtocolHGS)) @@ -214,15 +242,15 @@ internal static bool TryConvertToAttestationProtocol(string value, out SqlConnec } else - result = DbConnectionStringDefaults.AttestationProtocol; + result = DbConnectionStringDefaults.AttestationProtocol; return false; - } + } internal static bool IsValidAttestationProtocol(SqlConnectionAttestationProtocol value) { Debug.Assert(Enum.GetNames(typeof(SqlConnectionAttestationProtocol)).Length == 3, "SqlConnectionAttestationProtocol enum has changed, update needed"); - return value == SqlConnectionAttestationProtocol.NotSpecified - || value == SqlConnectionAttestationProtocol.HGS + return value == SqlConnectionAttestationProtocol.NotSpecified + || value == SqlConnectionAttestationProtocol.HGS || value == SqlConnectionAttestationProtocol.AAS; } @@ -327,6 +355,16 @@ internal static string ApplicationIntentToString(ApplicationIntent value) } } + /// + /// This method attempts to convert the given value tp ApplicationIntent enum. The algorithm is: + /// * if the value is from type string, it will be matched against ApplicationIntent enum names only, using ordinal, case-insensitive comparer + /// * if the value is from type ApplicationIntent, it will be used as is + /// * if the value is from integral type (SByte, Int16, Int32, Int64, Byte, UInt16, UInt32, or UInt64), it will be converted to enum + /// * if the value is another enum or any other type, it will be blocked with an appropriate ArgumentException + /// + /// in any case above, if the converted value is out of valid range, the method raises ArgumentOutOfRangeException. + /// + /// application intent value in the valid range internal static ApplicationIntent ConvertToApplicationIntent(string keyword, object value) { Debug.Assert(null != value, "ConvertToApplicationIntent(null)"); @@ -492,6 +530,12 @@ internal static SqlAuthenticationMethod ConvertToAuthenticationType(string keywo } } + /// + /// Convert the provided value to a SqlConnectionColumnEncryptionSetting. + /// + /// + /// + /// internal static SqlConnectionColumnEncryptionSetting ConvertToColumnEncryptionSetting(string keyword, object value) { if (null == value) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetCoreApp.cs index 87eeff32b5..4de8b6c5e0 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetCoreApp.cs @@ -7,6 +7,9 @@ namespace Microsoft.Data.SqlClient { + /// + /// A delegate for communicating with secure enclave + /// internal partial class EnclaveDelegate { private static readonly string GetSerializedAttestationParametersName = "GetSerializedAttestationParameters"; @@ -53,6 +56,15 @@ internal byte[] GetSerializedAttestationParameters(SqlEnclaveAttestationParamete return CombineByteArrays(new[] { attestationProtocolBytes, attestationProtocolInputLengthBytes, attestationProtocolInputBytes, clientDHPublicKeyLengthBytes, clientDHPublicKey }); } + /// + /// Create a new enclave session + /// + /// attestation protocol + /// enclave type + /// servername + /// attestation url for attestation service endpoint + /// attestation info from SQL Server + /// attestation parameters internal void CreateEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, string serverName, string attestationUrl, byte[] attestationInfo, SqlEnclaveAttestationParameters attestationParameters) { @@ -75,6 +87,16 @@ internal void CreateEnclaveSession(SqlConnectionAttestationProtocol attestationP } } + /// + /// Generate the byte package that needs to be sent to the enclave + /// + /// attestation protocol + /// Keys to be sent to enclave + /// + /// enclave type + /// server name + /// url for attestation endpoint + /// internal EnclavePackage GenerateEnclavePackage(SqlConnectionAttestationProtocol attestationProtocol, Dictionary keysTobeSentToEnclave, string queryText, string enclaveType, string serverName, string enclaveAttestationUrl) { diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs index 9cc7353e98..20e32310f4 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -74,13 +74,17 @@ private static readonly Dictionary // Lock to control setting of _CustomColumnEncryptionKeyStoreProviders private static readonly Object _CustomColumnEncryptionKeyProvidersLock = new Object(); - // Custom provider list should be provided by the user. We shallow copy the user supplied dictionary into a ReadOnlyDictionary. - // Custom provider list can only supplied once per application. + /// + /// Custom provider list should be provided by the user. We shallow copy the user supplied dictionary into a ReadOnlyDictionary. + /// Custom provider list can only supplied once per application. + /// private static ReadOnlyDictionary _CustomColumnEncryptionKeyStoreProviders; - // Dictionary object holding trusted key paths for various SQL Servers. - // Key to the dictionary is a SQL Server Name - // IList contains a list of trusted key paths. + /// + /// Dictionary object holding trusted key paths for various SQL Servers. + /// Key to the dictionary is a SQL Server Name + /// IList contains a list of trusted key paths. + /// private static readonly ConcurrentDictionary> _ColumnEncryptionTrustedMasterKeyPaths = new ConcurrentDictionary>(concurrencyLevel: 4 * Environment.ProcessorCount /* default value in ConcurrentDictionary*/, capacity: 1, @@ -145,7 +149,12 @@ private SqlConnection(SqlConnection connection) CacheConnectionStringProperties(); } - // This function walks through both system and custom column encryption key store providers and returns an object if found. + /// + /// This function walks through both system and custom column encryption key store providers and returns an object if found. + /// + /// Provider Name to be searched in System Provider diction and Custom provider dictionary. + /// If the provider is found, returns the corresponding SqlColumnEncryptionKeyStoreProvider instance. + /// true if the provider is found, else returns false static internal bool TryGetColumnEncryptionKeyStoreProvider(string providerName, out SqlColumnEncryptionKeyStoreProvider columnKeyStoreProvider) { Debug.Assert(!string.IsNullOrWhiteSpace(providerName), "Provider name is invalid"); @@ -172,14 +181,20 @@ static internal bool TryGetColumnEncryptionKeyStoreProvider(string providerName, } } - // This function returns a list of system provider dictionary currently supported by this driver. + /// + /// This function returns a list of system provider dictionary currently supported by this driver. + /// + /// Combined list of provider names static internal List GetColumnEncryptionSystemKeyStoreProviders() { HashSet providerNames = new HashSet(_SystemColumnEncryptionKeyStoreProviders.Keys); return providerNames.ToList(); } - // This function returns a list of custom provider dictionary currently registered. + /// + /// This function returns a list of custom provider dictionary currently registered. + /// + /// Combined list of provider names static internal List GetColumnEncryptionCustomKeyStoreProviders() { if (_CustomColumnEncryptionKeyStoreProviders != null) @@ -191,7 +206,9 @@ static internal List GetColumnEncryptionCustomKeyStoreProviders() return new List(); } - // Is this connection using column encryption ? + /// + /// Is this connection using column encryption ? + /// internal bool IsColumnEncryptionSettingEnabled { get @@ -254,10 +271,14 @@ public static void RegisterColumnEncryptionKeyStoreProviders(IDictionary + /// Get enclave attestation url to be used with enclave based Always Encrypted + /// internal string EnclaveAttestationUrl => ((SqlConnectionString)ConnectionOptions).EnclaveAttestationUrl; - // Get attestation protocol + /// + /// Get attestation protocol + /// internal SqlConnectionAttestationProtocol AttestationProtocol { get diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionString.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionString.cs index dc73c42ab7..f642e84a64 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionString.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionString.cs @@ -691,7 +691,10 @@ internal SqlAuthenticationMethod ConvertValueToAuthenticationType() } } - // Convert the value to SqlConnectionColumnEncryptionSetting. + /// + /// Convert the value to SqlConnectionColumnEncryptionSetting. + /// + /// internal SqlConnectionColumnEncryptionSetting ConvertValueToColumnEncryptionSetting() { if (!TryGetParsetableValue(KEY.ColumnEncryptionSetting, out string value)) @@ -713,8 +716,10 @@ internal SqlConnectionColumnEncryptionSetting ConvertValueToColumnEncryptionSett } } - - // Convert the value to SqlConnectionAttestationProtocol + /// + /// Convert the value to SqlConnectionAttestationProtocol + /// + /// internal SqlConnectionAttestationProtocol ConvertValueToAttestationProtocol() { if (!TryGetParsetableValue(KEY.AttestationProtocol, out string value)) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs index 4156437061..5dc46872ab 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs @@ -4064,554 +4064,868 @@ internal static string TCE_AttestationInfoNotReturnedFromSQLServer { return ResourceManager.GetString("TCE_AttestationInfoNotReturnedFromSQLServer", resourceCulture); } } - - // Looks up a localized string similar to {0} should be identical on all commands ({1}, {2}, {3}, {4}) when doing batch updates.. - internal static string TCE_BatchedUpdateColumnEncryptionSettingMismatch { - get { + + /// + /// Looks up a localized string similar to {0} should be identical on all commands ({1}, {2}, {3}, {4}) when doing batch updates.. + /// + internal static string TCE_BatchedUpdateColumnEncryptionSettingMismatch + { + get + { return ResourceManager.GetString("TCE_BatchedUpdateColumnEncryptionSettingMismatch", resourceCulture); } } - - // Looks up a localized string similar to Failed to instantiate an enclave provider with type '{1}' for name '{0}'. Error message: {2}. - internal static string TCE_CannotCreateSqlColumnEncryptionEnclaveProvider { - get { + + /// + /// Looks up a localized string similar to Failed to instantiate an enclave provider with type '{1}' for name '{0}'. Error message: {2}. + /// + internal static string TCE_CannotCreateSqlColumnEncryptionEnclaveProvider + { + get + { return ResourceManager.GetString("TCE_CannotCreateSqlColumnEncryptionEnclaveProvider", resourceCulture); } } - - // Looks up a localized string similar to Failed to read the configuration section for enclave providers. Make sure the section is correctly formatted in your application configuration file. Error Message: {0}. - internal static string TCE_CannotGetSqlColumnEncryptionEnclaveProviderConfig { - get { + + /// + /// Looks up a localized string similar to Failed to read the configuration section for enclave providers. Make sure the section is correctly formatted in your application configuration file. Error Message: {0}. + /// + internal static string TCE_CannotGetSqlColumnEncryptionEnclaveProviderConfig + { + get + { return ResourceManager.GetString("TCE_CannotGetSqlColumnEncryptionEnclaveProviderConfig", resourceCulture); } } - - // Looks up a localized string similar to Key store providers cannot be set more than once.. - internal static string TCE_CanOnlyCallOnce { - get { + + /// + /// Looks up a localized string similar to Key store providers cannot be set more than once.. + /// + internal static string TCE_CanOnlyCallOnce + { + get + { return ResourceManager.GetString("TCE_CanOnlyCallOnce", resourceCulture); } } - - // Looks up a localized string similar to Certificate with thumbprint '{0}' not found in certificate store '{1}' in certificate location '{2}'.. - internal static string TCE_CertificateNotFound { - get { + + /// + /// Looks up a localized string similar to Certificate with thumbprint '{0}' not found in certificate store '{1}' in certificate location '{2}'.. + /// + internal static string TCE_CertificateNotFound + { + get + { return ResourceManager.GetString("TCE_CertificateNotFound", resourceCulture); } } - - // Looks up a localized string similar to Certificate with thumbprint '{0}' not found in certificate store '{1}' in certificate location '{2}'. Verify the certificate path in the column master key definition in the database is correct, and the certificate has been imported correctly into the certificate location/store.. - internal static string TCE_CertificateNotFoundSysErr { - get { + + /// + /// Looks up a localized string similar to Certificate with thumbprint '{0}' not found in certificate store '{1}' in certificate location '{2}'. Verify the certificate path in the column master key definition in the database is correct, and the certificate has been imported correctly into the certificate location/store.. + /// + internal static string TCE_CertificateNotFoundSysErr + { + get + { return ResourceManager.GetString("TCE_CertificateNotFoundSysErr", resourceCulture); } } - - // Looks up a localized string similar to Certificate specified in key path '{0}' does not have a private key to encrypt a column encryption key. Verify the certificate is imported correctly.. - internal static string TCE_CertificateWithNoPrivateKey { - get { + + /// + /// Looks up a localized string similar to Certificate specified in key path '{0}' does not have a private key to encrypt a column encryption key. Verify the certificate is imported correctly.. + /// + internal static string TCE_CertificateWithNoPrivateKey + { + get + { return ResourceManager.GetString("TCE_CertificateWithNoPrivateKey", resourceCulture); } } - - // Looks up a localized string similar to Certificate specified in key path '{0}' does not have a private key to decrypt a column encryption key. Verify the certificate is imported correctly.. - internal static string TCE_CertificateWithNoPrivateKeySysErr { - get { + + /// + /// Looks up a localized string similar to Certificate specified in key path '{0}' does not have a private key to decrypt a column encryption key. Verify the certificate is imported correctly.. + /// + internal static string TCE_CertificateWithNoPrivateKeySysErr + { + get + { return ResourceManager.GetString("TCE_CertificateWithNoPrivateKeySysErr", resourceCulture); } } - - // Looks up a localized string similar to Failed to decrypt column '{0}'.. - internal static string TCE_ColumnDecryptionFailed { - get { + + /// + /// Looks up a localized string similar to Failed to decrypt column '{0}'.. + /// + internal static string TCE_ColumnDecryptionFailed + { + get + { return ResourceManager.GetString("TCE_ColumnDecryptionFailed", resourceCulture); } } - - // Looks up a localized string similar to Internal Error. Encrypted column encryption keys not found when trying to send the keys to the enclave.. - internal static string TCE_ColumnEncryptionKeysNotFound { - get { + + /// + /// Looks up a localized string similar to Internal Error. Encrypted column encryption keys not found when trying to send the keys to the enclave.. + /// + internal static string TCE_ColumnEncryptionKeysNotFound + { + get + { return ResourceManager.GetString("TCE_ColumnEncryptionKeysNotFound", resourceCulture); } } - - // Looks up a localized string similar to Internal error. The signature returned by SQL Server for enclave-enabled column master key, specified at key path '{0}', cannot be null or empty.. - internal static string TCE_ColumnMasterKeySignatureNotFound { - get { + + /// + /// Looks up a localized string similar to Internal error. The signature returned by SQL Server for enclave-enabled column master key, specified at key path '{0}', cannot be null or empty.. + /// + internal static string TCE_ColumnMasterKeySignatureNotFound + { + get + { return ResourceManager.GetString("TCE_ColumnMasterKeySignatureNotFound", resourceCulture); } } - - // Looks up a localized string similar to The signature returned by SQL Server for the column master key, specified in key path '{0}', is invalid (does not match the computed signature). Recreate column master key metadata, making sure the signature inside the metadata is computed using the column master key being referenced in the metadata. If the error persists, please contact Microsoft for assistance.. - internal static string TCE_ColumnMasterKeySignatureVerificationFailed { - get { + + /// + /// Looks up a localized string similar to The signature returned by SQL Server for the column master key, specified in key path '{0}', is invalid (does not match the computed signature). Recreate column master key metadata, making sure the signature inside the metadata is computed using the column master key being referenced in the metadata. If the error persists, please contact Microsoft for assistance.. + /// + internal static string TCE_ColumnMasterKeySignatureVerificationFailed + { + get + { return ResourceManager.GetString("TCE_ColumnMasterKeySignatureVerificationFailed", resourceCulture); } } - - // Looks up a localized string similar to Decryption failed. The last 10 bytes of the encrypted column encryption key are: '{0}'. The first 10 bytes of ciphertext are: '{1}'.. - internal static string TCE_DecryptionFailed { - get { + + /// + /// Looks up a localized string similar to Decryption failed. The last 10 bytes of the encrypted column encryption key are: '{0}'. The first 10 bytes of ciphertext are: '{1}'.. + /// + internal static string TCE_DecryptionFailed + { + get + { return ResourceManager.GetString("TCE_DecryptionFailed", resourceCulture); } } - - // Looks up a localized string similar to Internal Error. Empty argument '{0}' specified when constructing an object of type '{1}'. '{0}' cannot be empty.. - internal static string TCE_EmptyArgumentInConstructorInternal { - get { + + /// + /// Looks up a localized string similar to Internal Error. Empty argument '{0}' specified when constructing an object of type '{1}'. '{0}' cannot be empty.. + /// + internal static string TCE_EmptyArgumentInConstructorInternal + { + get + { return ResourceManager.GetString("TCE_EmptyArgumentInConstructorInternal", resourceCulture); } } - - // Looks up a localized string similar to Internal Error. Argument '{0}' cannot be empty when executing method '{1}.{2}'.. - internal static string TCE_EmptyArgumentInternal { - get { + + /// + /// Looks up a localized string similar to Internal Error. Argument '{0}' cannot be empty when executing method '{1}.{2}'.. + /// + internal static string TCE_EmptyArgumentInternal + { + get + { return ResourceManager.GetString("TCE_EmptyArgumentInternal", resourceCulture); } } - - // Looks up a localized string similar to Empty certificate thumbprint specified in certificate path '{0}'.. - internal static string TCE_EmptyCertificateThumbprint { - get { + + /// + /// Looks up a localized string similar to Empty certificate thumbprint specified in certificate path '{0}'.. + /// + internal static string TCE_EmptyCertificateThumbprint + { + get + { return ResourceManager.GetString("TCE_EmptyCertificateThumbprint", resourceCulture); } } - - // Looks up a localized string similar to Internal error. Empty certificate thumbprint specified in certificate path '{0}'.. - internal static string TCE_EmptyCertificateThumbprintSysErr { - get { + + /// + /// Looks up a localized string similar to Internal error. Empty certificate thumbprint specified in certificate path '{0}'.. + /// + internal static string TCE_EmptyCertificateThumbprintSysErr + { + get + { return ResourceManager.GetString("TCE_EmptyCertificateThumbprintSysErr", resourceCulture); } } - - // Looks up a localized string similar to Empty key identifier specified in column master key path: '{0}'. Use the following format for a key stored in a Microsoft Cryptography API: Next Generation (CNG) provider: <CNG Provider Name>{1}<Key Identifier>.. - internal static string TCE_EmptyCngKeyId { - get { + + /// + /// Looks up a localized string similar to Empty key identifier specified in column master key path: '{0}'. Use the following format for a key stored in a Microsoft Cryptography API: Next Generation (CNG) provider: <CNG Provider Name>{1}<Key Identifier>.. + /// + internal static string TCE_EmptyCngKeyId + { + get + { return ResourceManager.GetString("TCE_EmptyCngKeyId", resourceCulture); } } - - // Looks up a localized string similar to Internal error. Empty key identifier specified in column master key path: '{0}'. Use the following format for a key stored in a Microsoft Cryptography API: Next Generation (CNG) provider: <CNG Provider Name>{1}<Key Identifier>.. - internal static string TCE_EmptyCngKeyIdSysErr { - get { + + /// + /// Looks up a localized string similar to Internal error. Empty key identifier specified in column master key path: '{0}'. Use the following format for a key stored in a Microsoft Cryptography API: Next Generation (CNG) provider: <CNG Provider Name>{1}<Key Identifier>.. + /// + internal static string TCE_EmptyCngKeyIdSysErr + { + get + { return ResourceManager.GetString("TCE_EmptyCngKeyIdSysErr", resourceCulture); } } - - // Looks up a localized string similar to Empty Microsoft Cryptography API: Next Generation (CNG) provider name specified in column master key path: '{0}'. Use the following format for a key stored in a Microsoft Cryptography API: Next Generation (CNG) provider: <CNG Provider Name>{1}<Key Identifier>.. - internal static string TCE_EmptyCngName { - get { + + /// + /// Looks up a localized string similar to Empty Microsoft Cryptography API: Next Generation (CNG) provider name specified in column master key path: '{0}'. Use the following format for a key stored in a Microsoft Cryptography API: Next Generation (CNG) provider: <CNG Provider Name>{1}<Key Identifier>.. + /// + internal static string TCE_EmptyCngName + { + get + { return ResourceManager.GetString("TCE_EmptyCngName", resourceCulture); } } - - // Looks up a localized string similar to Internal error. Empty Microsoft Cryptography API: Next Generation (CNG) provider name specified in column master key path: '{0}'. Use the following format for a key stored in a Microsoft Cryptography API: Next Generation (CNG) provider: <CNG Provider Name>{1}<Key Identifier>.. - internal static string TCE_EmptyCngNameSysErr { - get { + + /// + /// Looks up a localized string similar to Internal error. Empty Microsoft Cryptography API: Next Generation (CNG) provider name specified in column master key path: '{0}'. Use the following format for a key stored in a Microsoft Cryptography API: Next Generation (CNG) provider: <CNG Provider Name>{1}<Key Identifier>.. + /// + internal static string TCE_EmptyCngNameSysErr + { + get + { return ResourceManager.GetString("TCE_EmptyCngNameSysErr", resourceCulture); } } - - // Looks up a localized string similar to Empty column encryption key specified.. - internal static string TCE_EmptyColumnEncryptionKey { - get { + + /// + /// Looks up a localized string similar to Empty column encryption key specified.. + /// + internal static string TCE_EmptyColumnEncryptionKey + { + get + { return ResourceManager.GetString("TCE_EmptyColumnEncryptionKey", resourceCulture); } } - - // Looks up a localized string similar to Empty key identifier specified in column master key path: '{0}'. Use the following format for a key stored in a Microsoft cryptographic service provider (CSP): <CSP Provider Name>{1}<Key Identifier>.. - internal static string TCE_EmptyCspKeyId { - get { + + /// + /// Looks up a localized string similar to Empty key identifier specified in column master key path: '{0}'. Use the following format for a key stored in a Microsoft cryptographic service provider (CSP): <CSP Provider Name>{1}<Key Identifier>.. + /// + internal static string TCE_EmptyCspKeyId + { + get + { return ResourceManager.GetString("TCE_EmptyCspKeyId", resourceCulture); } } - - // Looks up a localized string similar to Internal error. Empty key identifier specified in column master key path: '{0}'. Use the following format for a key stored in a Microsoft cryptographic service provider (CSP): <CSP Provider Name>{1}<Key Identifier>.. - internal static string TCE_EmptyCspKeyIdSysErr { - get { + + /// + /// Looks up a localized string similar to Internal error. Empty key identifier specified in column master key path: '{0}'. Use the following format for a key stored in a Microsoft cryptographic service provider (CSP): <CSP Provider Name>{1}<Key Identifier>.. + /// + internal static string TCE_EmptyCspKeyIdSysErr + { + get + { return ResourceManager.GetString("TCE_EmptyCspKeyIdSysErr", resourceCulture); } } - - // Looks up a localized string similar to Empty Microsoft cryptographic service provider (CSP) name specified in column master key path: '{0}'. Use the following format for a key stored in a Microsoft cryptographic service provider (CSP): <CSP Provider Name>{1}<Key Identifier>.. - internal static string TCE_EmptyCspName { - get { + + /// + /// Looks up a localized string similar to Empty Microsoft cryptographic service provider (CSP) name specified in column master key path: '{0}'. Use the following format for a key stored in a Microsoft cryptographic service provider (CSP): <CSP Provider Name>{1}<Key Identifier>.. + /// + internal static string TCE_EmptyCspName + { + get + { return ResourceManager.GetString("TCE_EmptyCspName", resourceCulture); } } - - // Looks up a localized string similar to Internal error. Empty Microsoft cryptographic service provider (CSP) name specified in column master key path: '{0}'. Use the following format for a key stored in a Microsoft cryptographic service provider (CSP): <CSP Provider Name>{1}<Key Identifier>.. - internal static string TCE_EmptyCspNameSysErr { - get { + + /// + /// Looks up a localized string similar to Internal error. Empty Microsoft cryptographic service provider (CSP) name specified in column master key path: '{0}'. Use the following format for a key stored in a Microsoft cryptographic service provider (CSP): <CSP Provider Name>{1}<Key Identifier>.. + /// + internal static string TCE_EmptyCspNameSysErr + { + get + { return ResourceManager.GetString("TCE_EmptyCspNameSysErr", resourceCulture); } } - - // Looks up a localized string similar to Internal error. Empty encrypted column encryption key specified.. - internal static string TCE_EmptyEncryptedColumnEncryptionKey { - get { + + /// + /// Looks up a localized string similar to Internal error. Empty encrypted column encryption key specified.. + /// + internal static string TCE_EmptyEncryptedColumnEncryptionKey + { + get + { return ResourceManager.GetString("TCE_EmptyEncryptedColumnEncryptionKey", resourceCulture); } } - - // Looks up a localized string similar to Invalid key store provider name specified. Key store provider names cannot be null or empty.. - internal static string TCE_EmptyProviderName { - get { + + /// + /// Looks up a localized string similar to Invalid key store provider name specified. Key store provider names cannot be null or empty.. + /// + internal static string TCE_EmptyProviderName + { + get + { return ResourceManager.GetString("TCE_EmptyProviderName", resourceCulture); } } - - // Looks up a localized string similar to You have specified the enclave attestation URL in the connection string, but the SQL Server instance in use does not support enclave based computations.. - internal static string TCE_EnclaveComputationsNotSupported { - get { + + /// + /// Looks up a localized string similar to You have specified the enclave attestation URL in the connection string, but the SQL Server instance in use does not support enclave based computations.. + /// + internal static string TCE_EnclaveComputationsNotSupported + { + get + { return ResourceManager.GetString("TCE_EnclaveComputationsNotSupported", resourceCulture); } } - - // Looks up a localized string similar to No enclave provider found for enclave type '{0}'. Please specify the provider in the application configuration.. - internal static string TCE_EnclaveProviderNotFound { - get { + + /// + /// Looks up a localized string similar to No enclave provider found for enclave type '{0}'. Please specify the provider in the application configuration.. + /// + internal static string TCE_EnclaveProviderNotFound + { + get + { return ResourceManager.GetString("TCE_EnclaveProviderNotFound", resourceCulture); } } - // Looks up a localized string similar to Specifies an attestation protocol for its corresponding enclave attestation service. - internal static string TCE_DbConnectionString_AttestationProtocol { - get { + /// + /// Looks up a localized string similar to Specifies an attestation protocol for its corresponding enclave attestation service. + /// + internal static string TCE_DbConnectionString_AttestationProtocol + { + get + { return ResourceManager.GetString("TCE_DbConnectionString_AttestationProtocol", resourceCulture); } } - // Looks up a localized string similar to The enclave type '{0}' returned from the server is not supported. - internal static string TCE_EnclaveTypeNotSupported { - get { + /// + /// Looks up a localized string similar to The enclave type '{0}' returned from the server is not supported. + /// + internal static string TCE_EnclaveTypeNotSupported + { + get + { return ResourceManager.GetString("TCE_EnclaveTypeNotSupported", resourceCulture); } } - // Looks up a localized string similar to Failed to initialize connection. The attestation protocol '{0}' does not support the enclave type '{1}'. - internal static string TCE_AttestationProtocolNotSupportEnclaveType { - get { + /// + /// Looks up a localized string similar to Failed to initialize connection. The attestation protocol '{0}' does not support the enclave type '{1}'. + /// + internal static string TCE_AttestationProtocolNotSupportEnclaveType + { + get + { return ResourceManager.GetString("TCE_AttestationProtocolNotSupportEnclaveType", resourceCulture); } } - // Looks up a localized string similar to Error occured when generating enclave package. Attestation Protocol has not been specified in the connection string, but the query requires enclave computations. - internal static string TCE_AttestationProtocolNotSpecifiedForGeneratingEnclavePackage { - get { + /// + /// Looks up a localized string similar to Error occured when generating enclave package. Attestation Protocol has not been specified in the connection string, but the query requires enclave computations. + /// + internal static string TCE_AttestationProtocolNotSpecifiedForGeneratingEnclavePackage + { + get + { return ResourceManager.GetString("TCE_AttestationProtocolNotSpecifiedForGeneratingEnclavePackage", resourceCulture); } } - // Looks up a localized string similar to Executing a query requires enclave computations, but the application configuration is missing the enclave provider section.. - internal static string TCE_EnclaveProvidersNotConfiguredForEnclaveBasedQuery { - get { + /// + /// Looks up a localized string similar to Executing a query requires enclave computations, but the application configuration is missing the enclave provider section.. + /// + internal static string TCE_EnclaveProvidersNotConfiguredForEnclaveBasedQuery + { + get + { return ResourceManager.GetString("TCE_EnclaveProvidersNotConfiguredForEnclaveBasedQuery", resourceCulture); } } - - // Looks up a localized string similar to You have specified the enclave attestation URL in the connection string, but the SQL Server instance did not return an enclave type. Please make sure the enclave type is correctly configured in your instance.. - internal static string TCE_EnclaveTypeNotReturned { - get { + + /// + /// Looks up a localized string similar to You have specified the enclave attestation URL in the connection string, but the SQL Server instance did not return an enclave type. Please make sure the enclave type is correctly configured in your instance.. + /// + internal static string TCE_EnclaveTypeNotReturned + { + get + { return ResourceManager.GetString("TCE_EnclaveTypeNotReturned", resourceCulture); } } - - // Looks up a localized string similar to Internal Error. Enclave type received from SQL Server is null or empty when executing a query requiring enclave computations.. - internal static string TCE_EnclaveTypeNullForEnclaveBasedQuery { - get { + + /// + /// Looks up a localized string similar to Internal Error. Enclave type received from SQL Server is null or empty when executing a query requiring enclave computations.. + /// + internal static string TCE_EnclaveTypeNullForEnclaveBasedQuery + { + get + { return ResourceManager.GetString("TCE_EnclaveTypeNullForEnclaveBasedQuery", resourceCulture); } } - - // Looks up a localized string similar to Error encountered while generating package to be sent to enclave. Error message: {0}. - internal static string TCE_ExceptionWhenGeneratingEnclavePackage { - get { + + /// + /// Looks up a localized string similar to Error encountered while generating package to be sent to enclave. Error message: {0}. + /// + internal static string TCE_ExceptionWhenGeneratingEnclavePackage + { + get + { return ResourceManager.GetString("TCE_ExceptionWhenGeneratingEnclavePackage", resourceCulture); } } - - // Looks up a localized string similar to Internal Error. Failed to encrypt byte package to be sent to the enclave. Error Message: {0}. - internal static string TCE_FailedToEncryptRegisterRulesBytePackage { - get { + + /// + /// Looks up a localized string similar to Internal Error. Failed to encrypt byte package to be sent to the enclave. Error Message: {0}. + /// + internal static string TCE_FailedToEncryptRegisterRulesBytePackage + { + get + { return ResourceManager.GetString("TCE_FailedToEncryptRegisterRulesBytePackage", resourceCulture); } } - - // Looks up a localized string similar to Internal Error. The buffer specified by argument '{0}' for method '{1}.{2}' has insufficient space.. - internal static string TCE_InsufficientBuffer { - get { + + /// + /// Looks up a localized string similar to Internal Error. The buffer specified by argument '{0}' for method '{1}.{2}' has insufficient space.. + /// + internal static string TCE_InsufficientBuffer + { + get + { return ResourceManager.GetString("TCE_InsufficientBuffer", resourceCulture); } } - - // Looks up a localized string similar to The specified ciphertext's encryption algorithm version '{0}' does not match the expected encryption algorithm version '{1}'.. - internal static string TCE_InvalidAlgorithmVersion { - get { + + /// + /// Looks up a localized string similar to The specified ciphertext's encryption algorithm version '{0}' does not match the expected encryption algorithm version '{1}'.. + /// + internal static string TCE_InvalidAlgorithmVersion + { + get + { return ResourceManager.GetString("TCE_InvalidAlgorithmVersion", resourceCulture); } } - - // Looks up a localized string similar to Specified encrypted column encryption key contains an invalid encryption algorithm version '{0}'. Expected version is '{1}'.. - internal static string TCE_InvalidAlgorithmVersionInEncryptedCEK { - get { + + /// + /// Looks up a localized string similar to Specified encrypted column encryption key contains an invalid encryption algorithm version '{0}'. Expected version is '{1}'.. + /// + internal static string TCE_InvalidAlgorithmVersionInEncryptedCEK + { + get + { return ResourceManager.GetString("TCE_InvalidAlgorithmVersionInEncryptedCEK", resourceCulture); } } - - // Looks up a localized string similar to Invalid attestation parameters specified by the enclave provider for enclave type '{0}'. Error occurred when converting the value '{1}' of parameter '{2}' to unsigned int. Error Message: {3}. - internal static string TCE_InvalidAttestationParameterUnableToConvertToUnsignedInt { - get { + + /// + /// Looks up a localized string similar to Invalid attestation parameters specified by the enclave provider for enclave type '{0}'. Error occurred when converting the value '{1}' of parameter '{2}' to unsigned int. Error Message: {3}. + /// + internal static string TCE_InvalidAttestationParameterUnableToConvertToUnsignedInt + { + get + { return ResourceManager.GetString("TCE_InvalidAttestationParameterUnableToConvertToUnsignedInt", resourceCulture); } } - - // Looks up a localized string similar to Specified ciphertext has an invalid authentication tag.. - internal static string TCE_InvalidAuthenticationTag { - get { + + /// + /// Looks up a localized string similar to Specified ciphertext has an invalid authentication tag.. + /// + internal static string TCE_InvalidAuthenticationTag + { + get + { return ResourceManager.GetString("TCE_InvalidAuthenticationTag", resourceCulture); } } - - // Looks up a localized string similar to Invalid certificate location '{0}' in certificate path '{1}'. Use the following format: <certificate location>{4}<certificate store>{4}<certificate thumbprint>, where <certificate location> is either '{2}' or '{3}'.. - internal static string TCE_InvalidCertificateLocation { - get { + + /// + /// Looks up a localized string similar to Invalid certificate location '{0}' in certificate path '{1}'. Use the following format: <certificate location>{4}<certificate store>{4}<certificate thumbprint>, where <certificate location> is either '{2}' or '{3}'.. + /// + internal static string TCE_InvalidCertificateLocation + { + get + { return ResourceManager.GetString("TCE_InvalidCertificateLocation", resourceCulture); } } - - // Looks up a localized string similar to Internal error. Invalid certificate location '{0}' in certificate path '{1}'. Use the following format: <certificate location>{4}<certificate store>{4}<certificate thumbprint>, where <certificate location> is either '{2}' or '{3}'.. - internal static string TCE_InvalidCertificateLocationSysErr { - get { + + /// + /// Looks up a localized string similar to Internal error. Invalid certificate location '{0}' in certificate path '{1}'. Use the following format: <certificate location>{4}<certificate store>{4}<certificate thumbprint>, where <certificate location> is either '{2}' or '{3}'.. + /// + internal static string TCE_InvalidCertificateLocationSysErr + { + get + { return ResourceManager.GetString("TCE_InvalidCertificateLocationSysErr", resourceCulture); } } - - // Looks up a localized string similar to Invalid certificate path: '{0}'. Use the following format: <certificate location>{3}<certificate store>{3}<certificate thumbprint>, where <certificate location> is either '{1}' or '{2}'.. - internal static string TCE_InvalidCertificatePath { - get { + + /// + /// Looks up a localized string similar to Invalid certificate path: '{0}'. Use the following format: <certificate location>{3}<certificate store>{3}<certificate thumbprint>, where <certificate location> is either '{1}' or '{2}'.. + /// + internal static string TCE_InvalidCertificatePath + { + get + { return ResourceManager.GetString("TCE_InvalidCertificatePath", resourceCulture); } } - - // Looks up a localized string similar to Internal error. Invalid certificate path: '{0}'. Use the following format: <certificate location>{3}<certificate store>{3}<certificate thumbprint>, where <certificate location> is either '{1}' or '{2}'.. - internal static string TCE_InvalidCertificatePathSysErr { - get { + + /// + /// Looks up a localized string similar to Internal error. Invalid certificate path: '{0}'. Use the following format: <certificate location>{3}<certificate store>{3}<certificate thumbprint>, where <certificate location> is either '{1}' or '{2}'.. + /// + internal static string TCE_InvalidCertificatePathSysErr + { + get + { return ResourceManager.GetString("TCE_InvalidCertificatePathSysErr", resourceCulture); } } - - // Looks up a localized string similar to The specified encrypted column encryption key signature does not match the signature computed with the column master key (certificate) in '{0}'. The encrypted column encryption key may be corrupt, or the specified path may be incorrect.. - internal static string TCE_InvalidCertificateSignature { - get { + + /// + /// Looks up a localized string similar to The specified encrypted column encryption key signature does not match the signature computed with the column master key (certificate) in '{0}'. The encrypted column encryption key may be corrupt, or the specified path may be incorrect.. + /// + internal static string TCE_InvalidCertificateSignature + { + get + { return ResourceManager.GetString("TCE_InvalidCertificateSignature", resourceCulture); } } - - // Looks up a localized string similar to Invalid certificate store '{0}' specified in certificate path '{1}'. Expected value: '{2}'.. - internal static string TCE_InvalidCertificateStore { - get { + + /// + /// Looks up a localized string similar to Invalid certificate store '{0}' specified in certificate path '{1}'. Expected value: '{2}'.. + /// + internal static string TCE_InvalidCertificateStore + { + get + { return ResourceManager.GetString("TCE_InvalidCertificateStore", resourceCulture); } } - - // Looks up a localized string similar to Internal error. Invalid certificate store '{0}' specified in certificate path '{1}'. Expected value: '{2}'.. - internal static string TCE_InvalidCertificateStoreSysErr { - get { + + /// + /// Looks up a localized string similar to Internal error. Invalid certificate store '{0}' specified in certificate path '{1}'. Expected value: '{2}'.. + /// + internal static string TCE_InvalidCertificateStoreSysErr + { + get + { return ResourceManager.GetString("TCE_InvalidCertificateStoreSysErr", resourceCulture); } } - - // Looks up a localized string similar to The specified encrypted column encryption key's ciphertext length: {0} does not match the ciphertext length: {1} when using column master key (certificate) in '{2}'. The encrypted column encryption key may be corrupt, or the specified certificate path may be incorrect.. - internal static string TCE_InvalidCiphertextLengthInEncryptedCEK { - get { + + /// + /// Looks up a localized string similar to The specified encrypted column encryption key's ciphertext length: {0} does not match the ciphertext length: {1} when using column master key (certificate) in '{2}'. The encrypted column encryption key may be corrupt, or the specified certificate path may be incorrect.. + /// + internal static string TCE_InvalidCiphertextLengthInEncryptedCEK + { + get + { return ResourceManager.GetString("TCE_InvalidCiphertextLengthInEncryptedCEK", resourceCulture); } } - - // Looks up a localized string similar to The specified encrypted column encryption key's ciphertext length: {0} does not match the ciphertext length: {1} when using column master key (asymmetric key) in '{2}'. The encrypted column encryption key may be corrupt, or the specified Microsoft Cryptography API: Next Generation (CNG) provider path may be incorrect.. - internal static string TCE_InvalidCiphertextLengthInEncryptedCEKCng { - get { + + /// + /// Looks up a localized string similar to The specified encrypted column encryption key's ciphertext length: {0} does not match the ciphertext length: {1} when using column master key (asymmetric key) in '{2}'. The encrypted column encryption key may be corrupt, or the specified Microsoft Cryptography API: Next Generation (CNG) provider path may be incorrect.. + /// + internal static string TCE_InvalidCiphertextLengthInEncryptedCEKCng + { + get + { return ResourceManager.GetString("TCE_InvalidCiphertextLengthInEncryptedCEKCng", resourceCulture); } } - - // Looks up a localized string similar to The specified encrypted column encryption key's ciphertext length: {0} does not match the ciphertext length: {1} when using column master key (asymmetric key) in '{2}'. The encrypted column encryption key may be corrupt, or the specified Microsoft Cryptographic Service provider (CSP) path may be incorrect.. - internal static string TCE_InvalidCiphertextLengthInEncryptedCEKCsp { - get { + + /// + /// Looks up a localized string similar to The specified encrypted column encryption key's ciphertext length: {0} does not match the ciphertext length: {1} when using column master key (asymmetric key) in '{2}'. The encrypted column encryption key may be corrupt, or the specified Microsoft Cryptographic Service provider (CSP) path may be incorrect.. + /// + internal static string TCE_InvalidCiphertextLengthInEncryptedCEKCsp + { + get + { return ResourceManager.GetString("TCE_InvalidCiphertextLengthInEncryptedCEKCsp", resourceCulture); } } - - // Looks up a localized string similar to Specified ciphertext has an invalid size of {0} bytes, which is below the minimum {1} bytes required for decryption.. - internal static string TCE_InvalidCipherTextSize { - get { + + /// + /// Looks up a localized string similar to Specified ciphertext has an invalid size of {0} bytes, which is below the minimum {1} bytes required for decryption.. + /// + internal static string TCE_InvalidCipherTextSize + { + get + { return ResourceManager.GetString("TCE_InvalidCipherTextSize", resourceCulture); } } - - // Looks up a localized string similar to An error occurred while opening the Microsoft Cryptography API: Next Generation (CNG) key: '{0}'. Verify that the CNG provider name '{1}' is valid, installed on the machine, and the key '{2}' exists.. - internal static string TCE_InvalidCngKey { - get { + + /// + /// Looks up a localized string similar to An error occurred while opening the Microsoft Cryptography API: Next Generation (CNG) key: '{0}'. Verify that the CNG provider name '{1}' is valid, installed on the machine, and the key '{2}' exists.. + /// + internal static string TCE_InvalidCngKey + { + get + { return ResourceManager.GetString("TCE_InvalidCngKey", resourceCulture); } } - - // Looks up a localized string similar to Internal error. An error occurred while opening the Microsoft Cryptography API: Next Generation (CNG) key: '{0}'. Verify that the CNG provider name '{1}' is valid, installed on the machine, and the key '{2}' exists.. - internal static string TCE_InvalidCngKeySysErr { - get { + + /// + /// Looks up a localized string similar to Internal error. An error occurred while opening the Microsoft Cryptography API: Next Generation (CNG) key: '{0}'. Verify that the CNG provider name '{1}' is valid, installed on the machine, and the key '{2}' exists.. + /// + internal static string TCE_InvalidCngKeySysErr + { + get + { return ResourceManager.GetString("TCE_InvalidCngKeySysErr", resourceCulture); } } - - // Looks up a localized string similar to Invalid column master key path: '{0}'. Use the following format for a key stored in a Microsoft Cryptography API: Next Generation (CNG) provider: <CNG Provider Name>{1}<Key Identifier>.. - internal static string TCE_InvalidCngPath { - get { + + /// + /// Looks up a localized string similar to Invalid column master key path: '{0}'. Use the following format for a key stored in a Microsoft Cryptography API: Next Generation (CNG) provider: <CNG Provider Name>{1}<Key Identifier>.. + /// + internal static string TCE_InvalidCngPath + { + get + { return ResourceManager.GetString("TCE_InvalidCngPath", resourceCulture); } } - // Looks up a localized string similar to Internal error. Invalid column master key path: '{0}'. Use the following format for a key stored in a Microsoft Cryptography API: Next Generation (CNG) provider: <CNG Provider Name>{1}<Key Identifier>.. - internal static string TCE_InvalidCngPathSysErr { - get { + /// + /// Looks up a localized string similar to Internal error. Invalid column master key path: '{0}'. Use the following format for a key stored in a Microsoft Cryptography API: Next Generation (CNG) provider: <CNG Provider Name>{1}<Key Identifier>.. + /// + internal static string TCE_InvalidCngPathSysErr + { + get + { return ResourceManager.GetString("TCE_InvalidCngPathSysErr", resourceCulture); } } - - // Looks up a localized string similar to Invalid key identifier: '{0}'. Verify that the key identifier in column master key path: '{1}' is valid and exists in the CSP.. - internal static string TCE_InvalidCspKeyId { - get { + + /// + /// Looks up a localized string similar to Invalid key identifier: '{0}'. Verify that the key identifier in column master key path: '{1}' is valid and exists in the CSP.. + /// + internal static string TCE_InvalidCspKeyId + { + get + { return ResourceManager.GetString("TCE_InvalidCspKeyId", resourceCulture); } } - - // Looks up a localized string similar to Internal error. Invalid key identifier: '{0}'. Verify that the key identifier in column master key path: '{1}' is valid and exists in the CSP.. - internal static string TCE_InvalidCspKeyIdSysErr { - get { + + /// + /// Looks up a localized string similar to Internal error. Invalid key identifier: '{0}'. Verify that the key identifier in column master key path: '{1}' is valid and exists in the CSP.. + /// + internal static string TCE_InvalidCspKeyIdSysErr + { + get + { return ResourceManager.GetString("TCE_InvalidCspKeyIdSysErr", resourceCulture); } } - - // Looks up a localized string similar to Invalid Microsoft cryptographic service provider (CSP) name: '{0}'. Verify that the CSP provider name in column master key path: '{1}' is valid and installed on the machine.. - internal static string TCE_InvalidCspName { - get { + + /// + /// Looks up a localized string similar to Invalid Microsoft cryptographic service provider (CSP) name: '{0}'. Verify that the CSP provider name in column master key path: '{1}' is valid and installed on the machine.. + /// + internal static string TCE_InvalidCspName + { + get + { return ResourceManager.GetString("TCE_InvalidCspName", resourceCulture); } } - - // Looks up a localized string similar to Internal error. Invalid Microsoft cryptographic service provider (CSP) name: '{0}'. Verify that the CSP provider name in column master key path: '{1}' is valid and installed on the machine.. - internal static string TCE_InvalidCspNameSysErr { - get { + + /// + /// Looks up a localized string similar to Internal error. Invalid Microsoft cryptographic service provider (CSP) name: '{0}'. Verify that the CSP provider name in column master key path: '{1}' is valid and installed on the machine.. + /// + internal static string TCE_InvalidCspNameSysErr + { + get + { return ResourceManager.GetString("TCE_InvalidCspNameSysErr", resourceCulture); } } - - // Looks up a localized string similar to Invalid column master key path: '{0}'. Use the following format for a key stored in a Microsoft cryptographic service provider (CSP): <CSP Provider Name>{1}<Key Identifier>.. - internal static string TCE_InvalidCspPath { - get { + + /// + /// Looks up a localized string similar to Invalid column master key path: '{0}'. Use the following format for a key stored in a Microsoft cryptographic service provider (CSP): <CSP Provider Name>{1}<Key Identifier>.. + /// + internal static string TCE_InvalidCspPath + { + get + { return ResourceManager.GetString("TCE_InvalidCspPath", resourceCulture); } } - - // Looks up a localized string similar to Internal error. Invalid column master key path: '{0}'. Use the following format for a key stored in a Microsoft cryptographic service provider (CSP): <CSP Provider Name>{1}<Key Identifier>.. - internal static string TCE_InvalidCspPathSysErr { - get { + + /// + /// Looks up a localized string similar to Internal error. Invalid column master key path: '{0}'. Use the following format for a key stored in a Microsoft cryptographic service provider (CSP): <CSP Provider Name>{1}<Key Identifier>.. + /// + internal static string TCE_InvalidCspPathSysErr + { + get + { return ResourceManager.GetString("TCE_InvalidCspPathSysErr", resourceCulture); } } - - // Looks up a localized string similar to Invalid key store provider name '{0}'. '{1}' prefix is reserved for system key store providers.. - internal static string TCE_InvalidCustomKeyStoreProviderName { - get { + + /// + /// Looks up a localized string similar to Invalid key store provider name '{0}'. '{1}' prefix is reserved for system key store providers.. + /// + internal static string TCE_InvalidCustomKeyStoreProviderName + { + get + { return ResourceManager.GetString("TCE_InvalidCustomKeyStoreProviderName", resourceCulture); } } - - // Looks up a localized string similar to Internal Error. The given database id '{0}' is not valid. Error occurred when converting the database id to unsigned int. Error Message: {1}. - internal static string TCE_InvalidDatabaseIdUnableToCastToUnsignedInt { - get { + + /// + /// Looks up a localized string similar to Internal Error. The given database id '{0}' is not valid. Error occurred when converting the database id to unsigned int. Error Message: {1}. + /// + internal static string TCE_InvalidDatabaseIdUnableToCastToUnsignedInt + { + get + { return ResourceManager.GetString("TCE_InvalidDatabaseIdUnableToCastToUnsignedInt", resourceCulture); } } - - // Looks up a localized string similar to Internal error. Error occurred when populating enclave metadata. The referenced column encryption key ordinal '{0}' is missing in the encryption metadata returned by SQL Server. Max ordinal is '{1}'.. - internal static string TCE_InvalidEncryptionKeyOrdinalEnclaveMetadata { - get { + + /// + /// Looks up a localized string similar to Internal error. Error occurred when populating enclave metadata. The referenced column encryption key ordinal '{0}' is missing in the encryption metadata returned by SQL Server. Max ordinal is '{1}'.. + /// + internal static string TCE_InvalidEncryptionKeyOrdinalEnclaveMetadata + { + get + { return ResourceManager.GetString("TCE_InvalidEncryptionKeyOrdinalEnclaveMetadata", resourceCulture); } } - - // Looks up a localized string similar to Internal error. Error occurred when populating parameter metadata. The referenced column encryption key ordinal '{0}' is missing in the encryption metadata returned by SQL Server. Max ordinal is '{1}'.. - internal static string TCE_InvalidEncryptionKeyOrdinalParameterMetadata { - get { + + /// + /// Looks up a localized string similar to Internal error. Error occurred when populating parameter metadata. The referenced column encryption key ordinal '{0}' is missing in the encryption metadata returned by SQL Server. Max ordinal is '{1}'.. + /// + internal static string TCE_InvalidEncryptionKeyOrdinalParameterMetadata + { + get + { return ResourceManager.GetString("TCE_InvalidEncryptionKeyOrdinalParameterMetadata", resourceCulture); } } - - // Looks up a localized string similar to Encryption type '{1}' specified for the column in the database is either invalid or corrupted. Valid encryption types for algorithm '{0}' are: {2}.. - internal static string TCE_InvalidEncryptionType { - get { + + /// + /// Looks up a localized string similar to Encryption type '{1}' specified for the column in the database is either invalid or corrupted. Valid encryption types for algorithm '{0}' are: {2}.. + /// + internal static string TCE_InvalidEncryptionType + { + get + { return ResourceManager.GetString("TCE_InvalidEncryptionType", resourceCulture); } } - - // Looks up a localized string similar to Invalid key encryption algorithm specified: '{0}'. Expected value: '{1}'.. - internal static string TCE_InvalidKeyEncryptionAlgorithm { - get { + + /// + /// Looks up a localized string similar to Invalid key encryption algorithm specified: '{0}'. Expected value: '{1}'.. + /// + internal static string TCE_InvalidKeyEncryptionAlgorithm + { + get + { return ResourceManager.GetString("TCE_InvalidKeyEncryptionAlgorithm", resourceCulture); } } - - // Looks up a localized string similar to Internal error. Invalid key encryption algorithm specified: '{0}'. Expected value: '{1}'.. - internal static string TCE_InvalidKeyEncryptionAlgorithmSysErr { - get { + + /// + /// Looks up a localized string similar to Internal error. Invalid key encryption algorithm specified: '{0}'. Expected value: '{1}'.. + /// + internal static string TCE_InvalidKeyEncryptionAlgorithmSysErr + { + get + { return ResourceManager.GetString("TCE_InvalidKeyEncryptionAlgorithmSysErr", resourceCulture); } } - - // Looks up a localized string similar to Internal Error. The given key id '{0}' is not valid. Error occurred when converting the key id to unsigned short. Error Message: {1}. - internal static string TCE_InvalidKeyIdUnableToCastToUnsignedShort { - get { + + /// + /// Looks up a localized string similar to Internal Error. The given key id '{0}' is not valid. Error occurred when converting the key id to unsigned short. Error Message: {1}. + /// + internal static string TCE_InvalidKeyIdUnableToCastToUnsignedShort + { + get + { return ResourceManager.GetString("TCE_InvalidKeyIdUnableToCastToUnsignedShort", resourceCulture); } } - - // Looks up a localized string similar to The column encryption key has been successfully decrypted but it's length: {1} does not match the length: {2} for algorithm '{0}'. Verify the encrypted value of the column encryption key in the database.. - internal static string TCE_InvalidKeySize { - get { + + /// + /// Looks up a localized string similar to The column encryption key has been successfully decrypted but it's length: {1} does not match the length: {2} for algorithm '{0}'. Verify the encrypted value of the column encryption key in the database.. + /// + internal static string TCE_InvalidKeySize + { + get + { return ResourceManager.GetString("TCE_InvalidKeySize", resourceCulture); } } - - // Looks up a localized string similar to Invalid key store provider name: '{0}'. A key store provider name must denote either a system key store provider or a registered custom key store provider. Valid system key store provider names are: {1}. Valid (currently registered) custom key store provider names are: {2}. Please verify key store provider information in column master key definitions in the database, and verify all custom key store providers used in your application are registered properly.. - internal static string TCE_InvalidKeyStoreProviderName { - get { + + /// + /// Looks up a localized string similar to Invalid key store provider name: '{0}'. A key store provider name must denote either a system key store provider or a registered custom key store provider. Valid system key store provider names are: {1}. Valid (currently registered) custom key store provider names are: {2}. Please verify key store provider information in column master key definitions in the database, and verify all custom key store providers used in your application are registered properly.. + /// + internal static string TCE_InvalidKeyStoreProviderName + { + get + { return ResourceManager.GetString("TCE_InvalidKeyStoreProviderName", resourceCulture); } } - - // Looks up a localized string similar to The specified encrypted column encryption key signature does not match the signature computed with the column master key (asymmetric key) in '{0}'. The encrypted column encryption key may be corrupt, or the specified path may be incorrect.. - internal static string TCE_InvalidSignature { - get { + + /// + /// Looks up a localized string similar to The specified encrypted column encryption key signature does not match the signature computed with the column master key (asymmetric key) in '{0}'. The encrypted column encryption key may be corrupt, or the specified path may be incorrect.. + /// + internal static string TCE_InvalidSignature + { + get + { return ResourceManager.GetString("TCE_InvalidSignature", resourceCulture); } } - - // Looks up a localized string similar to The specified encrypted column encryption key's signature length: {0} does not match the signature length: {1} when using column master key (certificate) in '{2}'. The encrypted column encryption key may be corrupt, or the specified certificate path may be incorrect.. - internal static string TCE_InvalidSignatureInEncryptedCEK { - get { + + /// + /// Looks up a localized string similar to The specified encrypted column encryption key's signature length: {0} does not match the signature length: {1} when using column master key (certificate) in '{2}'. The encrypted column encryption key may be corrupt, or the specified certificate path may be incorrect.. + /// + internal static string TCE_InvalidSignatureInEncryptedCEK + { + get + { return ResourceManager.GetString("TCE_InvalidSignatureInEncryptedCEK", resourceCulture); } } - - // Looks up a localized string similar to The specified encrypted column encryption key's signature length: {0} does not match the signature length: {1} when using column master key (asymmetric key) in '{2}'. The encrypted column encryption key may be corrupt, or the specified Microsoft Cryptography API: Next Generation (CNG) provider path may be incorrect.. - internal static string TCE_InvalidSignatureInEncryptedCEKCng { - get { + + /// + /// Looks up a localized string similar to The specified encrypted column encryption key's signature length: {0} does not match the signature length: {1} when using column master key (asymmetric key) in '{2}'. The encrypted column encryption key may be corrupt, or the specified Microsoft Cryptography API: Next Generation (CNG) provider path may be incorrect.. + /// + internal static string TCE_InvalidSignatureInEncryptedCEKCng + { + get + { return ResourceManager.GetString("TCE_InvalidSignatureInEncryptedCEKCng", resourceCulture); } } - - // Looks up a localized string similar to The specified encrypted column encryption key's signature length: {0} does not match the signature length: {1} when using column master key (asymmetric key) in '{2}'. The encrypted column encryption key may be corrupt, or the specified Microsoft cryptographic service provider (CSP) path may be incorrect.. + + /// + /// Looks up a localized string similar to The specified encrypted column encryption key's signature length: {0} does not match the signature length: {1} when using column master key (asymmetric key) in '{2}'. The encrypted column encryption key may be corrupt, or the specified Microsoft cryptographic service provider (CSP) path may be incorrect.. + /// internal static string TCE_InvalidSignatureInEncryptedCEKCsp { get { return ResourceManager.GetString("TCE_InvalidSignatureInEncryptedCEKCsp", resourceCulture); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs index 0190404d41..fdec403870 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs @@ -11,6 +11,9 @@ namespace Microsoft.Data.SqlClient { + /// + /// A delegate for communicating with secure enclave + /// internal class EnclaveDelegate { @@ -32,6 +35,16 @@ internal class EnclaveDelegate private EnclaveDelegate() { } + /// + /// Generate the byte package that needs to be sent to the enclave + /// + /// attestation protocol + /// Keys to be sent to enclave + /// + /// enclave type + /// server name + /// url for attestation endpoint + /// internal EnclavePackage GenerateEnclavePackage(SqlConnectionAttestationProtocol attestationProtocol, Dictionary keysTobeSentToEnclave, string queryText, string enclaveType, string serverName, string enclaveAttestationUrl) { @@ -75,7 +88,7 @@ private void GetEnclaveSession(SqlConnectionAttestationProtocol attestationProto if (throwIfNull && sqlEnclaveSession == null) { - throw SQL.NullEnclaveSessionDuringQueryExecution(enclaveType, enclaveAttestationUrl); + throw SQL.NullEnclaveSessionDuringQueryExecution(enclaveType, enclaveAttestationUrl); } } @@ -139,6 +152,15 @@ private byte[] GetUintBytes(string enclaveType, int intValue, string variableNam } } + /// + /// Create a new enclave session + /// + /// attestation protocol + /// enclave type + /// servername + /// attestation url for attestation service endpoint + /// attestation info from SQL Server + /// attestation parameters internal void CreateEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, string serverName, string attestationUrl, byte[] attestationInfo, SqlEnclaveAttestationParameters attestationParameters) { @@ -210,6 +232,12 @@ private string ConvertAttestationProtocolToString(SqlConnectionAttestationProtoc } } + /// + /// Decrypt the keys that need to be sent to the enclave + /// + /// Keys that need to sent to the enclave + /// + /// private List GetDecryptedKeysToBeSentToEnclave(Dictionary keysTobeSentToEnclave, string serverName) { List decryptedKeysToBeSentToEnclave = new List(); @@ -238,6 +266,13 @@ private List GetDecryptedKeysToBeSentToEnclave(Dictiona return decryptedKeysToBeSentToEnclave; } + /// + /// Generate a byte package consisting of decrypted keys and some headers expected by the enclave + /// + /// counter to avoid replay attacks + /// + /// + /// private byte[] GenerateBytePackageForKeys(long enclaveSessionCounter, byte[] queryStringHashBytes, List keys) { @@ -275,6 +310,13 @@ private byte[] GenerateBytePackageForKeys(long enclaveSessionCounter, byte[] que return bytePackage; } + /// + /// Encrypt the byte package containing keys with the session key + /// + /// byte package containing keys + /// session key used to encrypt the package + /// server hosting the enclave + /// private byte[] EncryptBytePackage(byte[] bytePackage, byte[] sessionKey, string serverName) { if (sessionKey == null) @@ -297,6 +339,11 @@ private byte[] EncryptBytePackage(byte[] bytePackage, byte[] sessionKey, string } } + /// + /// Combine the array of given byte arrays into one + /// + /// byte arrays to be combined + /// private byte[] CombineByteArrays(byte[][] byteArraysToCombine) { byte[] combinedArray = new byte[byteArraysToCombine.Sum(ba => ba.Length)]; @@ -337,17 +384,28 @@ private byte[] ComputeQueryStringHash(string queryString) return hash; } + /// + /// Exception when executing a enclave based Always Encrypted query + /// internal class RetriableEnclaveQueryExecutionException : Exception { internal RetriableEnclaveQueryExecutionException(string message, Exception innerException) : base(message, innerException) { } } + /// + /// Class encapsulating necessary information about the byte package that needs to be sent to the enclave + /// internal class EnclavePackage { public SqlEnclaveSession EnclaveSession { get; } public byte[] EnclavePackageBytes { get; } + /// + /// Constructor + /// + /// byte package to be sent to enclave + /// enclave session to be used internal EnclavePackage(byte[] enclavePackageBytes, SqlEnclaveSession enclaveSession) { EnclavePackageBytes = enclavePackageBytes; 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 085105db89..31b13cee19 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 @@ -45,33 +45,39 @@ public sealed class SqlCommand : DbCommand, ICloneable private UpdateRowSource _updatedRowSource = UpdateRowSource.Both; private bool _designTimeInvisible; - - // Indicates if the column encryption setting was set at-least once in the batch rpc mode, when using AddBatchCommand. - + /// + /// Indicates if the column encryption setting was set at-least once in the batch rpc mode, when using AddBatchCommand. + /// private bool _wasBatchModeColumnEncryptionSettingSetOnce; - - // Column Encryption Override. Defaults to SqlConnectionSetting, in which case - // it will be Enabled if SqlConnectionOptions.IsColumnEncryptionSettingEnabled = true, Disabled if false. - // This may also be used to set other behavior which overrides connection level setting. - + /// + /// Column Encryption Override. Defaults to SqlConnectionSetting, in which case + /// it will be Enabled if SqlConnectionOptions.IsColumnEncryptionSettingEnabled = true, Disabled if false. + /// This may also be used to set other behavior which overrides connection level setting. + /// private SqlCommandColumnEncryptionSetting _columnEncryptionSetting = SqlCommandColumnEncryptionSetting.UseConnectionSetting; internal SqlDependency _sqlDep; #if DEBUG - - // Force the client to sleep during sp_describe_parameter_encryption in the function TryFetchInputParameterEncryptionInfo. + /// + /// Force the client to sleep during sp_describe_parameter_encryption in the function TryFetchInputParameterEncryptionInfo. + /// private static bool _sleepDuringTryFetchInputParameterEncryptionInfo = false; - - // Force the client to sleep during sp_describe_parameter_encryption in the function RunExecuteReaderTds. + /// + /// Force the client to sleep during sp_describe_parameter_encryption in the function RunExecuteReaderTds. + /// private static bool _sleepDuringRunExecuteReaderTdsForSpDescribeParameterEncryption = false; - // Force the client to sleep during sp_describe_parameter_encryption after ReadDescribeEncryptionParameterResults. + /// + /// Force the client to sleep during sp_describe_parameter_encryption after ReadDescribeEncryptionParameterResults. + /// private static bool _sleepAfterReadDescribeEncryptionParameterResults = false; - // Internal flag for testing purposes that forces all queries to internally end async calls. + /// + /// Internal flag for testing purposes that forces all queries to internally end async calls. + /// private static bool _forceInternalEndQuery = false; #endif @@ -134,11 +140,12 @@ internal bool InPrepare } } - - // Return if column encryption setting is enabled. - // The order in the below if is important since _activeConnection.Parser can throw if the - // underlying tds connection is closed and we don't want to change the behavior for folks - // not trying to use transparent parameter encryption i.e. who don't use (SqlCommandColumnEncryptionSetting.Enabled or _activeConnection.IsColumnEncryptionSettingEnabled) here. + /// + /// Return if column encryption setting is enabled. + /// The order in the below if is important since _activeConnection.Parser can throw if the + /// underlying tds connection is closed and we don't want to change the behavior for folks + /// not trying to use transparent parameter encryption i.e. who don't use (SqlCommandColumnEncryptionSetting.Enabled or _activeConnection.IsColumnEncryptionSettingEnabled) here. + /// internal bool IsColumnEncryptionEnabled { get @@ -289,16 +296,21 @@ private CachedAsyncState cachedAsyncState private List _parameterCollectionList; private int _currentlyExecutingBatch; - - // This variable is used to keep track of which RPC batch's results are being read when reading the results of - // describe parameter encryption RPC requests in BatchRPCMode. + /// + /// This variable is used to keep track of which RPC batch's results are being read when reading the results of + /// describe parameter encryption RPC requests in BatchRPCMode. + /// private int _currentlyExecutingDescribeParameterEncryptionRPC; - // A flag to indicate if we have in-progress describe parameter encryption RPC requests. - // Reset to false when completed. + /// + /// A flag to indicate if we have in-progress describe parameter encryption RPC requests. + /// Reset to false when completed. + /// private bool _isDescribeParameterEncryptionRPCCurrentlyInProgress; - // Return the flag that indicates if describe parameter encryption RPC requests are in-progress. + /// + /// Return the flag that indicates if describe parameter encryption RPC requests are in-progress. + /// internal bool IsDescribeParameterEncryptionRPCCurrentlyInProgress { get @@ -307,10 +319,14 @@ internal bool IsDescribeParameterEncryptionRPCCurrentlyInProgress } } - // A flag to indicate if EndExecute was already initiated by the Begin call. + /// + /// A flag to indicate if EndExecute was already initiated by the Begin call. + /// private volatile bool _internalEndExecuteInitiated; - // A flag to indicate whether we postponed caching the query metadata for this command. + /// + /// A flag to indicate whether we postponed caching the query metadata for this command. + /// internal bool CachingQueryMetadataPostponed { get; set; } // @@ -521,8 +537,8 @@ private SqlCommand(SqlCommand from) : this() { tdsReliabilitySection.Start(); #endif //DEBUG - // cleanup - Unprepare(); + // cleanup + Unprepare(); #if DEBUG } finally @@ -3828,9 +3844,11 @@ private void RunExecuteNonQuerySmi(bool sendToPipe) } } - // Resets the encryption related state of the command object and each of the parameters. - // BatchRPC doesn't need special handling to cleanup the state of each RPC object and its parameters since a new RPC object and - // parameters are generated on every execution. + /// + /// Resets the encryption related state of the command object and each of the parameters. + /// BatchRPC doesn't need special handling to cleanup the state of each RPC object and its parameters since a new RPC object and + /// parameters are generated on every execution. + /// private void ResetEncryptionState() { // First reset the command level state. @@ -3858,7 +3876,9 @@ private void ResetEncryptionState() enclaveAttestationParameters = null; } - // Steps to be executed in the Prepare Transparent Encryption finally block. + /// + /// Steps to be executed in the Prepare Transparent Encryption finally block. + /// private void PrepareTransparentEncryptionFinallyBlock(bool closeDataReader, bool clearDataStructures, bool decrementAsyncCount, @@ -3897,8 +3917,20 @@ private void PrepareTransparentEncryptionFinallyBlock(bool closeDataReader, } } - // Executes the reader after checking to see if we need to encrypt input parameters and then encrypting it if required. - // TryFetchInputParameterEncryptionInfo() -> ReadDescribeEncryptionParameterResults()-> EncryptInputParameters() ->RunExecuteReaderTds() + /// + /// Executes the reader after checking to see if we need to encrypt input parameters and then encrypting it if required. + /// TryFetchInputParameterEncryptionInfo() -> ReadDescribeEncryptionParameterResults()-> EncryptInputParameters() ->RunExecuteReaderTds() + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// private void PrepareForTransparentEncryption(CommandBehavior cmdBehavior, bool returnStream, bool async, int timeout, TaskCompletionSource completion, out Task returnTask, bool asyncWrite, out bool usedCache, bool inRetry) { // Fetch reader with input params @@ -4010,24 +4042,24 @@ private void PrepareForTransparentEncryption(CommandBehavior cmdBehavior, bool r { tdsReliabilitySectionAsync.Start(); #endif //DEBUG - // Check for any exceptions on network write, before reading. - CheckThrowSNIException(); + // Check for any exceptions on network write, before reading. + CheckThrowSNIException(); - // If it is async, then TryFetchInputParameterEncryptionInfo-> RunExecuteReaderTds would have incremented the async count. - // Decrement it when we are about to complete async execute reader. - SqlInternalConnectionTds internalConnectionTds = _activeConnection.GetOpenTdsConnection(); - if (internalConnectionTds != null) - { - internalConnectionTds.DecrementAsyncCount(); - decrementAsyncCountInFinallyBlockAsync = false; - } + // If it is async, then TryFetchInputParameterEncryptionInfo-> RunExecuteReaderTds would have incremented the async count. + // Decrement it when we are about to complete async execute reader. + SqlInternalConnectionTds internalConnectionTds = _activeConnection.GetOpenTdsConnection(); + if (internalConnectionTds != null) + { + internalConnectionTds.DecrementAsyncCount(); + decrementAsyncCountInFinallyBlockAsync = false; + } - // Complete executereader. - describeParameterEncryptionDataReader = CompleteAsyncExecuteReader(forDescribeParameterEncryption: true); - Debug.Assert(null == _stateObj, "non-null state object in PrepareForTransparentEncryption."); + // Complete executereader. + describeParameterEncryptionDataReader = CompleteAsyncExecuteReader(forDescribeParameterEncryption: true); + Debug.Assert(null == _stateObj, "non-null state object in PrepareForTransparentEncryption."); - // Read the results of describe parameter encryption. - ReadDescribeEncryptionParameterResults(describeParameterEncryptionDataReader, describeParameterEncryptionRpcOriginalRpcMap); + // Read the results of describe parameter encryption. + ReadDescribeEncryptionParameterResults(describeParameterEncryptionDataReader, describeParameterEncryptionRpcOriginalRpcMap); #if DEBUG // Failpoint to force the thread to halt to simulate cancellation of SqlCommand. @@ -4095,24 +4127,24 @@ private void PrepareForTransparentEncryption(CommandBehavior cmdBehavior, bool r tdsReliabilitySectionAsync.Start(); #endif //DEBUG - // Check for any exceptions on network write, before reading. - CheckThrowSNIException(); + // Check for any exceptions on network write, before reading. + CheckThrowSNIException(); - // If it is async, then TryFetchInputParameterEncryptionInfo-> RunExecuteReaderTds would have incremented the async count. - // Decrement it when we are about to complete async execute reader. - SqlInternalConnectionTds internalConnectionTds = _activeConnection.GetOpenTdsConnection(); - if (internalConnectionTds != null) - { - internalConnectionTds.DecrementAsyncCount(); - decrementAsyncCountInFinallyBlockAsync = false; - } + // If it is async, then TryFetchInputParameterEncryptionInfo-> RunExecuteReaderTds would have incremented the async count. + // Decrement it when we are about to complete async execute reader. + SqlInternalConnectionTds internalConnectionTds = _activeConnection.GetOpenTdsConnection(); + if (internalConnectionTds != null) + { + internalConnectionTds.DecrementAsyncCount(); + decrementAsyncCountInFinallyBlockAsync = false; + } - // Complete executereader. - describeParameterEncryptionDataReader = CompleteAsyncExecuteReader(forDescribeParameterEncryption: true); - Debug.Assert(null == _stateObj, "non-null state object in PrepareForTransparentEncryption."); + // Complete executereader. + describeParameterEncryptionDataReader = CompleteAsyncExecuteReader(forDescribeParameterEncryption: true); + Debug.Assert(null == _stateObj, "non-null state object in PrepareForTransparentEncryption."); - // Read the results of describe parameter encryption. - ReadDescribeEncryptionParameterResults(describeParameterEncryptionDataReader, describeParameterEncryptionRpcOriginalRpcMap); + // Read the results of describe parameter encryption. + ReadDescribeEncryptionParameterResults(describeParameterEncryptionDataReader, describeParameterEncryptionRpcOriginalRpcMap); #if DEBUG // Failpoint to force the thread to halt to simulate cancellation of SqlCommand. if (_sleepAfterReadDescribeEncryptionParameterResults) @@ -4217,8 +4249,17 @@ private void PrepareForTransparentEncryption(CommandBehavior cmdBehavior, bool r } } - // Executes an RPC to fetch param encryption info from SQL Engine. If this method is not done writing - // the request to wire, it'll set the "task" parameter which can be used to create continuations. + /// + /// Executes an RPC to fetch param encryption info from SQL Engine. If this method is not done writing + /// the request to wire, it'll set the "task" parameter which can be used to create continuations. + /// + /// + /// + /// + /// + /// + /// + /// private SqlDataReader TryFetchInputParameterEncryptionInfo(int timeout, bool async, bool asyncWrite, @@ -4352,7 +4393,11 @@ private SqlDataReader TryFetchInputParameterEncryptionInfo(int timeout, } } - // Constructs a SqlParameter with a given string value + /// + /// Constructs a SqlParameter with a given string value + /// + /// + /// private SqlParameter GetSqlParameterWithQueryText(string queryText) { SqlParameter sqlParam = new SqlParameter(null, ((queryText.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT) ? SqlDbType.NVarChar : SqlDbType.NText, queryText.Length); @@ -4361,9 +4406,14 @@ private SqlParameter GetSqlParameterWithQueryText(string queryText) return sqlParam; } - // Constructs the sp_describe_parameter_encryption request with the values from the original RPC call. - // Prototype for <sp_describe_parameter_encryption> is - // exec sp_describe_parameter_encryption @tsql=N'[SQL Statement]', @params=N'@p1 varbinary(256)' + /// + /// Constructs the sp_describe_parameter_encryption request with the values from the original RPC call. + /// Prototype for <sp_describe_parameter_encryption> is + /// exec sp_describe_parameter_encryption @tsql=N'[SQL Statement]', @params=N'@p1 varbinary(256)' + /// + /// + /// + /// private void PrepareDescribeParameterEncryptionRequest(_SqlRPC originalRpcRequest, ref _SqlRPC describeParameterEncryptionRequest, byte[] attestationParameters = null) { Debug.Assert(originalRpcRequest != null); @@ -4474,7 +4524,11 @@ private void PrepareDescribeParameterEncryptionRequest(_SqlRPC originalRpcReques } } - // Read the output of sp_describe_parameter_encryption + /// + /// Read the output of sp_describe_parameter_encryption + /// + /// Resultset from calling to sp_describe_parameter_encryption + /// Readonly dictionary with the map of parameter encryption rpc requests with the corresponding original rpc requests. private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDictionary<_SqlRPC, _SqlRPC> describeParameterEncryptionRpcOriginalRpcMap) { _SqlRPC rpc = null; @@ -4891,7 +4945,7 @@ internal SqlDataReader RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior if (ShouldUseEnclaveBasedWorkflow && this.enclavePackage != null) { - EnclaveDelegate.Instance.InvalidateEnclaveSession(this._activeConnection.AttestationProtocol, this._activeConnection.Parser.EnclaveType, + EnclaveDelegate.Instance.InvalidateEnclaveSession(this._activeConnection.AttestationProtocol, this._activeConnection.Parser.EnclaveType, this._activeConnection.DataSource, this._activeConnection.EnclaveAttestationUrl, this.enclavePackage.EnclaveSession); } @@ -4973,7 +5027,21 @@ internal SqlDataReader RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior } } - // RunExecuteReaderTds after Transparent Parameter Encryption is complete. + /// + /// RunExecuteReaderTds after Transparent Parameter Encryption is complete. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// private SqlDataReader RunExecuteReaderTdsWithTransparentParameterEncryption(CommandBehavior cmdBehavior, RunBehavior runBehavior, bool returnStream, @@ -5857,8 +5925,10 @@ private void PutStateObject() } } - // IMPORTANT NOTE: This is created as a copy of OnDoneProc below for Transparent Column Encryption improvement - // as there is not much time, to address regressions. Will revisit removing the duplication, when we have time again. + /// + /// IMPORTANT NOTE: This is created as a copy of OnDoneProc below for Transparent Column Encryption improvement + /// as there is not much time, to address regressions. Will revisit removing the duplication, when we have time again. + /// internal void OnDoneDescribeParameterEncryptionProc(TdsParserStateObject stateObj) { // called per rpc batch complete @@ -5895,8 +5965,10 @@ internal void OnDoneDescribeParameterEncryptionProc(TdsParserStateObject stateOb } } - // IMPORTANT NOTE: There is a copy of this function above in OnDoneDescribeParameterEncryptionProc. - // Please consider the changes being done in this function for the above function as well. + /// + /// IMPORTANT NOTE: There is a copy of this function above in OnDoneDescribeParameterEncryptionProc. + /// Please consider the changes being done in this function for the above function as well. + /// internal void OnDoneProc() { // called per rpc batch complete if (BatchRPCMode) @@ -6591,9 +6663,14 @@ private void BuildExecuteSql(CommandBehavior behavior, string commandText, SqlPa } } - // This function constructs a string parameter containing the exec statement in the following format - // N'EXEC sp_name @param1=@param1, @param1=@param2, ..., @paramN=@paramN' - // TODO: Need to handle return values. + /// + /// This function constructs a string parameter containing the exec statement in the following format + /// N'EXEC sp_name @param1=@param1, @param1=@param2, ..., @paramN=@paramN' + /// TODO: Need to handle return values. + /// + /// Stored procedure name + /// SqlParameter list + /// A string SqlParameter containing the constructed sql statement value private SqlParameter BuildStoredProcedureStatementForColumnEncryption(string storedProcedureName, SqlParameter[] parameters) { Debug.Assert(CommandType == CommandType.StoredProcedure, "BuildStoredProcedureStatementForColumnEncryption() should only be called for stored procedures"); @@ -7006,8 +7083,10 @@ internal bool IsDirty } } - // Get or set the number of records affected by SpDescribeParameterEncryption. - // The below line is used only for debug asserts and not exposed publicly or impacts functionality otherwise. + /// + /// Get or set the number of records affected by SpDescribeParameterEncryption. + /// The below line is used only for debug asserts and not exposed publicly or impacts functionality otherwise. + /// internal int RowsAffectedByDescribeParameterEncryption { get @@ -7074,7 +7153,9 @@ internal bool BatchRPCMode } } - // Clear the state in sqlcommand related to describe parameter encryption RPC requests. + /// + /// Clear the state in sqlcommand related to describe parameter encryption RPC requests. + /// private void ClearDescribeParameterEncryptionRequests() { _sqlRPCParameterEncryptionReqArray = null; @@ -7098,8 +7179,10 @@ internal void ClearBatchCommand() _currentlyExecutingBatch = 0; } - // Set the column encryption setting to the new one. - // Do not allow conflicting column encryption settings. + /// + /// Set the column encryption setting to the new one. + /// Do not allow conflicting column encryption settings. + /// private void SetColumnEncryptionSetting(SqlCommandColumnEncryptionSetting newColumnEncryptionSetting) { if (!this._wasBatchModeColumnEncryptionSettingSetOnce) @@ -7377,7 +7460,12 @@ private void WriteBeginExecuteEvent() } } - // Writes and end execute event in Event Source. + /// + /// Writes and end execute event in Event Source. + /// + /// True if SQL command finished successfully, otherwise false. + /// Gets a number that identifies the type of error. + /// True if SQL command was executed synchronously, otherwise false. private void WriteEndExecuteEvent(bool success, int? sqlExceptionNumber, bool synchronous) { if (SqlEventSource.Log.IsEnabled()) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionString.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionString.cs index 3e9f4ff42b..9249f01995 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionString.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionString.cs @@ -971,7 +971,10 @@ internal SqlAuthenticationMethod ConvertValueToAuthenticationType() } } - // Convert the value to SqlConnectionColumnEncryptionSetting. + /// + /// Convert the value to SqlConnectionColumnEncryptionSetting. + /// + /// internal SqlConnectionColumnEncryptionSetting ConvertValueToColumnEncryptionSetting() { object value = base.Parsetable[KEY.ColumnEncryptionSetting]; diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/Utility.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/Utility.cs index f413244e1a..22779b63c2 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/Utility.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/Utility.cs @@ -439,6 +439,11 @@ public override void WriteLine(string message) } } + /// + /// String to Byte array conversion. + /// + /// + /// internal static byte[] StringToByteArray(string hex) { Assert.True(!string.IsNullOrWhiteSpace(hex)); From a2888c28e6ab7224d16b55b2a0b802e159a6018c Mon Sep 17 00:00:00 2001 From: dengel Date: Thu, 31 Oct 2019 15:08:16 -0700 Subject: [PATCH 49/63] Reverting version change Reverting auto format change on last revert One other minor edit --- .../SqlClient/EnclaveDelegate.NetCoreApp.cs | 1 + .../netcore/src/Resources/SR.Designer.cs | 618 +++++++----------- 2 files changed, 232 insertions(+), 387 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetCoreApp.cs index 4de8b6c5e0..66c079ec22 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetCoreApp.cs @@ -1,6 +1,7 @@ // 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.Security.Cryptography; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs index 5dc46872ab..336b21c1f5 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs @@ -19,7 +19,7 @@ namespace System { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class SR { @@ -4064,333 +4064,273 @@ internal static string TCE_AttestationInfoNotReturnedFromSQLServer { return ResourceManager.GetString("TCE_AttestationInfoNotReturnedFromSQLServer", resourceCulture); } } - + /// /// Looks up a localized string similar to {0} should be identical on all commands ({1}, {2}, {3}, {4}) when doing batch updates.. /// - internal static string TCE_BatchedUpdateColumnEncryptionSettingMismatch - { - get - { + internal static string TCE_BatchedUpdateColumnEncryptionSettingMismatch { + get { return ResourceManager.GetString("TCE_BatchedUpdateColumnEncryptionSettingMismatch", resourceCulture); } } - + /// /// Looks up a localized string similar to Failed to instantiate an enclave provider with type '{1}' for name '{0}'. Error message: {2}. /// - internal static string TCE_CannotCreateSqlColumnEncryptionEnclaveProvider - { - get - { + internal static string TCE_CannotCreateSqlColumnEncryptionEnclaveProvider { + get { return ResourceManager.GetString("TCE_CannotCreateSqlColumnEncryptionEnclaveProvider", resourceCulture); } } - + /// /// Looks up a localized string similar to Failed to read the configuration section for enclave providers. Make sure the section is correctly formatted in your application configuration file. Error Message: {0}. /// - internal static string TCE_CannotGetSqlColumnEncryptionEnclaveProviderConfig - { - get - { + internal static string TCE_CannotGetSqlColumnEncryptionEnclaveProviderConfig { + get { return ResourceManager.GetString("TCE_CannotGetSqlColumnEncryptionEnclaveProviderConfig", resourceCulture); } } - + /// /// Looks up a localized string similar to Key store providers cannot be set more than once.. /// - internal static string TCE_CanOnlyCallOnce - { - get - { + internal static string TCE_CanOnlyCallOnce { + get { return ResourceManager.GetString("TCE_CanOnlyCallOnce", resourceCulture); } } - + /// /// Looks up a localized string similar to Certificate with thumbprint '{0}' not found in certificate store '{1}' in certificate location '{2}'.. /// - internal static string TCE_CertificateNotFound - { - get - { + internal static string TCE_CertificateNotFound { + get { return ResourceManager.GetString("TCE_CertificateNotFound", resourceCulture); } } - + /// /// Looks up a localized string similar to Certificate with thumbprint '{0}' not found in certificate store '{1}' in certificate location '{2}'. Verify the certificate path in the column master key definition in the database is correct, and the certificate has been imported correctly into the certificate location/store.. /// - internal static string TCE_CertificateNotFoundSysErr - { - get - { + internal static string TCE_CertificateNotFoundSysErr { + get { return ResourceManager.GetString("TCE_CertificateNotFoundSysErr", resourceCulture); } } - + /// /// Looks up a localized string similar to Certificate specified in key path '{0}' does not have a private key to encrypt a column encryption key. Verify the certificate is imported correctly.. /// - internal static string TCE_CertificateWithNoPrivateKey - { - get - { + internal static string TCE_CertificateWithNoPrivateKey { + get { return ResourceManager.GetString("TCE_CertificateWithNoPrivateKey", resourceCulture); } } - + /// /// Looks up a localized string similar to Certificate specified in key path '{0}' does not have a private key to decrypt a column encryption key. Verify the certificate is imported correctly.. /// - internal static string TCE_CertificateWithNoPrivateKeySysErr - { - get - { + internal static string TCE_CertificateWithNoPrivateKeySysErr { + get { return ResourceManager.GetString("TCE_CertificateWithNoPrivateKeySysErr", resourceCulture); } } - + /// /// Looks up a localized string similar to Failed to decrypt column '{0}'.. /// - internal static string TCE_ColumnDecryptionFailed - { - get - { + internal static string TCE_ColumnDecryptionFailed { + get { return ResourceManager.GetString("TCE_ColumnDecryptionFailed", resourceCulture); } } - + /// /// Looks up a localized string similar to Internal Error. Encrypted column encryption keys not found when trying to send the keys to the enclave.. /// - internal static string TCE_ColumnEncryptionKeysNotFound - { - get - { + internal static string TCE_ColumnEncryptionKeysNotFound { + get { return ResourceManager.GetString("TCE_ColumnEncryptionKeysNotFound", resourceCulture); } } - + /// /// Looks up a localized string similar to Internal error. The signature returned by SQL Server for enclave-enabled column master key, specified at key path '{0}', cannot be null or empty.. /// - internal static string TCE_ColumnMasterKeySignatureNotFound - { - get - { + internal static string TCE_ColumnMasterKeySignatureNotFound { + get { return ResourceManager.GetString("TCE_ColumnMasterKeySignatureNotFound", resourceCulture); } } - + /// /// Looks up a localized string similar to The signature returned by SQL Server for the column master key, specified in key path '{0}', is invalid (does not match the computed signature). Recreate column master key metadata, making sure the signature inside the metadata is computed using the column master key being referenced in the metadata. If the error persists, please contact Microsoft for assistance.. /// - internal static string TCE_ColumnMasterKeySignatureVerificationFailed - { - get - { + internal static string TCE_ColumnMasterKeySignatureVerificationFailed { + get { return ResourceManager.GetString("TCE_ColumnMasterKeySignatureVerificationFailed", resourceCulture); } } - + /// /// Looks up a localized string similar to Decryption failed. The last 10 bytes of the encrypted column encryption key are: '{0}'. The first 10 bytes of ciphertext are: '{1}'.. /// - internal static string TCE_DecryptionFailed - { - get - { + internal static string TCE_DecryptionFailed { + get { return ResourceManager.GetString("TCE_DecryptionFailed", resourceCulture); } } - + /// /// Looks up a localized string similar to Internal Error. Empty argument '{0}' specified when constructing an object of type '{1}'. '{0}' cannot be empty.. /// - internal static string TCE_EmptyArgumentInConstructorInternal - { - get - { + internal static string TCE_EmptyArgumentInConstructorInternal { + get { return ResourceManager.GetString("TCE_EmptyArgumentInConstructorInternal", resourceCulture); } } - + /// /// Looks up a localized string similar to Internal Error. Argument '{0}' cannot be empty when executing method '{1}.{2}'.. /// - internal static string TCE_EmptyArgumentInternal - { - get - { + internal static string TCE_EmptyArgumentInternal { + get { return ResourceManager.GetString("TCE_EmptyArgumentInternal", resourceCulture); } } - + /// /// Looks up a localized string similar to Empty certificate thumbprint specified in certificate path '{0}'.. /// - internal static string TCE_EmptyCertificateThumbprint - { - get - { + internal static string TCE_EmptyCertificateThumbprint { + get { return ResourceManager.GetString("TCE_EmptyCertificateThumbprint", resourceCulture); } } - + /// /// Looks up a localized string similar to Internal error. Empty certificate thumbprint specified in certificate path '{0}'.. /// - internal static string TCE_EmptyCertificateThumbprintSysErr - { - get - { + internal static string TCE_EmptyCertificateThumbprintSysErr { + get { return ResourceManager.GetString("TCE_EmptyCertificateThumbprintSysErr", resourceCulture); } } - + /// /// Looks up a localized string similar to Empty key identifier specified in column master key path: '{0}'. Use the following format for a key stored in a Microsoft Cryptography API: Next Generation (CNG) provider: <CNG Provider Name>{1}<Key Identifier>.. /// - internal static string TCE_EmptyCngKeyId - { - get - { + internal static string TCE_EmptyCngKeyId { + get { return ResourceManager.GetString("TCE_EmptyCngKeyId", resourceCulture); } } - + /// /// Looks up a localized string similar to Internal error. Empty key identifier specified in column master key path: '{0}'. Use the following format for a key stored in a Microsoft Cryptography API: Next Generation (CNG) provider: <CNG Provider Name>{1}<Key Identifier>.. /// - internal static string TCE_EmptyCngKeyIdSysErr - { - get - { + internal static string TCE_EmptyCngKeyIdSysErr { + get { return ResourceManager.GetString("TCE_EmptyCngKeyIdSysErr", resourceCulture); } } - + /// /// Looks up a localized string similar to Empty Microsoft Cryptography API: Next Generation (CNG) provider name specified in column master key path: '{0}'. Use the following format for a key stored in a Microsoft Cryptography API: Next Generation (CNG) provider: <CNG Provider Name>{1}<Key Identifier>.. /// - internal static string TCE_EmptyCngName - { - get - { + internal static string TCE_EmptyCngName { + get { return ResourceManager.GetString("TCE_EmptyCngName", resourceCulture); } } - + /// /// Looks up a localized string similar to Internal error. Empty Microsoft Cryptography API: Next Generation (CNG) provider name specified in column master key path: '{0}'. Use the following format for a key stored in a Microsoft Cryptography API: Next Generation (CNG) provider: <CNG Provider Name>{1}<Key Identifier>.. /// - internal static string TCE_EmptyCngNameSysErr - { - get - { + internal static string TCE_EmptyCngNameSysErr { + get { return ResourceManager.GetString("TCE_EmptyCngNameSysErr", resourceCulture); } } - + /// /// Looks up a localized string similar to Empty column encryption key specified.. /// - internal static string TCE_EmptyColumnEncryptionKey - { - get - { + internal static string TCE_EmptyColumnEncryptionKey { + get { return ResourceManager.GetString("TCE_EmptyColumnEncryptionKey", resourceCulture); } } - + /// /// Looks up a localized string similar to Empty key identifier specified in column master key path: '{0}'. Use the following format for a key stored in a Microsoft cryptographic service provider (CSP): <CSP Provider Name>{1}<Key Identifier>.. /// - internal static string TCE_EmptyCspKeyId - { - get - { + internal static string TCE_EmptyCspKeyId { + get { return ResourceManager.GetString("TCE_EmptyCspKeyId", resourceCulture); } } - + /// /// Looks up a localized string similar to Internal error. Empty key identifier specified in column master key path: '{0}'. Use the following format for a key stored in a Microsoft cryptographic service provider (CSP): <CSP Provider Name>{1}<Key Identifier>.. /// - internal static string TCE_EmptyCspKeyIdSysErr - { - get - { + internal static string TCE_EmptyCspKeyIdSysErr { + get { return ResourceManager.GetString("TCE_EmptyCspKeyIdSysErr", resourceCulture); } } - + /// /// Looks up a localized string similar to Empty Microsoft cryptographic service provider (CSP) name specified in column master key path: '{0}'. Use the following format for a key stored in a Microsoft cryptographic service provider (CSP): <CSP Provider Name>{1}<Key Identifier>.. /// - internal static string TCE_EmptyCspName - { - get - { + internal static string TCE_EmptyCspName { + get { return ResourceManager.GetString("TCE_EmptyCspName", resourceCulture); } } - + /// /// Looks up a localized string similar to Internal error. Empty Microsoft cryptographic service provider (CSP) name specified in column master key path: '{0}'. Use the following format for a key stored in a Microsoft cryptographic service provider (CSP): <CSP Provider Name>{1}<Key Identifier>.. /// - internal static string TCE_EmptyCspNameSysErr - { - get - { + internal static string TCE_EmptyCspNameSysErr { + get { return ResourceManager.GetString("TCE_EmptyCspNameSysErr", resourceCulture); } } - + /// /// Looks up a localized string similar to Internal error. Empty encrypted column encryption key specified.. /// - internal static string TCE_EmptyEncryptedColumnEncryptionKey - { - get - { + internal static string TCE_EmptyEncryptedColumnEncryptionKey { + get { return ResourceManager.GetString("TCE_EmptyEncryptedColumnEncryptionKey", resourceCulture); } } - + /// /// Looks up a localized string similar to Invalid key store provider name specified. Key store provider names cannot be null or empty.. /// - internal static string TCE_EmptyProviderName - { - get - { + internal static string TCE_EmptyProviderName { + get { return ResourceManager.GetString("TCE_EmptyProviderName", resourceCulture); } } - + /// /// Looks up a localized string similar to You have specified the enclave attestation URL in the connection string, but the SQL Server instance in use does not support enclave based computations.. /// - internal static string TCE_EnclaveComputationsNotSupported - { - get - { + internal static string TCE_EnclaveComputationsNotSupported { + get { return ResourceManager.GetString("TCE_EnclaveComputationsNotSupported", resourceCulture); } } - + /// /// Looks up a localized string similar to No enclave provider found for enclave type '{0}'. Please specify the provider in the application configuration.. /// - internal static string TCE_EnclaveProviderNotFound - { - get - { + internal static string TCE_EnclaveProviderNotFound { + get { return ResourceManager.GetString("TCE_EnclaveProviderNotFound", resourceCulture); } } @@ -4398,10 +4338,8 @@ internal static string TCE_EnclaveProviderNotFound /// /// Looks up a localized string similar to Specifies an attestation protocol for its corresponding enclave attestation service. /// - internal static string TCE_DbConnectionString_AttestationProtocol - { - get - { + internal static string TCE_DbConnectionString_AttestationProtocol { + get { return ResourceManager.GetString("TCE_DbConnectionString_AttestationProtocol", resourceCulture); } } @@ -4409,10 +4347,8 @@ internal static string TCE_DbConnectionString_AttestationProtocol /// /// Looks up a localized string similar to The enclave type '{0}' returned from the server is not supported. /// - internal static string TCE_EnclaveTypeNotSupported - { - get - { + internal static string TCE_EnclaveTypeNotSupported { + get { return ResourceManager.GetString("TCE_EnclaveTypeNotSupported", resourceCulture); } } @@ -4420,10 +4356,8 @@ internal static string TCE_EnclaveTypeNotSupported /// /// Looks up a localized string similar to Failed to initialize connection. The attestation protocol '{0}' does not support the enclave type '{1}'. /// - internal static string TCE_AttestationProtocolNotSupportEnclaveType - { - get - { + internal static string TCE_AttestationProtocolNotSupportEnclaveType { + get { return ResourceManager.GetString("TCE_AttestationProtocolNotSupportEnclaveType", resourceCulture); } } @@ -4431,10 +4365,8 @@ internal static string TCE_AttestationProtocolNotSupportEnclaveType /// /// Looks up a localized string similar to Error occured when generating enclave package. Attestation Protocol has not been specified in the connection string, but the query requires enclave computations. /// - internal static string TCE_AttestationProtocolNotSpecifiedForGeneratingEnclavePackage - { - get - { + internal static string TCE_AttestationProtocolNotSpecifiedForGeneratingEnclavePackage { + get { return ResourceManager.GetString("TCE_AttestationProtocolNotSpecifiedForGeneratingEnclavePackage", resourceCulture); } } @@ -4442,487 +4374,399 @@ internal static string TCE_AttestationProtocolNotSpecifiedForGeneratingEnclavePa /// /// Looks up a localized string similar to Executing a query requires enclave computations, but the application configuration is missing the enclave provider section.. /// - internal static string TCE_EnclaveProvidersNotConfiguredForEnclaveBasedQuery - { - get - { + internal static string TCE_EnclaveProvidersNotConfiguredForEnclaveBasedQuery { + get { return ResourceManager.GetString("TCE_EnclaveProvidersNotConfiguredForEnclaveBasedQuery", resourceCulture); } } - + /// /// Looks up a localized string similar to You have specified the enclave attestation URL in the connection string, but the SQL Server instance did not return an enclave type. Please make sure the enclave type is correctly configured in your instance.. /// - internal static string TCE_EnclaveTypeNotReturned - { - get - { + internal static string TCE_EnclaveTypeNotReturned { + get { return ResourceManager.GetString("TCE_EnclaveTypeNotReturned", resourceCulture); } } - + /// /// Looks up a localized string similar to Internal Error. Enclave type received from SQL Server is null or empty when executing a query requiring enclave computations.. /// - internal static string TCE_EnclaveTypeNullForEnclaveBasedQuery - { - get - { + internal static string TCE_EnclaveTypeNullForEnclaveBasedQuery { + get { return ResourceManager.GetString("TCE_EnclaveTypeNullForEnclaveBasedQuery", resourceCulture); } } - + /// /// Looks up a localized string similar to Error encountered while generating package to be sent to enclave. Error message: {0}. /// - internal static string TCE_ExceptionWhenGeneratingEnclavePackage - { - get - { + internal static string TCE_ExceptionWhenGeneratingEnclavePackage { + get { return ResourceManager.GetString("TCE_ExceptionWhenGeneratingEnclavePackage", resourceCulture); } } - + /// /// Looks up a localized string similar to Internal Error. Failed to encrypt byte package to be sent to the enclave. Error Message: {0}. /// - internal static string TCE_FailedToEncryptRegisterRulesBytePackage - { - get - { + internal static string TCE_FailedToEncryptRegisterRulesBytePackage { + get { return ResourceManager.GetString("TCE_FailedToEncryptRegisterRulesBytePackage", resourceCulture); } } - + /// /// Looks up a localized string similar to Internal Error. The buffer specified by argument '{0}' for method '{1}.{2}' has insufficient space.. /// - internal static string TCE_InsufficientBuffer - { - get - { + internal static string TCE_InsufficientBuffer { + get { return ResourceManager.GetString("TCE_InsufficientBuffer", resourceCulture); } } - + /// /// Looks up a localized string similar to The specified ciphertext's encryption algorithm version '{0}' does not match the expected encryption algorithm version '{1}'.. /// - internal static string TCE_InvalidAlgorithmVersion - { - get - { + internal static string TCE_InvalidAlgorithmVersion { + get { return ResourceManager.GetString("TCE_InvalidAlgorithmVersion", resourceCulture); } } - + /// /// Looks up a localized string similar to Specified encrypted column encryption key contains an invalid encryption algorithm version '{0}'. Expected version is '{1}'.. /// - internal static string TCE_InvalidAlgorithmVersionInEncryptedCEK - { - get - { + internal static string TCE_InvalidAlgorithmVersionInEncryptedCEK { + get { return ResourceManager.GetString("TCE_InvalidAlgorithmVersionInEncryptedCEK", resourceCulture); } } - + /// /// Looks up a localized string similar to Invalid attestation parameters specified by the enclave provider for enclave type '{0}'. Error occurred when converting the value '{1}' of parameter '{2}' to unsigned int. Error Message: {3}. /// - internal static string TCE_InvalidAttestationParameterUnableToConvertToUnsignedInt - { - get - { + internal static string TCE_InvalidAttestationParameterUnableToConvertToUnsignedInt { + get { return ResourceManager.GetString("TCE_InvalidAttestationParameterUnableToConvertToUnsignedInt", resourceCulture); } } - + /// /// Looks up a localized string similar to Specified ciphertext has an invalid authentication tag.. /// - internal static string TCE_InvalidAuthenticationTag - { - get - { + internal static string TCE_InvalidAuthenticationTag { + get { return ResourceManager.GetString("TCE_InvalidAuthenticationTag", resourceCulture); } } - + /// /// Looks up a localized string similar to Invalid certificate location '{0}' in certificate path '{1}'. Use the following format: <certificate location>{4}<certificate store>{4}<certificate thumbprint>, where <certificate location> is either '{2}' or '{3}'.. /// - internal static string TCE_InvalidCertificateLocation - { - get - { + internal static string TCE_InvalidCertificateLocation { + get { return ResourceManager.GetString("TCE_InvalidCertificateLocation", resourceCulture); } } - + /// /// Looks up a localized string similar to Internal error. Invalid certificate location '{0}' in certificate path '{1}'. Use the following format: <certificate location>{4}<certificate store>{4}<certificate thumbprint>, where <certificate location> is either '{2}' or '{3}'.. /// - internal static string TCE_InvalidCertificateLocationSysErr - { - get - { + internal static string TCE_InvalidCertificateLocationSysErr { + get { return ResourceManager.GetString("TCE_InvalidCertificateLocationSysErr", resourceCulture); } } - + /// /// Looks up a localized string similar to Invalid certificate path: '{0}'. Use the following format: <certificate location>{3}<certificate store>{3}<certificate thumbprint>, where <certificate location> is either '{1}' or '{2}'.. /// - internal static string TCE_InvalidCertificatePath - { - get - { + internal static string TCE_InvalidCertificatePath { + get { return ResourceManager.GetString("TCE_InvalidCertificatePath", resourceCulture); } } - + /// /// Looks up a localized string similar to Internal error. Invalid certificate path: '{0}'. Use the following format: <certificate location>{3}<certificate store>{3}<certificate thumbprint>, where <certificate location> is either '{1}' or '{2}'.. /// - internal static string TCE_InvalidCertificatePathSysErr - { - get - { + internal static string TCE_InvalidCertificatePathSysErr { + get { return ResourceManager.GetString("TCE_InvalidCertificatePathSysErr", resourceCulture); } } - + /// /// Looks up a localized string similar to The specified encrypted column encryption key signature does not match the signature computed with the column master key (certificate) in '{0}'. The encrypted column encryption key may be corrupt, or the specified path may be incorrect.. /// - internal static string TCE_InvalidCertificateSignature - { - get - { + internal static string TCE_InvalidCertificateSignature { + get { return ResourceManager.GetString("TCE_InvalidCertificateSignature", resourceCulture); } } - + /// /// Looks up a localized string similar to Invalid certificate store '{0}' specified in certificate path '{1}'. Expected value: '{2}'.. /// - internal static string TCE_InvalidCertificateStore - { - get - { + internal static string TCE_InvalidCertificateStore { + get { return ResourceManager.GetString("TCE_InvalidCertificateStore", resourceCulture); } } - + /// /// Looks up a localized string similar to Internal error. Invalid certificate store '{0}' specified in certificate path '{1}'. Expected value: '{2}'.. /// - internal static string TCE_InvalidCertificateStoreSysErr - { - get - { + internal static string TCE_InvalidCertificateStoreSysErr { + get { return ResourceManager.GetString("TCE_InvalidCertificateStoreSysErr", resourceCulture); } } - + /// /// Looks up a localized string similar to The specified encrypted column encryption key's ciphertext length: {0} does not match the ciphertext length: {1} when using column master key (certificate) in '{2}'. The encrypted column encryption key may be corrupt, or the specified certificate path may be incorrect.. /// - internal static string TCE_InvalidCiphertextLengthInEncryptedCEK - { - get - { + internal static string TCE_InvalidCiphertextLengthInEncryptedCEK { + get { return ResourceManager.GetString("TCE_InvalidCiphertextLengthInEncryptedCEK", resourceCulture); } } - + /// /// Looks up a localized string similar to The specified encrypted column encryption key's ciphertext length: {0} does not match the ciphertext length: {1} when using column master key (asymmetric key) in '{2}'. The encrypted column encryption key may be corrupt, or the specified Microsoft Cryptography API: Next Generation (CNG) provider path may be incorrect.. /// - internal static string TCE_InvalidCiphertextLengthInEncryptedCEKCng - { - get - { + internal static string TCE_InvalidCiphertextLengthInEncryptedCEKCng { + get { return ResourceManager.GetString("TCE_InvalidCiphertextLengthInEncryptedCEKCng", resourceCulture); } } - + /// /// Looks up a localized string similar to The specified encrypted column encryption key's ciphertext length: {0} does not match the ciphertext length: {1} when using column master key (asymmetric key) in '{2}'. The encrypted column encryption key may be corrupt, or the specified Microsoft Cryptographic Service provider (CSP) path may be incorrect.. /// - internal static string TCE_InvalidCiphertextLengthInEncryptedCEKCsp - { - get - { + internal static string TCE_InvalidCiphertextLengthInEncryptedCEKCsp { + get { return ResourceManager.GetString("TCE_InvalidCiphertextLengthInEncryptedCEKCsp", resourceCulture); } } - + /// /// Looks up a localized string similar to Specified ciphertext has an invalid size of {0} bytes, which is below the minimum {1} bytes required for decryption.. /// - internal static string TCE_InvalidCipherTextSize - { - get - { + internal static string TCE_InvalidCipherTextSize { + get { return ResourceManager.GetString("TCE_InvalidCipherTextSize", resourceCulture); } } - + /// /// Looks up a localized string similar to An error occurred while opening the Microsoft Cryptography API: Next Generation (CNG) key: '{0}'. Verify that the CNG provider name '{1}' is valid, installed on the machine, and the key '{2}' exists.. /// - internal static string TCE_InvalidCngKey - { - get - { + internal static string TCE_InvalidCngKey { + get { return ResourceManager.GetString("TCE_InvalidCngKey", resourceCulture); } } - + /// /// Looks up a localized string similar to Internal error. An error occurred while opening the Microsoft Cryptography API: Next Generation (CNG) key: '{0}'. Verify that the CNG provider name '{1}' is valid, installed on the machine, and the key '{2}' exists.. /// - internal static string TCE_InvalidCngKeySysErr - { - get - { + internal static string TCE_InvalidCngKeySysErr { + get { return ResourceManager.GetString("TCE_InvalidCngKeySysErr", resourceCulture); } } - + /// /// Looks up a localized string similar to Invalid column master key path: '{0}'. Use the following format for a key stored in a Microsoft Cryptography API: Next Generation (CNG) provider: <CNG Provider Name>{1}<Key Identifier>.. /// - internal static string TCE_InvalidCngPath - { - get - { + internal static string TCE_InvalidCngPath { + get { return ResourceManager.GetString("TCE_InvalidCngPath", resourceCulture); } } - + /// /// Looks up a localized string similar to Internal error. Invalid column master key path: '{0}'. Use the following format for a key stored in a Microsoft Cryptography API: Next Generation (CNG) provider: <CNG Provider Name>{1}<Key Identifier>.. /// - internal static string TCE_InvalidCngPathSysErr - { - get - { + internal static string TCE_InvalidCngPathSysErr { + get { return ResourceManager.GetString("TCE_InvalidCngPathSysErr", resourceCulture); } } - + /// /// Looks up a localized string similar to Invalid key identifier: '{0}'. Verify that the key identifier in column master key path: '{1}' is valid and exists in the CSP.. /// - internal static string TCE_InvalidCspKeyId - { - get - { + internal static string TCE_InvalidCspKeyId { + get { return ResourceManager.GetString("TCE_InvalidCspKeyId", resourceCulture); } } - + /// /// Looks up a localized string similar to Internal error. Invalid key identifier: '{0}'. Verify that the key identifier in column master key path: '{1}' is valid and exists in the CSP.. /// - internal static string TCE_InvalidCspKeyIdSysErr - { - get - { + internal static string TCE_InvalidCspKeyIdSysErr { + get { return ResourceManager.GetString("TCE_InvalidCspKeyIdSysErr", resourceCulture); } } - + /// /// Looks up a localized string similar to Invalid Microsoft cryptographic service provider (CSP) name: '{0}'. Verify that the CSP provider name in column master key path: '{1}' is valid and installed on the machine.. /// - internal static string TCE_InvalidCspName - { - get - { + internal static string TCE_InvalidCspName { + get { return ResourceManager.GetString("TCE_InvalidCspName", resourceCulture); } } - + /// /// Looks up a localized string similar to Internal error. Invalid Microsoft cryptographic service provider (CSP) name: '{0}'. Verify that the CSP provider name in column master key path: '{1}' is valid and installed on the machine.. /// - internal static string TCE_InvalidCspNameSysErr - { - get - { + internal static string TCE_InvalidCspNameSysErr { + get { return ResourceManager.GetString("TCE_InvalidCspNameSysErr", resourceCulture); } } - + /// /// Looks up a localized string similar to Invalid column master key path: '{0}'. Use the following format for a key stored in a Microsoft cryptographic service provider (CSP): <CSP Provider Name>{1}<Key Identifier>.. /// - internal static string TCE_InvalidCspPath - { - get - { + internal static string TCE_InvalidCspPath { + get { return ResourceManager.GetString("TCE_InvalidCspPath", resourceCulture); } } - + /// /// Looks up a localized string similar to Internal error. Invalid column master key path: '{0}'. Use the following format for a key stored in a Microsoft cryptographic service provider (CSP): <CSP Provider Name>{1}<Key Identifier>.. /// - internal static string TCE_InvalidCspPathSysErr - { - get - { + internal static string TCE_InvalidCspPathSysErr { + get { return ResourceManager.GetString("TCE_InvalidCspPathSysErr", resourceCulture); } } - + /// /// Looks up a localized string similar to Invalid key store provider name '{0}'. '{1}' prefix is reserved for system key store providers.. /// - internal static string TCE_InvalidCustomKeyStoreProviderName - { - get - { + internal static string TCE_InvalidCustomKeyStoreProviderName { + get { return ResourceManager.GetString("TCE_InvalidCustomKeyStoreProviderName", resourceCulture); } } - + /// /// Looks up a localized string similar to Internal Error. The given database id '{0}' is not valid. Error occurred when converting the database id to unsigned int. Error Message: {1}. /// - internal static string TCE_InvalidDatabaseIdUnableToCastToUnsignedInt - { - get - { + internal static string TCE_InvalidDatabaseIdUnableToCastToUnsignedInt { + get { return ResourceManager.GetString("TCE_InvalidDatabaseIdUnableToCastToUnsignedInt", resourceCulture); } } - + /// /// Looks up a localized string similar to Internal error. Error occurred when populating enclave metadata. The referenced column encryption key ordinal '{0}' is missing in the encryption metadata returned by SQL Server. Max ordinal is '{1}'.. /// - internal static string TCE_InvalidEncryptionKeyOrdinalEnclaveMetadata - { - get - { + internal static string TCE_InvalidEncryptionKeyOrdinalEnclaveMetadata { + get { return ResourceManager.GetString("TCE_InvalidEncryptionKeyOrdinalEnclaveMetadata", resourceCulture); } } - + /// /// Looks up a localized string similar to Internal error. Error occurred when populating parameter metadata. The referenced column encryption key ordinal '{0}' is missing in the encryption metadata returned by SQL Server. Max ordinal is '{1}'.. /// - internal static string TCE_InvalidEncryptionKeyOrdinalParameterMetadata - { - get - { + internal static string TCE_InvalidEncryptionKeyOrdinalParameterMetadata { + get { return ResourceManager.GetString("TCE_InvalidEncryptionKeyOrdinalParameterMetadata", resourceCulture); } } - + /// /// Looks up a localized string similar to Encryption type '{1}' specified for the column in the database is either invalid or corrupted. Valid encryption types for algorithm '{0}' are: {2}.. /// - internal static string TCE_InvalidEncryptionType - { - get - { + internal static string TCE_InvalidEncryptionType { + get { return ResourceManager.GetString("TCE_InvalidEncryptionType", resourceCulture); } } - + /// /// Looks up a localized string similar to Invalid key encryption algorithm specified: '{0}'. Expected value: '{1}'.. /// - internal static string TCE_InvalidKeyEncryptionAlgorithm - { - get - { + internal static string TCE_InvalidKeyEncryptionAlgorithm { + get { return ResourceManager.GetString("TCE_InvalidKeyEncryptionAlgorithm", resourceCulture); } } - + /// /// Looks up a localized string similar to Internal error. Invalid key encryption algorithm specified: '{0}'. Expected value: '{1}'.. /// - internal static string TCE_InvalidKeyEncryptionAlgorithmSysErr - { - get - { + internal static string TCE_InvalidKeyEncryptionAlgorithmSysErr { + get { return ResourceManager.GetString("TCE_InvalidKeyEncryptionAlgorithmSysErr", resourceCulture); } } - + /// /// Looks up a localized string similar to Internal Error. The given key id '{0}' is not valid. Error occurred when converting the key id to unsigned short. Error Message: {1}. /// - internal static string TCE_InvalidKeyIdUnableToCastToUnsignedShort - { - get - { + internal static string TCE_InvalidKeyIdUnableToCastToUnsignedShort { + get { return ResourceManager.GetString("TCE_InvalidKeyIdUnableToCastToUnsignedShort", resourceCulture); } } - + /// /// Looks up a localized string similar to The column encryption key has been successfully decrypted but it's length: {1} does not match the length: {2} for algorithm '{0}'. Verify the encrypted value of the column encryption key in the database.. /// - internal static string TCE_InvalidKeySize - { - get - { + internal static string TCE_InvalidKeySize { + get { return ResourceManager.GetString("TCE_InvalidKeySize", resourceCulture); } } - + /// /// Looks up a localized string similar to Invalid key store provider name: '{0}'. A key store provider name must denote either a system key store provider or a registered custom key store provider. Valid system key store provider names are: {1}. Valid (currently registered) custom key store provider names are: {2}. Please verify key store provider information in column master key definitions in the database, and verify all custom key store providers used in your application are registered properly.. /// - internal static string TCE_InvalidKeyStoreProviderName - { - get - { + internal static string TCE_InvalidKeyStoreProviderName { + get { return ResourceManager.GetString("TCE_InvalidKeyStoreProviderName", resourceCulture); } } - + /// /// Looks up a localized string similar to The specified encrypted column encryption key signature does not match the signature computed with the column master key (asymmetric key) in '{0}'. The encrypted column encryption key may be corrupt, or the specified path may be incorrect.. /// - internal static string TCE_InvalidSignature - { - get - { + internal static string TCE_InvalidSignature { + get { return ResourceManager.GetString("TCE_InvalidSignature", resourceCulture); } } - + /// /// Looks up a localized string similar to The specified encrypted column encryption key's signature length: {0} does not match the signature length: {1} when using column master key (certificate) in '{2}'. The encrypted column encryption key may be corrupt, or the specified certificate path may be incorrect.. /// - internal static string TCE_InvalidSignatureInEncryptedCEK - { - get - { + internal static string TCE_InvalidSignatureInEncryptedCEK { + get { return ResourceManager.GetString("TCE_InvalidSignatureInEncryptedCEK", resourceCulture); } } - + /// /// Looks up a localized string similar to The specified encrypted column encryption key's signature length: {0} does not match the signature length: {1} when using column master key (asymmetric key) in '{2}'. The encrypted column encryption key may be corrupt, or the specified Microsoft Cryptography API: Next Generation (CNG) provider path may be incorrect.. /// - internal static string TCE_InvalidSignatureInEncryptedCEKCng - { - get - { + internal static string TCE_InvalidSignatureInEncryptedCEKCng { + get { return ResourceManager.GetString("TCE_InvalidSignatureInEncryptedCEKCng", resourceCulture); } } - + /// /// Looks up a localized string similar to The specified encrypted column encryption key's signature length: {0} does not match the signature length: {1} when using column master key (asymmetric key) in '{2}'. The encrypted column encryption key may be corrupt, or the specified Microsoft cryptographic service provider (CSP) path may be incorrect.. /// From 8b86dae50f69402f5be1e92c3b6800af911c9100 Mon Sep 17 00:00:00 2001 From: dengel Date: Thu, 31 Oct 2019 15:25:32 -0700 Subject: [PATCH 50/63] Minor formatting edits --- .../src/Microsoft/Data/Common/DbConnectionStringCommon.cs | 4 ++-- .../Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionStringCommon.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionStringCommon.cs index 2bd0ee1489..2c50159a00 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionStringCommon.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionStringCommon.cs @@ -854,8 +854,8 @@ internal static bool TryConvertToAttestationProtocol(string value, out SqlConnec internal static bool IsValidAttestationProtocol(SqlConnectionAttestationProtocol value) { Debug.Assert(Enum.GetNames(typeof(SqlConnectionAttestationProtocol)).Length == 3, "SqlConnectionAttestationProtocol enum has changed, update needed"); - return value == SqlConnectionAttestationProtocol.NotSpecified - || value == SqlConnectionAttestationProtocol.HGS + return value == SqlConnectionAttestationProtocol.NotSpecified + || value == SqlConnectionAttestationProtocol.HGS || value == SqlConnectionAttestationProtocol.AAS; } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs index 53753beacb..ea55b0cbd6 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs @@ -127,7 +127,6 @@ public override void CreateEnclaveSession(byte[] attestationInfo, ECDiffieHellma } } - // When overridden in a derived class, looks up and evicts an enclave session from the enclave session cache, if the provider implements session caching. public override void InvalidateEnclaveSession(string serverName, string enclaveAttestationUrl, SqlEnclaveSession enclaveSessionToInvalidate) { From ba619ca06c88ed8c3e378d76c22c775bd2d635da Mon Sep 17 00:00:00 2001 From: Karina Zhou Date: Thu, 31 Oct 2019 15:43:11 -0700 Subject: [PATCH 51/63] Resolve comments part2 and update conversion tests --- .../netcore/ref/Microsoft.Data.SqlClient.NetCoreApp.cs | 5 +++++ .../netfx/ref/Microsoft.Data.SqlClient.cs | 7 +++++++ .../tests/ManualTests/AlwaysEncrypted/ApiShould.cs | 8 ++++---- .../tests/ManualTests/AlwaysEncrypted/ConversionTests.cs | 2 +- .../AlwaysEncrypted/TestFixtures/DatabaseHelper.cs | 2 +- 5 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.NetCoreApp.cs index 09119e9c50..c2c3b58580 100644 --- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.NetCoreApp.cs @@ -53,6 +53,11 @@ public sealed partial class SqlConnectionStringBuilder : System.Data.Common.DbCo public Microsoft.Data.SqlClient.SqlConnectionColumnEncryptionSetting ColumnEncryptionSetting { get { throw null; } set { } } /// public string EnclaveAttestationUrl { get { throw null; } set { } } + + /// + /// To Do : add docs file + /// + public Microsoft.Data.SqlClient.SqlConnectionAttestationProtocol AttestationProtocol {get { throw null; } set { } } } /// public sealed partial class SqlParameter : System.Data.Common.DbParameter, System.ICloneable, System.Data.IDataParameter, System.Data.IDbDataParameter 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 897cc77093..c5f4cc6919 100644 --- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs @@ -867,6 +867,13 @@ public SqlConnectionStringBuilder(string connectionString) { } [System.ComponentModel.DisplayNameAttribute("Enclave Attestation Url")] [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] public string EnclaveAttestationUrl { get { throw null; } set { } } + /// + /// To Do : add docs file + /// + [System.ComponentModel.DisplayNameAttribute("Attestation Protocol")] + [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] + public Microsoft.Data.SqlClient.SqlConnectionAttestationProtocol AttestationProtocol {get { throw null; } set { } } + /// [System.ComponentModel.DisplayNameAttribute("Encrypt")] [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs index 5e989eeb90..9a143375f8 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs @@ -33,7 +33,7 @@ public ApiShould(SQLSetupStrategyCertStoreProvider fixture) [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] [ClassData(typeof(AEConnectionStringProviderWithBooleanVariable))] - public void TestSqlTransactionCommitRollbackWithTransparentInsert(string connection, bool isPermitted) + public void TestSqlTransactionCommitRollbackWithTransparentInsert(string connection, bool isCommitted) { CleanUpTable(connection, tableName); @@ -48,7 +48,7 @@ public void TestSqlTransactionCommitRollbackWithTransparentInsert(string connect { InsertCustomerRecord(sqlConnection, sqlTransaction, customer); - if (isPermitted) + if (isCommitted) { sqlTransaction.Commit(); } @@ -59,7 +59,7 @@ public void TestSqlTransactionCommitRollbackWithTransparentInsert(string connect } // Data should be available on select if committed else, data should not be available. - if (isPermitted) + if (isCommitted) { VerifyRecordPresent(sqlConnection, customer); } @@ -598,7 +598,7 @@ public void TestExecuteScalar(string connection, bool isAsync) } else { - customerId = (int)sqlCommand?.ExecuteScalar(); + customerId = (int)sqlCommand.ExecuteScalar(); } Assert.Equal((int)values[0], customerId); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ConversionTests.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ConversionTests.cs index 806a849b5e..eb55710bcd 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ConversionTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ConversionTests.cs @@ -28,7 +28,7 @@ public class ConversionTests : IDisposable private const decimal SmallMoneyMaxValue = 214748.3647M; private const decimal SmallMoneyMinValue = -214748.3648M; private const int MaxLength = 10000; - private const int NumberOfRows = 100; + private int NumberOfRows = DataTestUtility.EnclaveEnabled ? 10 : 100; private readonly X509Certificate2 certificate; private ColumnMasterKey columnMasterKey; private ColumnEncryptionKey columnEncryptionKey; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/DatabaseHelper.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/DatabaseHelper.cs index 2aaa911a61..2f3e08db78 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/DatabaseHelper.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/DatabaseHelper.cs @@ -59,7 +59,7 @@ public IEnumerator GetEnumerator() yield return new object[] {connStrAE, false}; } } - + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } From fa29fbdfdef5061be10eabd149a7c147dcc59276 Mon Sep 17 00:00:00 2001 From: Javad Rahnama Date: Thu, 31 Oct 2019 16:28:51 -0700 Subject: [PATCH 52/63] Fixing Functional Tests. --- .../ConnectionStringBuilderShould.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs index 9be71de582..ffd2ce397a 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs @@ -46,6 +46,8 @@ public void TestSqlConnectionStringBuilderEnclaveAttestationUrl() VerifyEnclaveAttestationUrlSetting(connectionStringBuilder2, "www.foo.com"); connectionStringBuilder2.Clear(); + + Assert.Equal(SqlConnectionAttestationProtocol.NotSpecified, connectionStringBuilder2.AttestationProtocol); Assert.Equal(string.Empty, connectionStringBuilder2.EnclaveAttestationUrl); Assert.True(string.IsNullOrEmpty(connectionStringBuilder2.DataSource)); @@ -84,7 +86,7 @@ public void TestSqlConnectionStringAttestationProtocol() connectionStringBuilder3.Clear(); - //Assert.Equal(SqlConnectionAttestationProtocol.NotSpecified, connectionStringBuilder3.AttestationProtocol); + Assert.Equal(SqlConnectionAttestationProtocol.NotSpecified, connectionStringBuilder3.AttestationProtocol); Assert.True(string.IsNullOrEmpty(connectionStringBuilder3.DataSource)); } @@ -148,16 +150,15 @@ public void TestSqlConnectionStringBuilderColumnEncryptionSetting(SqlConnectionC [Theory] [InlineData(SqlConnectionAttestationProtocol.AAS)] [InlineData(SqlConnectionAttestationProtocol.HGS)] + [InlineData(SqlConnectionAttestationProtocol.NotSpecified)] public void TestSqlConnectionStringBuilderAttestationProtocol(SqlConnectionAttestationProtocol protocol) { SqlConnectionStringBuilder connectionStringBuilder = new SqlConnectionStringBuilder(); + connectionStringBuilder.DataSource = @"localhost"; // Modify value. - if (protocol != SqlConnectionAttestationProtocol.NotSpecified) - { connectionStringBuilder.AttestationProtocol = protocol; - } - + //Create a connection object with the above builder and verify the expected value. VerifyAttestationProtocol(connectionStringBuilder, protocol); } From c68c12e693b8fd25b43a452bb5353eb50a330618 Mon Sep 17 00:00:00 2001 From: Javad Rahnama Date: Thu, 31 Oct 2019 16:39:55 -0700 Subject: [PATCH 53/63] Fix comments --- .../AlwaysEncryptedTests/ConnectionStringBuilderShould.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs index ffd2ce397a..2a3044fccd 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs @@ -267,7 +267,7 @@ public void TestSqlConnectionStringBuilderTryGetValue(SqlConnectionColumnEncrypt Assert.True(tryGetValueResult); Assert.Equal(sqlConnectionColumnEncryptionSetting, (SqlConnectionColumnEncryptionSetting)outputValue); - // connectionStringBuilder should not have the key ColumnEncryptionSetting. The key is with spaces. + // connectionStringBuilder should not have the key Enclave Attestation URL. The key is with spaces. tryGetValueResult = connectionStringBuilder.TryGetValue(@"EnclaveAttestationUrl", out outputValue); Assert.False(tryGetValueResult); Assert.Null(outputValue); @@ -283,7 +283,7 @@ public void TestSqlConnectionStringBuilderTryGetValue(SqlConnectionColumnEncrypt Assert.True(tryGetValueResult); Assert.Equal("www.foo.com", (string)outputValue); - // connectionStringBuilder should not have the key ColumnEncryptionSetting. The key is with spaces. + // connectionStringBuilder should not have the key Enclave Attestation Protocol. The key is with spaces. tryGetValueResult = connectionStringBuilder.TryGetValue(@"AttestationProtocol", out outputValue); Assert.False(tryGetValueResult); Assert.Null(outputValue); From 25c88657ee3b1b109545868cd7054af8515d35d1 Mon Sep 17 00:00:00 2001 From: Javad Rahnama Date: Thu, 31 Oct 2019 23:37:47 -0700 Subject: [PATCH 54/63] Fixing/Editing more Functional Tests comments --- .../ConnectionStringBuilderShould.cs | 37 +++++++++++++------ 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs index 2a3044fccd..5e2edd3f4c 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs @@ -228,10 +228,10 @@ public void TestSqlConnectionStringBuilderContainsKey(SqlConnectionColumnEncrypt //also check attestatin url - // Key is "Column Encryption Setting" with spaces. So lookup for ColumnEncryptionSetting should return false. + // Key is "Column Encryption Setting" with spaces. So lookup for Enclave Attestation URL should return false. Assert.False(connectionStringBuilder.ContainsKey(@"EnclaveAttestationUrl")); - // connectionStringBuilder should have the key Column Encryption Setting, even if value is not set. + // connectionStringBuilder should have the key Enclave Attestation URL, even if value is not set. Assert.True(connectionStringBuilder.ContainsKey(@"Enclave Attestation Url")); // set a value and check for the key again, it should exist. @@ -293,11 +293,26 @@ public void TestSqlConnectionStringBuilderTryGetValue(SqlConnectionColumnEncrypt Assert.True(tryGetValueResult); Assert.Equal(SqlConnectionAttestationProtocol.NotSpecified, outputValue); - // set the value for the protocol without setting it. + // Get the value for the protocol without setting it. It should return not specified. + tryGetValueResult = connectionStringBuilder.TryGetValue(@"Attestation Protocol", out outputValue); + Assert.True(tryGetValueResult); + Assert.Equal(SqlConnectionAttestationProtocol.NotSpecified, outputValue); + + //Set the value for protocol to HGS. connectionStringBuilder.AttestationProtocol = SqlConnectionAttestationProtocol.HGS; + + //Get value for Attestation Protocol. tryGetValueResult = connectionStringBuilder.TryGetValue(@"Attestation Protocol", out outputValue); Assert.True(tryGetValueResult); Assert.Equal(SqlConnectionAttestationProtocol.HGS, outputValue); + + //Set the value for protocol to AAS. + connectionStringBuilder.AttestationProtocol = SqlConnectionAttestationProtocol.AAS; + + //Get value for Attestation Protocol. + tryGetValueResult = connectionStringBuilder.TryGetValue(@"Attestation Protocol", out outputValue); + Assert.True(tryGetValueResult); + Assert.Equal(SqlConnectionAttestationProtocol.AAS, outputValue); } [Theory] @@ -316,13 +331,13 @@ public void TestSqlConnectionStringBuilderAdd(SqlConnectionColumnEncryptionSetti string url = "www.foo.com"; SqlConnectionAttestationProtocol protocol = SqlConnectionAttestationProtocol.HGS; - // Use the Add function to update the Column Encryption Setting in the dictionary. + // Use the Add function to update the Enclae Attestation URL in the dictionary. connectionStringBuilder.Add(@"Enclave Attestation Url", url); // Query the property to check if the above add was effective. Assert.Equal(url, connectionStringBuilder.EnclaveAttestationUrl); - // Use the Add function to update the Column Encryption Setting in the dictionary. + // Use the Add function to update the Enclave Attestatopm Protocol in the dictionary. connectionStringBuilder.Add(@"Attestation Protocol", protocol); // Query the property to check if the above add was effective. @@ -349,14 +364,14 @@ public void TestSqlConnectionStringBuilderRemove(SqlConnectionColumnEncryptionSe connectionStringBuilder.TryGetValue(@"Column Encryption Setting", out outputValue); Assert.Equal(SqlConnectionColumnEncryptionSetting.Disabled, outputValue); - // Use the Add function to update the Column Encryption Setting in the dictionary. + // Use the Add function to update the Enclave Attestation URL in the dictionary. string url = "www.foo.com"; connectionStringBuilder.Add(@"Enclave Attestation Url", url); // Query the property to check if the above add was effective. Assert.Equal(url, connectionStringBuilder.EnclaveAttestationUrl); - // Use the Remove function to remove the Column Encryption Setting from the dictionary. + // Use the Remove function to remove the Enclave Attestation URL from the dictionary. connectionStringBuilder.Remove(@"Enclave Attestation Url"); // Query the property to check if the above remove was effective. @@ -395,27 +410,27 @@ public void TestSqlConnectionStringBuilderShouldSerialize(SqlConnectionColumnEnc // Query the property to check if the above add was effective. Assert.False(connectionStringBuilder.ShouldSerialize(@"Column Encryption Setting")); - // Use the Add function to update the Column Encryption Setting in the dictionary. + // Use the Add function to update the Enclave Attestation URL in the dictionary. string url = "www.foo.com"; connectionStringBuilder.Add(@"Enclave Attestation Url", url); // Query the ShouldSerialize method to check if the above add was effective. Assert.True(connectionStringBuilder.ShouldSerialize(@"Enclave Attestation Url")); - // Use the Remove function to Remove the Column Encryption Setting from the dictionary. + // Use the Remove function to Remove the Enclave Attestation URL from the dictionary. connectionStringBuilder.Remove(@"Enclave Attestation Url"); // Query the property to check if the above add was effective. Assert.False(connectionStringBuilder.ShouldSerialize(@"Enclave Attestation Url")); string protocol = "HGS"; - //Use the Add function to update the column Encryption Setting in the dictionary. + //Use the Add function to update the Enclave Attestation Protocol in the dictionary. connectionStringBuilder.Add(@"Attestation Protocol", protocol); // Query the ShouldSerialize method to check if the above add was effective. Assert.True(connectionStringBuilder.ShouldSerialize(@"Attestation Protocol")); - // Use the Remove function to Remove the Column Encryption Setting from the dictionary. + // Use the Remove function to Remove the Enclave Attestation Protocol from the dictionary. connectionStringBuilder.Remove(@"Attestation Protocol"); // Query the property to check if the above add was effective. From 8078b39ad94088598989add917b24d595308e75f Mon Sep 17 00:00:00 2001 From: dengel Date: Fri, 1 Nov 2019 09:18:49 -0700 Subject: [PATCH 55/63] RetriableEnclaveQueryExecutionException -> RetryableEnclaveQueryExecutionException --- .../Microsoft/Data/SqlClient/EnclaveDelegate.NetCoreApp.cs | 2 +- .../netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs | 4 ++-- .../netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs | 6 +++--- .../netfx/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs | 6 +++--- .../netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs | 6 +++--- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetCoreApp.cs index 66c079ec22..e7d69e0dc6 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetCoreApp.cs @@ -109,7 +109,7 @@ internal EnclavePackage GenerateEnclavePackage(SqlConnectionAttestationProtocol } catch (Exception e) { - throw new RetriableEnclaveQueryExecutionException(e.Message, e); + throw new RetryableEnclaveQueryExecutionException(e.Message, e); } List decryptedKeysToBeSentToEnclave = GetDecryptedKeysToBeSentToEnclave(keysTobeSentToEnclave, serverName); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs index 3d3ef1cab0..27c8bb18a9 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs @@ -197,9 +197,9 @@ private byte[] ComputeQueryStringHash(string queryString) /// /// Exception when executing a enclave based Always Encrypted query /// - internal class RetriableEnclaveQueryExecutionException : Exception + internal class RetryableEnclaveQueryExecutionException : Exception { - internal RetriableEnclaveQueryExecutionException(string message, Exception innerException) : base(message, innerException) { } + internal RetryableEnclaveQueryExecutionException(string message, Exception innerException) : base(message, innerException) { } } } } 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 4bb1f6c698..29ce7ec2e5 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 @@ -2003,7 +2003,7 @@ private void CreateLocalCompletionTask(CommandBehavior behavior, object stateObj ReliablePutStateObject(); } - bool shouldRetry = e is EnclaveDelegate.RetriableEnclaveQueryExecutionException; + bool shouldRetry = e is EnclaveDelegate.RetryableEnclaveQueryExecutionException; // Check if we have an error indicating that we can retry. if (e is SqlException) @@ -3964,7 +3964,7 @@ internal SqlDataReader RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior describeParameterEncryptionRequest: false, describeParameterEncryptionTask: returnTask); } - catch (EnclaveDelegate.RetriableEnclaveQueryExecutionException) + catch (EnclaveDelegate.RetryableEnclaveQueryExecutionException) { if (inRetry || isAsync) { @@ -4130,7 +4130,7 @@ private void GenerateEnclavePackage() this.CommandText, enclaveType, this._activeConnection.DataSource, this._activeConnection.EnclaveAttestationUrl); } - catch (EnclaveDelegate.RetriableEnclaveQueryExecutionException) + catch (EnclaveDelegate.RetryableEnclaveQueryExecutionException) { throw; } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs index fdec403870..e041c0f8a6 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs @@ -56,7 +56,7 @@ internal EnclavePackage GenerateEnclavePackage(SqlConnectionAttestationProtocol } catch (Exception e) { - throw new RetriableEnclaveQueryExecutionException(e.Message, e); + throw new RetryableEnclaveQueryExecutionException(e.Message, e); } List decryptedKeysToBeSentToEnclave = GetDecryptedKeysToBeSentToEnclave(keysTobeSentToEnclave, serverName); @@ -387,9 +387,9 @@ private byte[] ComputeQueryStringHash(string queryString) /// /// Exception when executing a enclave based Always Encrypted query /// - internal class RetriableEnclaveQueryExecutionException : Exception + internal class RetryableEnclaveQueryExecutionException : Exception { - internal RetriableEnclaveQueryExecutionException(string message, Exception innerException) : base(message, innerException) { } + internal RetryableEnclaveQueryExecutionException(string message, Exception innerException) : base(message, innerException) { } } /// 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 31b13cee19..61dc9301ff 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 @@ -2697,7 +2697,7 @@ private bool TriggerInternalEndAndRetryIfNecessary(CommandBehavior behavior, obj ReliablePutStateObject(); } - bool shouldRetry = e is EnclaveDelegate.RetriableEnclaveQueryExecutionException; + bool shouldRetry = e is EnclaveDelegate.RetryableEnclaveQueryExecutionException; // Check if we have an error indicating that we can retry. if (e is SqlException) @@ -4932,7 +4932,7 @@ internal SqlDataReader RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior describeParameterEncryptionRequest: false, describeParameterEncryptionTask: returnTask); } - catch (EnclaveDelegate.RetriableEnclaveQueryExecutionException) + catch (EnclaveDelegate.RetryableEnclaveQueryExecutionException) { if (inRetry || async) { @@ -5136,7 +5136,7 @@ private void GenerateEnclavePackage() this.CommandText, enclaveType, this._activeConnection.DataSource, this._activeConnection.EnclaveAttestationUrl); } - catch (EnclaveDelegate.RetriableEnclaveQueryExecutionException) + catch (EnclaveDelegate.RetryableEnclaveQueryExecutionException) { throw; } From 70e9d54be01c94d8d748105f03aa7e4d64405948 Mon Sep 17 00:00:00 2001 From: Karina Zhou Date: Sun, 3 Nov 2019 18:28:48 -0800 Subject: [PATCH 56/63] Fix test hunging for ExceptionGenericError --- .../AlwaysEncrypted/ExceptionsGenericError.cs | 87 ++++++++++--------- .../TestFixtures/Setup/CertificateUtility.cs | 16 ++-- .../ManualTests/DataCommon/DataTestUtility.cs | 5 +- 3 files changed, 63 insertions(+), 45 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ExceptionsGenericError.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ExceptionsGenericError.cs index 58da7ba3f4..288c20f139 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ExceptionsGenericError.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ExceptionsGenericError.cs @@ -6,10 +6,10 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted { public class ExceptionsGenericErrors : IClassFixture { - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] - [ActiveIssue(10036)] - public void TestCommandOptionWithNoTceFeature () { - SqlConnectionStringBuilder sb = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString); + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE), nameof(DataTestUtility.IsNotAzureServer), Skip = "ActiveIssue 10036")] + [ClassData(typeof(AEConnectionStringProvider))] + public void TestCommandOptionWithNoTceFeature (string connectionString) { + SqlConnectionStringBuilder sb = new SqlConnectionStringBuilder(connectionString); CertificateUtility.ChangeServerTceSetting (false, sb); // disable TCE on engine. using (SqlConnection conn = CertificateUtility.GetOpenConnection(false, sb, fSuppressAttestation: true)) { @@ -26,9 +26,10 @@ public void TestCommandOptionWithNoTceFeature () { CertificateUtility.ChangeServerTceSetting (true, sb); // enable tce } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] - public void TestDataAdapterAndEncrytionSetting () { - SqlConnectionStringBuilder sb = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString); + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE), nameof(DataTestUtility.IsNotAzureServer))] + [ClassData(typeof(AEConnectionStringProvider))] + public void TestDataAdapterAndEncrytionSetting (string connectionString) { + SqlConnectionStringBuilder sb = new SqlConnectionStringBuilder(connectionString); // Create a new SqlCommand for select and delete using (SqlConnection conn = CertificateUtility.GetOpenConnection(false, sb)) { @@ -64,9 +65,10 @@ public void TestDataAdapterAndEncrytionSetting () { } } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] - public void TestInvalidForceColumnEncryptionSetting() { - SqlConnectionStringBuilder sb = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString); + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE), nameof(DataTestUtility.IsNotAzureServer))] + [ClassData(typeof(AEConnectionStringProvider))] + public void TestInvalidForceColumnEncryptionSetting(string connectionString) { + SqlConnectionStringBuilder sb = new SqlConnectionStringBuilder(connectionString); using (SqlConnection conn = CertificateUtility.GetOpenConnection(false, sb)) { using (SqlCommand cmd = new SqlCommand(ExceptionGenericErrorFixture.encryptedProcedureName, conn)) @@ -81,9 +83,10 @@ public void TestInvalidForceColumnEncryptionSetting() { } } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] - public void TestParamUnexpectedEncryptionMD() { - SqlConnectionStringBuilder sb = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString); + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE), nameof(DataTestUtility.IsNotAzureServer))] + [ClassData(typeof(AEConnectionStringProvider))] + public void TestParamUnexpectedEncryptionMD(string connectionString) { + SqlConnectionStringBuilder sb = new SqlConnectionStringBuilder(connectionString); using (SqlConnection conn = CertificateUtility.GetOpenConnection(true, sb)) { using (SqlCommand cmd = new SqlCommand(ExceptionGenericErrorFixture.encryptedProcedureName, conn)) @@ -114,22 +117,25 @@ private void CreateAndPopulateSimpleTable() { encryptedTableName = DatabaseHelper.GenerateUniqueName("encrypted"); encryptedProcedureName = DatabaseHelper.GenerateUniqueName("encrypted"); - using (SqlConnection conn = CertificateUtility.GetOpenConnection(false, new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString))) + foreach (string connectionStr in DataTestUtility.AEConnStringsSetup) { - using (SqlCommand cmdCreate = new SqlCommand($"create table {encryptedTableName}(c1 int)", conn)) + using (SqlConnection conn = CertificateUtility.GetOpenConnection(false, new SqlConnectionStringBuilder(connectionStr))) { - cmdCreate.CommandType = CommandType.Text; - cmdCreate.ExecuteNonQuery(); - } - using (SqlCommand cmdInsert = new SqlCommand($"insert into {encryptedTableName} values(1)", conn)) - { - cmdInsert.CommandType = CommandType.Text; - cmdInsert.ExecuteNonQuery(); - } - using (SqlCommand cmdCreateProc = new SqlCommand($"create procedure {encryptedProcedureName}(@c1 int) as insert into {encryptedTableName} values (@c1)", conn)) - { - cmdCreateProc.CommandType = CommandType.Text; - cmdCreateProc.ExecuteNonQuery(); + using (SqlCommand cmdCreate = new SqlCommand($"create table {encryptedTableName}(c1 int)", conn)) + { + cmdCreate.CommandType = CommandType.Text; + cmdCreate.ExecuteNonQuery(); + } + using (SqlCommand cmdInsert = new SqlCommand($"insert into {encryptedTableName} values(1)", conn)) + { + cmdInsert.CommandType = CommandType.Text; + cmdInsert.ExecuteNonQuery(); + } + using (SqlCommand cmdCreateProc = new SqlCommand($"create procedure {encryptedProcedureName}(@c1 int) as insert into {encryptedTableName} values (@c1)", conn)) + { + cmdCreateProc.CommandType = CommandType.Text; + cmdCreateProc.ExecuteNonQuery(); + } } } } @@ -137,23 +143,26 @@ private void CreateAndPopulateSimpleTable() public void Dispose() { // Do NOT remove certificate for concurrent consistency. Certificates are used for other test cases as well. - SqlConnectionStringBuilder sb = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString); - using (SqlConnection conn = CertificateUtility.GetOpenConnection(false, sb)) + foreach (string connectionStr in DataTestUtility.AEConnStringsSetup) { - using (SqlCommand cmd = new SqlCommand($"drop table {encryptedTableName}", conn)) + SqlConnectionStringBuilder sb = new SqlConnectionStringBuilder(connectionStr); + using (SqlConnection conn = CertificateUtility.GetOpenConnection(false, sb)) { - cmd.CommandType = CommandType.Text; - cmd.ExecuteNonQuery(); + using (SqlCommand cmd = new SqlCommand($"drop table {encryptedTableName}", conn)) + { + cmd.CommandType = CommandType.Text; + cmd.ExecuteNonQuery(); - cmd.CommandText = $"drop procedure {encryptedProcedureName}"; - cmd.ExecuteNonQuery(); + cmd.CommandText = $"drop procedure {encryptedProcedureName}"; + cmd.ExecuteNonQuery(); + } } - } - // Only use traceoff for non-sysadmin role accounts, Azure accounts does not have the permission. - if (DataTestUtility.IsNotAzureServer()) - { - CertificateUtility.ChangeServerTceSetting(true, sb); + // Only use traceoff for non-sysadmin role accounts, Azure accounts does not have the permission. + if (DataTestUtility.IsNotAzureServer()) + { + CertificateUtility.ChangeServerTceSetting(true, sb); + } } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CertificateUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CertificateUtility.cs index 3356382dfe..68d0c25f68 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CertificateUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CertificateUtility.cs @@ -281,16 +281,22 @@ internal static SqlConnection GetOpenConnection(bool fTceEnabled, SqlConnectionS /// public static string GetConnectionString(bool fTceEnabled, SqlConnectionStringBuilder sb, bool fSuppressAttestation = false) { + SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(); + builder.DataSource = sb.DataSource; + builder.InitialCatalog = sb.InitialCatalog; + builder.UserID = sb.UserID; + builder.Password = sb.Password; if (fTceEnabled) { - sb.ColumnEncryptionSetting = SqlConnectionColumnEncryptionSetting.Enabled; + builder.ColumnEncryptionSetting = SqlConnectionColumnEncryptionSetting.Enabled; } - if (!fSuppressAttestation) + if (!fSuppressAttestation && DataTestUtility.EnclaveEnabled) { - sb.EnclaveAttestationUrl = sb.EnclaveAttestationUrl; + builder.EnclaveAttestationUrl = sb.EnclaveAttestationUrl; + builder.AttestationProtocol = sb.AttestationProtocol; } - sb.ConnectTimeout = 10000; - return sb.ToString(); + builder.ConnectTimeout = 10000; + return builder.ToString(); } /// diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs index bde3d10e56..6504e49fd7 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs @@ -167,7 +167,10 @@ public static bool IsAADPasswordConnStrSetup() return !string.IsNullOrEmpty(AADPasswordConnectionString); } - public static bool IsNotAzureServer() => !DataTestUtility.IsAzureSqlServer(new SqlConnectionStringBuilder((DataTestUtility.TCPConnectionString)).DataSource); + public static bool IsNotAzureServer() + { + return AreConnStringsSetup() ? !DataTestUtility.IsAzureSqlServer(new SqlConnectionStringBuilder((DataTestUtility.TCPConnectionString)).DataSource) : true; + } public static bool IsAKVSetupAvailable() { From d3fcd8172f641fc5ecb4428ec49f1982abb017c9 Mon Sep 17 00:00:00 2001 From: Javad Rahnama Date: Sun, 3 Nov 2019 22:45:14 -0800 Subject: [PATCH 57/63] Fixing MasterKeyPath Test with AEV2 --- .../SqlConnectionStringBuilder.xml | 4 ++ .../SqlClient/SqlConnectionStringBuilder.cs | 5 +- .../SqlClient/SqlConnectionStringBuilder.cs | 4 +- .../TestTrustedMasterKeyPaths.cs | 55 +++++++++++-------- 4 files changed, 37 insertions(+), 31 deletions(-) diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionStringBuilder.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionStringBuilder.xml index b574526670..d696b24933 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionStringBuilder.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionStringBuilder.xml @@ -321,6 +321,10 @@ False The enclave attestation Url. To be added. + + Set/Get the value of Attestation Protocol. + Returns Attestation Protocol. + Gets or sets a Boolean value that indicates whether SQL Server uses SSL encryption for all data sent between the client and server if the server has a certificate installed. The value of the property, or if none has been supplied. diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs index 6a7d4cf7b4..f2ee36f40e 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs @@ -482,10 +482,7 @@ public string EnclaveAttestationUrl } } - /// - /// - /// - /// + /// public SqlConnectionAttestationProtocol AttestationProtocol { get { return _attestationProtocol; } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs index 6b6c2cadd2..b5d458d7a6 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs @@ -645,9 +645,7 @@ public string EnclaveAttestationUrl } } - /// - /// To add include file for docs - /// + /// [DisplayName(DbConnectionStringKeywords.AttestationProtocol)] [ResCategoryAttribute(StringsHelper.ResourceNames.DataCategory_Security)] [ResDescriptionAttribute(StringsHelper.ResourceNames.TCE_DbConnectionString_AttestationProtocol)] diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestTrustedMasterKeyPaths.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestTrustedMasterKeyPaths.cs index c433c9e1b2..68f3b8beee 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestTrustedMasterKeyPaths.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestTrustedMasterKeyPaths.cs @@ -17,14 +17,17 @@ public class TestTrustedMasterKeyPaths : IClassFixture @@ -54,10 +57,11 @@ private void ValidateResultSet(SqlDataReader sqlDataReader) } [PlatformSpecific(TestPlatforms.Windows)] - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - public void TestTrustedColumnEncryptionMasterKeyPathsWithNullDictionary() + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ClassData(typeof(AEConnectionStringProvider))] + public void TestTrustedColumnEncryptionMasterKeyPathsWithNullDictionary(string connection) { - SqlConnectionStringBuilder connBuilder = new SqlConnectionStringBuilder(defaultConnectionString); + SqlConnectionStringBuilder connBuilder = new SqlConnectionStringBuilder(connection); // 1. Default should succeed. if (SqlConnection.ColumnEncryptionTrustedMasterKeyPaths.Count != 0) @@ -65,7 +69,7 @@ public void TestTrustedColumnEncryptionMasterKeyPathsWithNullDictionary() SqlConnection.ColumnEncryptionTrustedMasterKeyPaths.Clear(); } - using (SqlConnection sqlConnection = new SqlConnection(string.Concat(defaultConnectionString, @";Column Encryption Setting = Enabled;"))) + using (SqlConnection sqlConnection = new SqlConnection(string.Concat(connection, @";Column Encryption Setting = Enabled;"))) { sqlConnection.Open(); @@ -90,10 +94,11 @@ FROM [{tableName}] } [PlatformSpecific(TestPlatforms.Windows)] - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - public void TestTrustedColumnEncryptionMasterKeyPathsWithOneServer() + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ClassData(typeof(AEConnectionStringProvider))] + public void TestTrustedColumnEncryptionMasterKeyPathsWithOneServer(string connection) { - SqlConnectionStringBuilder connBuilder = new SqlConnectionStringBuilder(defaultConnectionString); + SqlConnectionStringBuilder connBuilder = new SqlConnectionStringBuilder(connection); // 2.. Test with valid key path // @@ -108,7 +113,7 @@ public void TestTrustedColumnEncryptionMasterKeyPathsWithOneServer() trustedKeyPaths.Add(columnMasterKeyPath); SqlConnection.ColumnEncryptionTrustedMasterKeyPaths.Add(connBuilder.DataSource, trustedKeyPaths); - using (SqlConnection sqlConnection = new SqlConnection(string.Concat(defaultConnectionString, @";Column Encryption Setting = Enabled;"))) + using (SqlConnection sqlConnection = new SqlConnection(string.Concat(connection, @";Column Encryption Setting = Enabled;"))) { sqlConnection.Open(); @@ -133,10 +138,11 @@ FROM [{tableName}] } [PlatformSpecific(TestPlatforms.Windows)] - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - public void TestTrustedColumnEncryptionMasterKeyPathsWithMultipleServers() + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ClassData(typeof(AEConnectionStringProvider))] + public void TestTrustedColumnEncryptionMasterKeyPathsWithMultipleServers(string connection) { - SqlConnectionStringBuilder connBuilder = new SqlConnectionStringBuilder(defaultConnectionString); + SqlConnectionStringBuilder connBuilder = new SqlConnectionStringBuilder(connection); // 3. Test with multiple servers with multiple key paths // @@ -168,7 +174,7 @@ public void TestTrustedColumnEncryptionMasterKeyPathsWithMultipleServers() server2TrustedKeyPaths.Add(@"https://balneetestkeyvault.vault.azure.net/keys/CryptoTest4/f4eb1dbbe6a9446599efe3c952614e70"); SqlConnection.ColumnEncryptionTrustedMasterKeyPaths.Add(@"randomeserver", server2TrustedKeyPaths); - using (SqlConnection sqlConnection = new SqlConnection(string.Concat(defaultConnectionString, @";Column Encryption Setting = Enabled;"))) + using (SqlConnection sqlConnection = new SqlConnection(string.Concat(connection, @";Column Encryption Setting = Enabled;"))) { sqlConnection.Open(); @@ -193,10 +199,11 @@ FROM [{tableName}] } [PlatformSpecific(TestPlatforms.Windows)] - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - public void TestTrustedColumnEncryptionMasterKeyPathsWithInvalidInputs() + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] + [ClassData(typeof(AEConnectionStringProvider))] + public void TestTrustedColumnEncryptionMasterKeyPathsWithInvalidInputs(string connection) { - SqlConnectionStringBuilder connBuilder = new SqlConnectionStringBuilder(defaultConnectionString); + SqlConnectionStringBuilder connBuilder = new SqlConnectionStringBuilder(connection); // 1. Test with null List // @@ -212,7 +219,7 @@ public void TestTrustedColumnEncryptionMasterKeyPathsWithInvalidInputs() // Prepare a dictionary with null list. SqlConnection.ColumnEncryptionTrustedMasterKeyPaths.Add(connBuilder.DataSource, (List)null); - using (SqlConnection sqlConnection = new SqlConnection(string.Concat(defaultConnectionString, @";Column Encryption Setting = Enabled;"))) + using (SqlConnection sqlConnection = new SqlConnection(string.Concat(connection, @";Column Encryption Setting = Enabled;"))) { sqlConnection.Open(); @@ -244,7 +251,7 @@ FROM [{tableName}] List emptyKeyPathList = new List(); SqlConnection.ColumnEncryptionTrustedMasterKeyPaths.Add(connBuilder.DataSource, emptyKeyPathList); - using (SqlConnection sqlConnection = new SqlConnection(string.Concat(defaultConnectionString, @";Column Encryption Setting = Enabled;"))) + using (SqlConnection sqlConnection = new SqlConnection(string.Concat(connection, @";Column Encryption Setting = Enabled;"))) { sqlConnection.Open(); @@ -279,7 +286,7 @@ FROM [{tableName}] invalidKeyPathList.Add(invalidKeyPath); SqlConnection.ColumnEncryptionTrustedMasterKeyPaths.Add(connBuilder.DataSource, invalidKeyPathList); - using (SqlConnection sqlConnection = new SqlConnection(string.Concat(defaultConnectionString, @";Column Encryption Setting = Enabled;"))) + using (SqlConnection sqlConnection = new SqlConnection(string.Concat(connection, @";Column Encryption Setting = Enabled;"))) { sqlConnection.Open(); From 9689f7656bbc4840f2640321bda2f873db121875 Mon Sep 17 00:00:00 2001 From: Javad Rahnama Date: Sun, 3 Nov 2019 22:58:41 -0800 Subject: [PATCH 58/63] Add Documentation Include tag --- .../netcore/ref/Microsoft.Data.SqlClient.NetCoreApp.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.NetCoreApp.cs index c2c3b58580..be2233a4ac 100644 --- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.NetCoreApp.cs @@ -54,9 +54,7 @@ public sealed partial class SqlConnectionStringBuilder : System.Data.Common.DbCo /// public string EnclaveAttestationUrl { get { throw null; } set { } } - /// - /// To Do : add docs file - /// + /// public Microsoft.Data.SqlClient.SqlConnectionAttestationProtocol AttestationProtocol {get { throw null; } set { } } } /// From 6f9803509cffe01b9808e3e24c31d3227f66d8b3 Mon Sep 17 00:00:00 2001 From: v-kaywon Date: Mon, 4 Nov 2019 10:44:03 -0800 Subject: [PATCH 59/63] minor changes --- .../Microsoft.Data.SqlClient/EnclavePublicKey.xml | 14 -------------- .../Data/Common/DbConnectionStringCommon.cs | 5 +++-- .../Data/Common/DbConnectionStringCommon.cs | 14 +++----------- .../ConnectionStringBuilderShould.cs | 8 ++++---- .../tests/ManualTests/config.json | 6 +++--- 5 files changed, 13 insertions(+), 34 deletions(-) delete mode 100644 doc/snippets/Microsoft.Data.SqlClient/EnclavePublicKey.xml diff --git a/doc/snippets/Microsoft.Data.SqlClient/EnclavePublicKey.xml b/doc/snippets/Microsoft.Data.SqlClient/EnclavePublicKey.xml deleted file mode 100644 index 41b0d371b9..0000000000 --- a/doc/snippets/Microsoft.Data.SqlClient/EnclavePublicKey.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - Class to hold the enclave's RSA public key - - - To be added. - - - Instantiates the Class with assigning received payload value to PublicKey - - - - diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs index d7317696ee..c45b12af7f 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs @@ -241,10 +241,11 @@ internal static bool TryConvertToAttestationProtocol(string value, out SqlConnec return true; } else - + { result = DbConnectionStringDefaults.AttestationProtocol; - return false; + return false; } + } internal static bool IsValidAttestationProtocol(SqlConnectionAttestationProtocol value) { diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionStringCommon.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionStringCommon.cs index 2c50159a00..772f422b1a 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionStringCommon.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionStringCommon.cs @@ -831,24 +831,21 @@ internal static SqlConnectionColumnEncryptionSetting ConvertToColumnEncryptionSe /// internal static bool TryConvertToAttestationProtocol(string value, out SqlConnectionAttestationProtocol result) { - bool isSuccess = false; - if (StringComparer.InvariantCultureIgnoreCase.Equals(value, AttestationProtocolHGS)) { result = SqlConnectionAttestationProtocol.HGS; - isSuccess = true; + return true; } else if (StringComparer.InvariantCultureIgnoreCase.Equals(value, AttestationProtocolAAS)) { result = SqlConnectionAttestationProtocol.AAS; - isSuccess = true; + return true; } else { result = DbConnectionStringDefaults.AttestationProtocol; + return false; } - - return isSuccess; } internal static bool IsValidAttestationProtocol(SqlConnectionAttestationProtocol value) @@ -887,11 +884,6 @@ internal static SqlConnectionAttestationProtocol ConvertToAttestationProtocol(st if (null != sValue) { - if (TryConvertToAttestationProtocol(sValue, out result)) - { - return result; - } - // try again after remove leading & trailing whitespaces. sValue = sValue.Trim(); if (TryConvertToAttestationProtocol(sValue, out result)) diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs index 5e2edd3f4c..5c9c124763 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs @@ -267,7 +267,7 @@ public void TestSqlConnectionStringBuilderTryGetValue(SqlConnectionColumnEncrypt Assert.True(tryGetValueResult); Assert.Equal(sqlConnectionColumnEncryptionSetting, (SqlConnectionColumnEncryptionSetting)outputValue); - // connectionStringBuilder should not have the key Enclave Attestation URL. The key is with spaces. + // connectionStringBuilder should not have the key EnclaveAttestationUrl. The key is with spaces. tryGetValueResult = connectionStringBuilder.TryGetValue(@"EnclaveAttestationUrl", out outputValue); Assert.False(tryGetValueResult); Assert.Null(outputValue); @@ -283,7 +283,7 @@ public void TestSqlConnectionStringBuilderTryGetValue(SqlConnectionColumnEncrypt Assert.True(tryGetValueResult); Assert.Equal("www.foo.com", (string)outputValue); - // connectionStringBuilder should not have the key Enclave Attestation Protocol. The key is with spaces. + // connectionStringBuilder should not have the key AttestationProtocol. The key is with spaces. tryGetValueResult = connectionStringBuilder.TryGetValue(@"AttestationProtocol", out outputValue); Assert.False(tryGetValueResult); Assert.Null(outputValue); @@ -331,13 +331,13 @@ public void TestSqlConnectionStringBuilderAdd(SqlConnectionColumnEncryptionSetti string url = "www.foo.com"; SqlConnectionAttestationProtocol protocol = SqlConnectionAttestationProtocol.HGS; - // Use the Add function to update the Enclae Attestation URL in the dictionary. + // Use the Add function to update the Enclave Attestation Url in the dictionary. connectionStringBuilder.Add(@"Enclave Attestation Url", url); // Query the property to check if the above add was effective. Assert.Equal(url, connectionStringBuilder.EnclaveAttestationUrl); - // Use the Add function to update the Enclave Attestatopm Protocol in the dictionary. + // Use the Add function to update the Attestation Protocol in the dictionary. connectionStringBuilder.Add(@"Attestation Protocol", protocol); // Query the property to check if the above add was effective. diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/config.json b/src/Microsoft.Data.SqlClient/tests/ManualTests/config.json index 27218a2f5e..2f46abe9fc 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/config.json +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/config.json @@ -1,10 +1,10 @@ { - "TCPConnectionString": "Data Source=tcp:localhost;Database=Northwind;Integrated Security=true;", + "TCPConnectionString": "", "NPConnectionString": "Data Source=np:localhost;Database=Northwind;Integrated Security=true;", - "TCPConnectionStringHGSVBS": "", + "TCPConnectionStringHGSVBS": "Data Source = WIN-K4TQB0TR8JQ; Database = Northwind; Attestation Protocol = HGS; Enclave Attestation Url= http://WIN-K4TQB0TR8JQ/Attestation;User ID=sa; Password=Moonshine4me; Integrated Security= False;", "TCPConnectionStringAASVBS": "", "TCPConnectionStringAASSGX": "", - "EnclaveEnabled": false, + "EnclaveEnabled": "true", "AADAccessToken": "", "AADPasswordConnectionString": "", "AzureKeyVaultURL": "", From b5304bce5bf8b2a0955c2ad8ee59f13ed67ab41a Mon Sep 17 00:00:00 2001 From: Karina Zhou Date: Mon, 4 Nov 2019 11:03:14 -0800 Subject: [PATCH 60/63] Minor fix for TestTrustedMasterKeyPaths --- .../TestTrustedMasterKeyPaths.cs | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestTrustedMasterKeyPaths.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestTrustedMasterKeyPaths.cs index 68f3b8beee..b20baabe31 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestTrustedMasterKeyPaths.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestTrustedMasterKeyPaths.cs @@ -11,23 +11,14 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted public class TestTrustedMasterKeyPaths : IClassFixture { private SQLSetupStrategyCertStoreProvider fixture; - static private string defaultConnectionString; private readonly string tableName; private readonly string columnMasterKeyPath; public TestTrustedMasterKeyPaths(SQLSetupStrategyCertStoreProvider fixture) { - foreach (var connectionString in DataTestUtility.AEConnStrings) - { - SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString); - builder.ConnectTimeout = 10000; - defaultConnectionString = builder.ToString(); - - columnMasterKeyPath = string.Format(@"{0}/{1}/{2}", StoreLocation.CurrentUser.ToString(), @"my", CertificateUtility.CreateCertificate().Thumbprint); - - this.fixture = fixture; - tableName = fixture.TrustedMasterKeyPathsTestTable.Name; - } + columnMasterKeyPath = string.Format(@"{0}/{1}/{2}", StoreLocation.CurrentUser.ToString(), @"my", CertificateUtility.CreateCertificate().Thumbprint); + this.fixture = fixture; + tableName = fixture.TrustedMasterKeyPathsTestTable.Name; } /// @@ -62,6 +53,8 @@ private void ValidateResultSet(SqlDataReader sqlDataReader) public void TestTrustedColumnEncryptionMasterKeyPathsWithNullDictionary(string connection) { SqlConnectionStringBuilder connBuilder = new SqlConnectionStringBuilder(connection); + connBuilder.ConnectTimeout = 10000; + string connStringNow = connBuilder.ToString(); // 1. Default should succeed. if (SqlConnection.ColumnEncryptionTrustedMasterKeyPaths.Count != 0) @@ -69,7 +62,7 @@ public void TestTrustedColumnEncryptionMasterKeyPathsWithNullDictionary(string c SqlConnection.ColumnEncryptionTrustedMasterKeyPaths.Clear(); } - using (SqlConnection sqlConnection = new SqlConnection(string.Concat(connection, @";Column Encryption Setting = Enabled;"))) + using (SqlConnection sqlConnection = new SqlConnection(string.Concat(connStringNow, @";Column Encryption Setting = Enabled;"))) { sqlConnection.Open(); @@ -99,6 +92,8 @@ FROM [{tableName}] public void TestTrustedColumnEncryptionMasterKeyPathsWithOneServer(string connection) { SqlConnectionStringBuilder connBuilder = new SqlConnectionStringBuilder(connection); + connBuilder.ConnectTimeout = 10000; + string connStringNow = connBuilder.ToString(); // 2.. Test with valid key path // @@ -113,7 +108,7 @@ public void TestTrustedColumnEncryptionMasterKeyPathsWithOneServer(string connec trustedKeyPaths.Add(columnMasterKeyPath); SqlConnection.ColumnEncryptionTrustedMasterKeyPaths.Add(connBuilder.DataSource, trustedKeyPaths); - using (SqlConnection sqlConnection = new SqlConnection(string.Concat(connection, @";Column Encryption Setting = Enabled;"))) + using (SqlConnection sqlConnection = new SqlConnection(string.Concat(connStringNow, @";Column Encryption Setting = Enabled;"))) { sqlConnection.Open(); @@ -143,6 +138,8 @@ FROM [{tableName}] public void TestTrustedColumnEncryptionMasterKeyPathsWithMultipleServers(string connection) { SqlConnectionStringBuilder connBuilder = new SqlConnectionStringBuilder(connection); + connBuilder.ConnectTimeout = 10000; + string connStringNow = connBuilder.ToString(); // 3. Test with multiple servers with multiple key paths // @@ -174,7 +171,7 @@ public void TestTrustedColumnEncryptionMasterKeyPathsWithMultipleServers(string server2TrustedKeyPaths.Add(@"https://balneetestkeyvault.vault.azure.net/keys/CryptoTest4/f4eb1dbbe6a9446599efe3c952614e70"); SqlConnection.ColumnEncryptionTrustedMasterKeyPaths.Add(@"randomeserver", server2TrustedKeyPaths); - using (SqlConnection sqlConnection = new SqlConnection(string.Concat(connection, @";Column Encryption Setting = Enabled;"))) + using (SqlConnection sqlConnection = new SqlConnection(string.Concat(connStringNow, @";Column Encryption Setting = Enabled;"))) { sqlConnection.Open(); @@ -204,6 +201,8 @@ FROM [{tableName}] public void TestTrustedColumnEncryptionMasterKeyPathsWithInvalidInputs(string connection) { SqlConnectionStringBuilder connBuilder = new SqlConnectionStringBuilder(connection); + connBuilder.ConnectTimeout = 10000; + string connStringNow = connBuilder.ToString(); // 1. Test with null List // @@ -219,7 +218,7 @@ public void TestTrustedColumnEncryptionMasterKeyPathsWithInvalidInputs(string co // Prepare a dictionary with null list. SqlConnection.ColumnEncryptionTrustedMasterKeyPaths.Add(connBuilder.DataSource, (List)null); - using (SqlConnection sqlConnection = new SqlConnection(string.Concat(connection, @";Column Encryption Setting = Enabled;"))) + using (SqlConnection sqlConnection = new SqlConnection(string.Concat(connStringNow, @";Column Encryption Setting = Enabled;"))) { sqlConnection.Open(); @@ -251,7 +250,7 @@ FROM [{tableName}] List emptyKeyPathList = new List(); SqlConnection.ColumnEncryptionTrustedMasterKeyPaths.Add(connBuilder.DataSource, emptyKeyPathList); - using (SqlConnection sqlConnection = new SqlConnection(string.Concat(connection, @";Column Encryption Setting = Enabled;"))) + using (SqlConnection sqlConnection = new SqlConnection(string.Concat(connStringNow, @";Column Encryption Setting = Enabled;"))) { sqlConnection.Open(); @@ -286,7 +285,7 @@ FROM [{tableName}] invalidKeyPathList.Add(invalidKeyPath); SqlConnection.ColumnEncryptionTrustedMasterKeyPaths.Add(connBuilder.DataSource, invalidKeyPathList); - using (SqlConnection sqlConnection = new SqlConnection(string.Concat(connection, @";Column Encryption Setting = Enabled;"))) + using (SqlConnection sqlConnection = new SqlConnection(string.Concat(connStringNow, @";Column Encryption Setting = Enabled;"))) { sqlConnection.Open(); From 938720e9540717630af7b2e19dae8017c881f53a Mon Sep 17 00:00:00 2001 From: Karina Zhou Date: Mon, 4 Nov 2019 11:04:47 -0800 Subject: [PATCH 61/63] fix config --- src/Microsoft.Data.SqlClient/tests/ManualTests/config.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/config.json b/src/Microsoft.Data.SqlClient/tests/ManualTests/config.json index 2f46abe9fc..27218a2f5e 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/config.json +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/config.json @@ -1,10 +1,10 @@ { - "TCPConnectionString": "", + "TCPConnectionString": "Data Source=tcp:localhost;Database=Northwind;Integrated Security=true;", "NPConnectionString": "Data Source=np:localhost;Database=Northwind;Integrated Security=true;", - "TCPConnectionStringHGSVBS": "Data Source = WIN-K4TQB0TR8JQ; Database = Northwind; Attestation Protocol = HGS; Enclave Attestation Url= http://WIN-K4TQB0TR8JQ/Attestation;User ID=sa; Password=Moonshine4me; Integrated Security= False;", + "TCPConnectionStringHGSVBS": "", "TCPConnectionStringAASVBS": "", "TCPConnectionStringAASSGX": "", - "EnclaveEnabled": "true", + "EnclaveEnabled": false, "AADAccessToken": "", "AADPasswordConnectionString": "", "AzureKeyVaultURL": "", From d41a2844f7d87cdfa1f32838b05c29ad5d2478fa Mon Sep 17 00:00:00 2001 From: Karina Zhou Date: Mon, 4 Nov 2019 13:33:16 -0800 Subject: [PATCH 62/63] Add missing docs --- .../netfx/ref/Microsoft.Data.SqlClient.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) 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 c5f4cc6919..95eed9298e 100644 --- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs @@ -867,9 +867,7 @@ public SqlConnectionStringBuilder(string connectionString) { } [System.ComponentModel.DisplayNameAttribute("Enclave Attestation Url")] [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] public string EnclaveAttestationUrl { get { throw null; } set { } } - /// - /// To Do : add docs file - /// + /// [System.ComponentModel.DisplayNameAttribute("Attestation Protocol")] [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] public Microsoft.Data.SqlClient.SqlConnectionAttestationProtocol AttestationProtocol {get { throw null; } set { } } From a37a3bdcf367526fc4af2bc7535e9ec304543a38 Mon Sep 17 00:00:00 2001 From: Erik Ejlskov Jensen Date: Mon, 4 Nov 2019 21:19:36 +0100 Subject: [PATCH 63/63] Get and set expected value for DdType.Date and DbType.Time (#269) * Get and set expected value for DdType.Date and DbType.Time Breaks support for SQL Server 2008 (I believe) fixes #5 Extend DateTimeTest.ReaderParameterTest to cover DbType parameters --- .../src/Microsoft/Data/SqlClient/SqlEnums.cs | 3 +- .../Microsoft/Data/SqlClient/SqlParameter.cs | 6 +- .../src/Microsoft/Data/SqlClient/SqlEnums.cs | 3 +- .../Microsoft/Data/SqlClient/SqlParameter.cs | 6 +- .../tests/FunctionalTests/SqlParameterTest.cs | 53 +++++ .../SQL/DateTimeTest/DateTimeTest.cs | 185 +++++++++++++++++- 6 files changed, 243 insertions(+), 13 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlEnums.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlEnums.cs index 656858b1fa..e07a449607 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlEnums.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlEnums.cs @@ -278,6 +278,7 @@ internal static MetaType GetMetaTypeFromDbType(DbType target) case DbType.Currency: return s_metaMoney; case DbType.Date: + return s_metaDate; case DbType.DateTime: return s_metaDateTime; case DbType.Decimal: @@ -301,7 +302,7 @@ internal static MetaType GetMetaTypeFromDbType(DbType target) case DbType.StringFixedLength: return s_metaNChar; case DbType.Time: - return s_metaDateTime; + return MetaTime; case DbType.Xml: return MetaXml; case DbType.DateTime2: diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlParameter.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlParameter.cs index 81d4b47210..77a15904ae 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlParameter.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlParameter.cs @@ -330,11 +330,7 @@ public override DbType DbType set { MetaType metatype = _metaType; - if ((null == metatype) || (metatype.DbType != value) || - // Two special datetime cases for backward compat - // DbType.Date and DbType.Time should always be treated as setting DbType.DateTime instead - value == DbType.Date || - value == DbType.Time) + if ((null == metatype) || (metatype.DbType != value)) { PropertyTypeChanging(); _metaType = MetaType.GetMetaTypeFromDbType(value); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlEnums.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlEnums.cs index 5fe6338173..288eead39f 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlEnums.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlEnums.cs @@ -279,6 +279,7 @@ internal static MetaType GetMetaTypeFromDbType(DbType target) case DbType.Currency: return MetaMoney; case DbType.Date: + return MetaDate; case DbType.DateTime: return MetaDateTime; case DbType.Decimal: @@ -302,7 +303,7 @@ internal static MetaType GetMetaTypeFromDbType(DbType target) case DbType.StringFixedLength: return MetaNChar; case DbType.Time: - return MetaDateTime; + return MetaTime; case DbType.Xml: return MetaXml; case DbType.DateTime2: diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlParameter.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlParameter.cs index 21a4266759..1648e9f24d 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlParameter.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlParameter.cs @@ -343,11 +343,7 @@ override public DbType DbType set { MetaType metatype = _metaType; - if ((null == metatype) || (metatype.DbType != value) || - // SQLBU 504029: Two special datetime cases for backward compat - // DbType.Date and DbType.Time should always be treated as setting DbType.DateTime instead - value == DbType.Date || - value == DbType.Time) + if ((null == metatype) || (metatype.DbType != value)) { PropertyTypeChanging(); _metaType = MetaType.GetMetaTypeFromDbType(value); diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlParameterTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlParameterTest.cs index 8fe10372b9..203fcc5a65 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlParameterTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlParameterTest.cs @@ -648,6 +648,17 @@ public void InferType_TimeSpan() Assert.Equal(DbType.Time, param.DbType); } + [Fact] + public void InferType_DateTimeOffset() + { + DateTimeOffset value = new DateTimeOffset(new DateTime(2019, 10, 15), new TimeSpan(1, 0, 0)); + + SqlParameter param = new SqlParameter(); + param.Value = value; + Assert.Equal(SqlDbType.DateTimeOffset, param.SqlDbType); + Assert.Equal(DbType.DateTimeOffset, param.DbType); + } + [Fact] public void LocaleId() { @@ -834,6 +845,48 @@ public void ResetDbType() Assert.Null(p.Value); } + [Theory] + [InlineData(DbType.AnsiString, SqlDbType.VarChar)] + [InlineData(DbType.AnsiStringFixedLength, SqlDbType.Char)] + [InlineData(DbType.Binary, SqlDbType.VarBinary)] + [InlineData(DbType.Boolean, SqlDbType.Bit)] + [InlineData(DbType.Byte, SqlDbType.TinyInt)] + [InlineData(DbType.Currency, SqlDbType.Money)] + [InlineData(DbType.Date, SqlDbType.Date)] + [InlineData(DbType.DateTime, SqlDbType.DateTime)] + [InlineData(DbType.DateTime2, SqlDbType.DateTime2)] + [InlineData(DbType.DateTimeOffset, SqlDbType.DateTimeOffset)] + [InlineData(DbType.Decimal, SqlDbType.Decimal)] + [InlineData(DbType.Double, SqlDbType.Float)] + [InlineData(DbType.Guid, SqlDbType.UniqueIdentifier)] + [InlineData(DbType.Int16, SqlDbType.SmallInt)] + [InlineData(DbType.Int32, SqlDbType.Int)] + [InlineData(DbType.Int64, SqlDbType.BigInt)] + [InlineData(DbType.Object, SqlDbType.Variant)] + [InlineData(DbType.Single, SqlDbType.Real)] + [InlineData(DbType.String, SqlDbType.NVarChar)] + [InlineData(DbType.Time, SqlDbType.Time)] + [InlineData(DbType.Xml, SqlDbType.Xml)] + public void Parameter_Supported(DbType dbType, SqlDbType sqlDbType) + { + var parameter = new SqlParameter(); + parameter.DbType = dbType; + Assert.Equal(dbType, parameter.DbType); + Assert.Equal(sqlDbType, parameter.SqlDbType); + } + + [Theory] + [InlineData(DbType.SByte)] + [InlineData(DbType.UInt16)] + [InlineData(DbType.UInt32)] + [InlineData(DbType.UInt64)] + [InlineData(DbType.VarNumeric)] + public void Parameter_NotSupported(DbType dbType) + { + var parameter = new SqlParameter(); + Assert.Throws(() => parameter.DbType = dbType); + } + [Fact] public void ResetSqlDbType() { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DateTimeTest/DateTimeTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DateTimeTest/DateTimeTest.cs index d74525031f..294d949814 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DateTimeTest/DateTimeTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DateTimeTest/DateTimeTest.cs @@ -4,6 +4,7 @@ using System; using System.Data; +using System.Data.Common; using System.Data.SqlTypes; using Xunit; @@ -239,9 +240,172 @@ public static void ReaderParameterTest() Assert.True(IsValidParam(SqlDbType.DateTimeOffset, "c4", dto.LocalDateTime, conn, tableName), "FAILED: Invalid param for DateTimeOffset SqlDbType"); Assert.False(IsValidParam(SqlDbType.DateTimeOffset, "c4", new TimeSpan(), conn, tableName), "FAILED: Invalid param for DateTimeOffset SqlDbType"); Assert.True(IsValidParam(SqlDbType.DateTimeOffset, "c4", "9999-12-31 23:59:59.997 +00:00", conn, tableName), "FAILED: Invalid param for DateTimeOffset SqlDbType"); - #endregion } + // Do the same thing as above except using DbType now + using (DbCommand cmd3 = conn.CreateCommand()) + { + cmd3.CommandText = tempTableInsert2; + DbParameter pi = cmd3.CreateParameter(); + pi.DbType = DbType.Int32; + pi.ParameterName = "@pi"; + DbParameter p0 = cmd3.CreateParameter(); + p0.DbType = DbType.DateTime; + p0.ParameterName = "@p0"; + DbParameter p1 = cmd3.CreateParameter(); + p1.DbType = DbType.Date; + p1.ParameterName = "@p1"; + DbParameter p2 = cmd3.CreateParameter(); + p2.DbType = DbType.Time; + p2.ParameterName = "@p2"; + DbParameter p3 = cmd3.CreateParameter(); + p3.DbType = DbType.DateTime2; + p3.ParameterName = "@p3"; + DbParameter p4 = cmd3.CreateParameter(); + p4.DbType = DbType.DateTimeOffset; + p4.ParameterName = "@p4"; + pi.Value = DBNull.Value; + p0.Value = DBNull.Value; + p1.Value = DBNull.Value; + p2.Value = DBNull.Value; + p3.Value = DBNull.Value; + p4.Value = DBNull.Value; + p3.Scale = 7; + + cmd3.Parameters.Add(pi); + cmd3.Parameters.Add(p0); + cmd3.Parameters.Add(p1); + cmd3.Parameters.Add(p2); + cmd3.Parameters.Add(p3); + cmd3.Parameters.Add(p4); + cmd3.ExecuteNonQuery(); + pi.Value = 1; + p0.Value = new DateTime(2000, 12, 31); + p1.Value = new DateTime(2000, 12, 31); + p2.Value = new TimeSpan(23, 59, 59); + p3.Value = new DateTime(2000, 12, 31); + p4.Value = new DateTimeOffset(2000, 12, 31, 23, 59, 59, new TimeSpan(-8, 0, 0)); + p3.Scale = 3; + + // This used to be broken for p2/TimeSpan before removing back-compat code for DbType.Date and DbType.Time parameters + cmd3.ExecuteNonQuery(); + + // Test 2 + cmd3.CommandText = "SELECT COUNT(*) FROM " + tableName + " WHERE @pi = ci AND @p0 = c0 AND @p1 = c1 AND @p2 = c2 AND @p3 = c3 AND @p4 = c4"; + pi.Value = 0; + p0.Value = new DateTime(1753, 1, 1, 0, 0, 0); + p1.Value = new DateTime(1753, 1, 1, 0, 0, 0); + p2.Value = new TimeSpan(0, 20, 12, 13, 360); + p3.Value = new DateTime(2000, 12, 31, 23, 59, 59, 997); + p4.Value = new DateTimeOffset(9999, 12, 31, 23, 59, 59, 997, TimeSpan.Zero); + p4.Scale = 3; + cmd3.Parameters.Clear(); + cmd3.Parameters.Add(pi); + cmd3.Parameters.Add(p0); + cmd3.Parameters.Add(p1); + cmd3.Parameters.Add(p2); + cmd3.Parameters.Add(p3); + cmd3.Parameters.Add(p4); + + // This used to be broken for p2/TimeSpan before removing back-compat code for DbType.Date and DbType.Time parameters + object scalarResult = cmd3.ExecuteScalar(); + Assert.True(scalarResult.Equals(1), string.Format("FAILED: Execute scalar returned unexpected result. Expected: {0}. Actual: {1}.", 1, scalarResult)); + + // Test 3 + + using (SqlCommand cmd4 = conn.CreateCommand()) + { + cmd4.CommandType = CommandType.StoredProcedure; + cmd4.CommandText = procName; + p0 = cmd3.CreateParameter(); + p0.DbType = DbType.DateTime; + p0.ParameterName = "@p0"; + cmd4.Parameters.Add(p0); + p1 = cmd3.CreateParameter(); + p1.DbType = DbType.Date; + p1.ParameterName = "@p1"; + cmd4.Parameters.Add(p1); + p2 = cmd3.CreateParameter(); + p2.DbType = DbType.Time; + p2.ParameterName = "@p2"; + cmd4.Parameters.Add(p2); + p3 = cmd3.CreateParameter(); + p3.DbType = DbType.DateTime2; + p3.ParameterName = "@p3"; + cmd4.Parameters.Add(p3); + p4 = cmd3.CreateParameter(); + p4.DbType = DbType.DateTimeOffset; + p4.ParameterName = "@p4"; + cmd4.Parameters.Add(p4); + p0.Direction = ParameterDirection.Output; + p1.Direction = ParameterDirection.Output; + p2.Direction = ParameterDirection.Output; + p3.Direction = ParameterDirection.Output; + p4.Direction = ParameterDirection.Output; + p2.Scale = 7; + cmd4.ExecuteNonQuery(); + + Assert.True(p0.Value.Equals((new SqlDateTime(1753, 1, 1, 0, 0, 0)).Value), "FAILED: SqlParameter p0 contained incorrect value"); + Assert.True(p1.Value.Equals(new DateTime(1753, 1, 1, 0, 0, 0)), "FAILED: SqlParameter p1 contained incorrect value"); + // This used to be broken for p2/TimeSpan before removing back-compat code for DbType.Date and DbType.Time parameters + Assert.True(p2.Value.Equals(new TimeSpan(0, 20, 12, 13, 360)), "FAILED: SqlParameter p2 contained incorrect value"); + Assert.True(p2.Scale.Equals(7), "FAILED: SqlParameter p0 contained incorrect scale"); + Assert.True(p3.Value.Equals(new DateTime(2000, 12, 31, 23, 59, 59, 997)), "FAILED: SqlParameter p3 contained incorrect value"); + Assert.True(p3.Scale.Equals(7), "FAILED: SqlParameter p3 contained incorrect scale"); + Assert.True(p4.Value.Equals(new DateTimeOffset(9999, 12, 31, 23, 59, 59, 997, TimeSpan.Zero)), "FAILED: SqlParameter p4 contained incorrect value"); + Assert.True(p4.Scale.Equals(7), "FAILED: SqlParameter p4 contained incorrect scale"); + + // Test 4 + cmd4.CommandText = procNullName; + cmd4.ExecuteNonQuery(); + } + Assert.True(p0.Value.Equals(DBNull.Value), "FAILED: SqlParameter p0 expected to be NULL"); + Assert.True(p1.Value.Equals(DBNull.Value), "FAILED: SqlParameter p1 expected to be NULL"); + Assert.True(p2.Value.Equals(DBNull.Value), "FAILED: SqlParameter p2 expected to be NULL"); + Assert.True(p3.Value.Equals(DBNull.Value), "FAILED: SqlParameter p3 expected to be NULL"); + Assert.True(p4.Value.Equals(DBNull.Value), "FAILED: SqlParameter p4 expected to be NULL"); + + // Date + Assert.False(IsValidParam(DbType.Date, "c1", new DateTimeOffset(1753, 1, 1, 0, 0, 0, TimeSpan.Zero), conn, tableName), "FAILED: Invalid param for Date DbType"); + Assert.False(IsValidParam(DbType.Date, "c1", new SqlDateTime(1753, 1, 1, 0, 0, 0), conn, tableName), "FAILED: Invalid param for Date DbType"); + Assert.True(IsValidParam(DbType.Date, "c1", new DateTime(1753, 1, 1, 0, 0, 0, DateTimeKind.Unspecified), conn, tableName), "FAILED: Invalid param for Date DbType"); + Assert.True(IsValidParam(DbType.Date, "c1", new DateTime(1753, 1, 1, 0, 0, 0, DateTimeKind.Utc), conn, tableName), "FAILED: Invalid param for Date DbType"); + Assert.True(IsValidParam(DbType.Date, "c1", new DateTime(1753, 1, 1, 0, 0, 0, DateTimeKind.Local), conn, tableName), "FAILED: Invalid param for Date DbType"); + Assert.False(IsValidParam(DbType.Date, "c1", new TimeSpan(), conn, tableName), "FAILED: Invalid param for Date DbType"); + Assert.True(IsValidParam(DbType.Date, "c1", "1753-1-1", conn, tableName), "FAILED: Invalid param for Date DbType"); + + // Time + // These 7 Asserts used to be broken for before removing back-compat code for DbType.Date and DbType.Time parameters + Assert.False(IsValidParam(DbType.Time, "c2", new DateTimeOffset(1753, 1, 1, 0, 0, 0, TimeSpan.Zero), conn, tableName), "FAILED: Invalid param for Time DbType"); + Assert.False(IsValidParam(DbType.Time, "c2", new SqlDateTime(1753, 1, 1, 0, 0, 0), conn, tableName), "FAILED: Invalid param for Time DbType"); + Assert.False(IsValidParam(DbType.Time, "c2", new DateTime(1753, 1, 1, 0, 0, 0, DateTimeKind.Unspecified), conn, tableName), "FAILED: Invalid param for Time DbType"); + Assert.False(IsValidParam(DbType.Time, "c2", new DateTime(1753, 1, 1, 0, 0, 0, DateTimeKind.Utc), conn, tableName), "FAILED: Invalid param for Time DbType"); + Assert.False(IsValidParam(DbType.Time, "c2", new DateTime(1753, 1, 1, 0, 0, 0, DateTimeKind.Local), conn, tableName), "FAILED: Invalid param for Time DbType"); + Assert.True(IsValidParam(DbType.Time, "c2", TimeSpan.Parse("20:12:13.36"), conn, tableName), "FAILED: Invalid param for Time DbType"); + Assert.True(IsValidParam(DbType.Time, "c2", "20:12:13.36", conn, tableName), "FAILED: Invalid param for Time DbType"); + + // DateTime2 + DateTime dt = DateTime.Parse("2000-12-31 23:59:59.997"); + Assert.False(IsValidParam(DbType.DateTime2, "c3", new DateTimeOffset(1753, 1, 1, 0, 0, 0, TimeSpan.Zero), conn, tableName), "FAILED: Invalid param for DateTime2 DbType"); + Assert.False(IsValidParam(DbType.DateTime2, "c3", new SqlDateTime(2000, 12, 31, 23, 59, 59, 997), conn, tableName), "FAILED: Invalid param for DateTime2 DbType"); + Assert.True(IsValidParam(DbType.DateTime2, "c3", new DateTime(2000, 12, 31, 23, 59, 59, 997, DateTimeKind.Unspecified), conn, tableName), "FAILED: Invalid param for DateTime2 DbType"); + Assert.True(IsValidParam(DbType.DateTime2, "c3", new DateTime(2000, 12, 31, 23, 59, 59, 997, DateTimeKind.Utc), conn, tableName), "FAILED: Invalid param for DateTime2 DbType"); + Assert.True(IsValidParam(DbType.DateTime2, "c3", new DateTime(2000, 12, 31, 23, 59, 59, 997, DateTimeKind.Local), conn, tableName), "FAILED: Invalid param for DateTime2 DbType"); + Assert.False(IsValidParam(DbType.DateTime2, "c3", new TimeSpan(), conn, tableName), "FAILED: Invalid param for DateTime2 DbType"); + Assert.True(IsValidParam(DbType.DateTime2, "c3", "2000-12-31 23:59:59.997", conn, tableName), "FAILED: Invalid param for DateTime2 DbType"); + + // DateTimeOffset + DateTimeOffset dto = DateTimeOffset.Parse("9999-12-31 23:59:59.997 +00:00"); + Assert.True(IsValidParam(DbType.DateTimeOffset, "c4", dto, conn, tableName), "FAILED: Invalid param for DateTimeOffset DbType"); + Assert.False(IsValidParam(DbType.DateTimeOffset, "c4", new SqlDateTime(1753, 1, 1, 0, 0, 0), conn, tableName), "FAILED: Invalid param for DateTimeOffset DbType"); + Assert.True(IsValidParam(DbType.DateTimeOffset, "c4", new DateTime(9999, 12, 31, 15, 59, 59, 997, DateTimeKind.Unspecified), conn, tableName), "FAILED: Invalid param for DateTimeOffset DbType"); + Assert.True(IsValidParam(DbType.DateTimeOffset, "c4", dto.UtcDateTime, conn, tableName), "FAILED: Invalid param for DateTimeOffset DbType"); + Assert.True(IsValidParam(DbType.DateTimeOffset, "c4", dto.LocalDateTime, conn, tableName), "FAILED: Invalid param for DateTimeOffset DbType"); + Assert.False(IsValidParam(DbType.DateTimeOffset, "c4", new TimeSpan(), conn, tableName), "FAILED: Invalid param for DateTimeOffset DbType"); + Assert.True(IsValidParam(DbType.DateTimeOffset, "c4", "9999-12-31 23:59:59.997 +00:00", conn, tableName), "FAILED: Invalid param for DateTimeOffset DbType"); + } + #endregion + #region reader // Reader Tests cmd.CommandText = "SELECT * FROM " + tableName; @@ -443,6 +607,25 @@ private static bool IsValidParam(SqlDbType dbType, string col, object value, Sql return true; } + private static bool IsValidParam(DbType dbType, string col, object value, SqlConnection conn, string tempTable) + { + try + { + DbCommand cmd = new SqlCommand("SELECT COUNT(*) FROM " + tempTable + " WHERE " + col + " = @p", conn); + DbParameter p = cmd.CreateParameter(); + p.ParameterName = "@p"; + p.DbType = dbType; + p.Value = value; + cmd.Parameters.Add(p); + cmd.ExecuteScalar(); + } + catch (InvalidCastException) + { + return false; + } + return true; + } + private static bool IsString(SqlDataReader rdr, int column) { if (!rdr.IsDBNull(column))