From a54654920a2e39594b480f787b3dddadc0d7e913 Mon Sep 17 00:00:00 2001 From: Wraith2 Date: Mon, 18 Jan 2021 22:07:19 +0000 Subject: [PATCH 1/6] Sync similar elements Enclaves --- .../src/Microsoft.Data.SqlClient.csproj | 37 +- .../AzureAttestationBasedEnclaveProvider.cs | 21 +- ...=> EnclaveDelegate.CrossPlatformCrypto.cs} | 228 ++++---- .../SqlClient/EnclaveDelegate.NetStandard.cs | 5 +- .../netfx/src/Microsoft.Data.SqlClient.csproj | 30 +- .../AzureAttestationBasedEnclaveProvider.cs | 13 + .../SqlClient/EnclaveDelegate.CngCryto.cs | 230 ++++++++ .../Data/SqlClient/EnclaveDelegate.cs | 433 --------------- .../Microsoft/Data/SqlClient/SqlCommand.cs | 2 +- .../Data/SqlClient/SqlEnclaveSession.cs | 70 --- .../VirtualSecureModeEnclaveProvider.cs | 500 ------------------ .../AlwaysEncryptedEnclaveProviderUtils.cs | 26 +- .../SqlClient/AlwaysEncryptedKeyConverter.cs} | 91 +--- .../Data/SqlClient/EnclaveDelegate.cs | 111 ++-- .../Data/SqlClient/EnclavePackage.cs | 0 .../Data/SqlClient/SqlEnclaveSession.cs | 10 +- .../VirtualSecureModeEnclaveProvider.cs | 0 17 files changed, 531 insertions(+), 1276 deletions(-) rename src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/{EnclaveDelegate.NetCoreApp.cs => EnclaveDelegate.CrossPlatformCrypto.cs} (76%) create mode 100644 src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveDelegate.CngCryto.cs delete mode 100644 src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs delete mode 100644 src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlEnclaveSession.cs delete mode 100644 src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.cs rename src/Microsoft.Data.SqlClient/{netfx => }/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs (63%) rename src/Microsoft.Data.SqlClient/{netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs => src/Microsoft/Data/SqlClient/AlwaysEncryptedKeyConverter.cs} (61%) rename src/Microsoft.Data.SqlClient/{netcore => }/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs (63%) rename src/Microsoft.Data.SqlClient/{netcore => }/src/Microsoft/Data/SqlClient/EnclavePackage.cs (100%) rename src/Microsoft.Data.SqlClient/{netcore => }/src/Microsoft/Data/SqlClient/SqlEnclaveSession.cs (75%) rename src/Microsoft.Data.SqlClient/{netcore => }/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.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 79d03a8863..3d9210ae1e 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -313,6 +313,12 @@ Microsoft\Data\SqlClient\AlwaysEncryptedAttestationException.cs + + Microsoft\Data\SqlClient\AlwaysEncryptedEnclaveProviderUtils.cs + + + Microsoft\Data\SqlClient\AlwaysEncryptedKeyConverter.cs + Microsoft\Data\SqlClient\EnclaveProviderBase.cs @@ -321,11 +327,12 @@ - + - + + Microsoft\Data\SqlClient\VirtualSecureModeEnclaveProvider.cs + - @@ -409,6 +416,12 @@ + + Microsoft\Data\SqlClient\EnclaveDelegate.cs + + + Microsoft\Data\SqlClient\EnclavePackage.cs + @@ -441,6 +454,9 @@ + + Microsoft\Data\SqlClient\SqlEnclaveSession.cs + @@ -496,18 +512,15 @@ - - - - - Microsoft\Data\SqlClient\Reliability\SqlConfigurableRetryLogicManager.cs - - - Microsoft\Data\SqlClient\SqlUtil.cs - + + Microsoft\Data\SqlClient\Reliability\SqlConfigurableRetryLogicManager.cs + + + Microsoft\Data\SqlClient\SqlUtil.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.cs index a4bff1a65d..0ffa1ad5ce 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs @@ -215,7 +215,7 @@ public AzureAttestationInfo(byte[] attestationInfo) } catch (Exception exception) { - throw new AlwaysEncryptedAttestationException(String.Format(Strings.FailToParseAttestationInfo, exception.Message)); + throw new AlwaysEncryptedAttestationException(string.Format(Strings.FailToParseAttestationInfo, exception.Message)); } } } @@ -313,7 +313,7 @@ private void VerifyAzureAttestationInfo(string attestationUrl, EnclaveType encla if (!isSignatureValid) { - throw new AlwaysEncryptedAttestationException(String.Format(Strings.AttestationTokenSignatureValidationFailed, exceptionMessage)); + throw new AlwaysEncryptedAttestationException(string.Format(Strings.AttestationTokenSignatureValidationFailed, exceptionMessage)); } // Validate claims in the token @@ -349,7 +349,7 @@ private OpenIdConnectConfiguration GetOpenIdConfigForSigningKeys(string url, boo } catch (Exception exception) { - throw new AlwaysEncryptedAttestationException(String.Format(Strings.GetAttestationTokenSigningKeysFailed, GetInnerMostExceptionMessage(exception)), exception); + throw new AlwaysEncryptedAttestationException(string.Format(Strings.GetAttestationTokenSigningKeysFailed, GetInnerMostExceptionMessage(exception)), exception); } OpenIdConnectConfigurationCache.Add(url, openIdConnectConfig, DateTime.UtcNow.AddDays(1)); @@ -381,7 +381,7 @@ private static ICollection GenerateListOfIssuers(string tokenIssuerUrl) if (isDefaultPort) { - issuerUrls.Add(String.Concat(issuerUrl, ":", port.ToString())); + issuerUrls.Add(string.Concat(issuerUrl, ":", port.ToString())); } return issuerUrls; @@ -428,7 +428,7 @@ private bool VerifyTokenSignature(string attestationToken, string tokenIssuerUrl } catch (Exception exception) { - throw new AlwaysEncryptedAttestationException(String.Format(Strings.InvalidAttestationToken, GetInnerMostExceptionMessage(exception))); + throw new AlwaysEncryptedAttestationException(string.Format(Strings.InvalidAttestationToken, GetInnerMostExceptionMessage(exception))); } return isSignatureValid; @@ -456,7 +456,7 @@ private byte[] ComputeSHA256(byte[] data) private void ValidateAttestationClaims(EnclaveType enclaveType, string attestationToken, EnclavePublicKey enclavePublicKey, byte[] nonce) { // Read the json token - JsonWebToken token = null; + JsonWebToken token; try { JsonWebTokenHandler tokenHandler = new JsonWebTokenHandler(); @@ -464,7 +464,7 @@ private void ValidateAttestationClaims(EnclaveType enclaveType, string attestati } catch (ArgumentException argumentException) { - throw new AlwaysEncryptedAttestationException(String.Format(Strings.FailToParseAttestationToken, argumentException.Message)); + throw new AlwaysEncryptedAttestationException(string.Format(Strings.FailToParseAttestationToken, argumentException.Message)); } // Get all the claims from the token @@ -492,7 +492,7 @@ private void ValidateClaim(Dictionary claims, string claimName, bool hasClaim = claims.TryGetValue(claimName, out claimData); if (!hasClaim) { - throw new AlwaysEncryptedAttestationException(String.Format(Strings.MissingClaimInAttestationToken, claimName)); + throw new AlwaysEncryptedAttestationException(string.Format(Strings.MissingClaimInAttestationToken, claimName)); } // Get the Base64Url of the actual data and compare it with claim @@ -506,14 +506,13 @@ private void ValidateClaim(Dictionary claims, string claimName, throw new AlwaysEncryptedAttestationException(Strings.InvalidArgumentToBase64UrlDecoder); } - bool hasValidClaim = String.Equals(encodedActualData, claimData, StringComparison.Ordinal); + bool hasValidClaim = string.Equals(encodedActualData, claimData, StringComparison.Ordinal); if (!hasValidClaim) { - throw new AlwaysEncryptedAttestationException(String.Format(Strings.InvalidClaimInAttestationToken, claimName, claimData)); + throw new AlwaysEncryptedAttestationException(string.Format(Strings.InvalidClaimInAttestationToken, claimName, claimData)); } } - // Derives the shared secret between the client and enclave. private byte[] GetSharedSecret(EnclavePublicKey enclavePublicKey, byte[] nonce, EnclaveType enclaveType, EnclaveDiffieHellmanInfo enclaveDHInfo, ECDiffieHellman clientDHKey) { byte[] enclaveRsaPublicKey = enclavePublicKey.PublicKey; 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.CrossPlatformCrypto.cs similarity index 76% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetCoreApp.cs rename to src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.CrossPlatformCrypto.cs index e1cff2d0e0..232843e99e 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.CrossPlatformCrypto.cs @@ -7,55 +7,9 @@ namespace Microsoft.Data.SqlClient { - /// - /// A delegate for communicating with secure enclave - /// - internal partial class EnclaveDelegate + internal sealed 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; - byte[] attestationProtocolInputLengthBytes = null; - byte[] clientDHPublicKeyLengthBytes = null; - int attestationProtocolInt = sqlEnclaveAttestationParameters.Protocol; - - // attestation protocol - attestationProtocolBytes = GetUintBytes(enclaveType, attestationProtocolInt, "attestationProtocol"); - - if (attestationProtocolBytes == null) - { - throw SQL.NullArgumentInternal("attestationProtocolBytes", ClassName, GetSerializedAttestationParametersName); - } - - // attestationProtocolInput - byte[] attestationProtocolInputBytes = sqlEnclaveAttestationParameters.GetInput(); - - // attestationProtocolInput length - attestationProtocolInputLengthBytes = GetUintBytes(enclaveType, attestationProtocolInputBytes.Length, "attestationProtocolInputLength"); - - if (attestationProtocolInputLengthBytes == null) - { - throw SQL.NullArgumentInternal("attestationProtocolInputLengthBytes", ClassName, GetSerializedAttestationParametersName); - } - - // clientDHPublicKey - byte[] clientDHPublicKey = KeyConverter.ECDHPublicKeyToECCKeyBlob(sqlEnclaveAttestationParameters.ClientDiffieHellmanKey.PublicKey); - - // clientDHPublicKey length - clientDHPublicKeyLengthBytes = GetUintBytes(enclaveType, clientDHPublicKey.Length, "clientDHPublicKeyLength"); - - if (clientDHPublicKeyLengthBytes == null) - { - throw SQL.NullArgumentInternal("clientDHPublicKeyLengthBytes", ClassName, GetSerializedAttestationParametersName); - } - - return CombineByteArrays(new[] { attestationProtocolBytes, attestationProtocolInputLengthBytes, - attestationProtocolInputBytes, clientDHPublicKeyLengthBytes, clientDHPublicKey }); - } + private static readonly Dictionary s_enclaveProviders = new Dictionary(); /// /// Create a new enclave session @@ -70,23 +24,33 @@ internal byte[] GetSerializedAttestationParameters(SqlEnclaveAttestationParamete internal void CreateEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, EnclaveSessionParameters enclaveSessionParameters, byte[] attestationInfo, SqlEnclaveAttestationParameters attestationParameters, byte[] customData, int customDataLength) { - lock (_lock) { SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = GetEnclaveProvider(attestationProtocol, enclaveType); - long counter; - SqlEnclaveSession sqlEnclaveSession = null; - byte[] dummyCustomData = null; - int dummyCustomDataLength; - sqlColumnEncryptionEnclaveProvider.GetEnclaveSession(enclaveSessionParameters, false, out sqlEnclaveSession, out counter, out dummyCustomData, out dummyCustomDataLength); + sqlColumnEncryptionEnclaveProvider.GetEnclaveSession( + enclaveSessionParameters, + generateCustomData: false, + sqlEnclaveSession: out SqlEnclaveSession sqlEnclaveSession, + counter: out _, + customData: out _, + customDataLength: out _ + ); if (sqlEnclaveSession != null) { return; } - sqlColumnEncryptionEnclaveProvider.CreateEnclaveSession(attestationInfo, attestationParameters.ClientDiffieHellmanKey, enclaveSessionParameters, customData, customDataLength, out sqlEnclaveSession, out counter); + sqlColumnEncryptionEnclaveProvider.CreateEnclaveSession( + attestationInfo, + attestationParameters.ClientDiffieHellmanKey, + enclaveSessionParameters, + customData, + customDataLength, + out sqlEnclaveSession, + counter: out _ + ); if (sqlEnclaveSession == null) { @@ -95,6 +59,68 @@ internal void CreateEnclaveSession(SqlConnectionAttestationProtocol attestationP } } + internal void GetEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, EnclaveSessionParameters enclaveSessionParameters, bool generateCustomData, out SqlEnclaveSession sqlEnclaveSession, out byte[] customData, out int customDataLength) + { + GetEnclaveSession(attestationProtocol, enclaveType, enclaveSessionParameters, generateCustomData, out sqlEnclaveSession, out _, out customData, out customDataLength, throwIfNull: false); + } + + private void GetEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, EnclaveSessionParameters enclaveSessionParameters, bool generateCustomData, out SqlEnclaveSession sqlEnclaveSession, out long counter, out byte[] customData, out int customDataLength, bool throwIfNull) + { + SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = GetEnclaveProvider(attestationProtocol, enclaveType); + sqlColumnEncryptionEnclaveProvider.GetEnclaveSession(enclaveSessionParameters, generateCustomData, out sqlEnclaveSession, out counter, out customData, out customDataLength); + + if (throwIfNull && sqlEnclaveSession == null) + { + throw SQL.NullEnclaveSessionDuringQueryExecution(enclaveType, enclaveSessionParameters.AttestationUrl); + } + } + + internal void InvalidateEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, EnclaveSessionParameters enclaveSessionParameters, SqlEnclaveSession enclaveSession) + { + SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = GetEnclaveProvider(attestationProtocol, enclaveType); + sqlColumnEncryptionEnclaveProvider.InvalidateEnclaveSession(enclaveSessionParameters, enclaveSession); + } + + + private SqlColumnEncryptionEnclaveProvider GetEnclaveProvider(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType) + { + if (!s_enclaveProviders.TryGetValue(attestationProtocol, out SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider)) + { + switch (attestationProtocol) + { + case SqlConnectionAttestationProtocol.AAS: + AzureAttestationEnclaveProvider azureAttestationEnclaveProvider = new AzureAttestationEnclaveProvider(); + s_enclaveProviders[attestationProtocol] = azureAttestationEnclaveProvider; + sqlColumnEncryptionEnclaveProvider = s_enclaveProviders[attestationProtocol]; + break; + + case SqlConnectionAttestationProtocol.HGS: + HostGuardianServiceEnclaveProvider hostGuardianServiceEnclaveProvider = new HostGuardianServiceEnclaveProvider(); + s_enclaveProviders[attestationProtocol] = hostGuardianServiceEnclaveProvider; + sqlColumnEncryptionEnclaveProvider = s_enclaveProviders[attestationProtocol]; + break; + +#if ENCLAVE_SIMULATOR + case SqlConnectionAttestationProtocol.SIM: + SimulatorEnclaveProvider simulatorEnclaveProvider = new SimulatorEnclaveProvider(); + EnclaveProviders[attestationProtocol] = (SqlColumnEncryptionEnclaveProvider)simulatorEnclaveProvider; + sqlColumnEncryptionEnclaveProvider = EnclaveProviders[attestationProtocol]; + break; +#endif + + default: + break; + } + } + + if (sqlColumnEncryptionEnclaveProvider == null) + { + throw SQL.EnclaveProviderNotFound(enclaveType, ConvertAttestationProtocolToString(attestationProtocol)); + } + + return sqlColumnEncryptionEnclaveProvider; + } + /// /// Generate the byte package that needs to be sent to the enclave /// @@ -106,15 +132,22 @@ internal void CreateEnclaveSession(SqlConnectionAttestationProtocol attestationP /// internal EnclavePackage GenerateEnclavePackage(SqlConnectionAttestationProtocol attestationProtocol, Dictionary keysToBeSentToEnclave, string queryText, string enclaveType, EnclaveSessionParameters enclaveSessionParameters) { - - SqlEnclaveSession sqlEnclaveSession = null; + SqlEnclaveSession sqlEnclaveSession; long counter; - byte[] dummyCustomData = null; - int dummyCustomDataLength; try { - GetEnclaveSession(attestationProtocol, enclaveType, enclaveSessionParameters, false, out sqlEnclaveSession, out counter, out dummyCustomData, out dummyCustomDataLength, throwIfNull: true); + GetEnclaveSession( + attestationProtocol, + enclaveType, + enclaveSessionParameters, + generateCustomData: false, + sqlEnclaveSession: out sqlEnclaveSession, + counter: out counter, + customData: out _, + customDataLength: out _, + throwIfNull: true + ); } catch (Exception e) { @@ -127,16 +160,10 @@ internal EnclavePackage GenerateEnclavePackage(SqlConnectionAttestationProtocol byte[] sessionKey = sqlEnclaveSession.GetSessionKey(); byte[] encryptedBytePackage = EncryptBytePackage(keyBytePackage, sessionKey, enclaveSessionParameters.ServerName); byte[] enclaveSessionHandle = BitConverter.GetBytes(sqlEnclaveSession.SessionId); - byte[] byteArrayToBeSentToEnclave = CombineByteArrays(new[] { enclaveSessionHandle, encryptedBytePackage }); + byte[] byteArrayToBeSentToEnclave = CombineByteArrays(enclaveSessionHandle, encryptedBytePackage); return new EnclavePackage(byteArrayToBeSentToEnclave, sqlEnclaveSession); } - internal void InvalidateEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, EnclaveSessionParameters enclaveSessionParameters, SqlEnclaveSession enclaveSession) - { - SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = GetEnclaveProvider(attestationProtocol, enclaveType); - sqlColumnEncryptionEnclaveProvider.InvalidateEnclaveSession(enclaveSessionParameters, enclaveSession); - } - internal SqlEnclaveAttestationParameters GetAttestationParameters(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, string attestationUrl, byte[] customData, int customDataLength) { @@ -144,45 +171,39 @@ internal SqlEnclaveAttestationParameters GetAttestationParameters(SqlConnectionA return sqlColumnEncryptionEnclaveProvider.GetAttestationParameters(attestationUrl, customData, customDataLength); } - private SqlColumnEncryptionEnclaveProvider GetEnclaveProvider(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType) + internal byte[] GetSerializedAttestationParameters(SqlEnclaveAttestationParameters sqlEnclaveAttestationParameters, string enclaveType) { - SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = null; + byte[] attestationProtocolBytes = null; + byte[] attestationProtocolInputLengthBytes = null; + byte[] clientDHPublicKeyLengthBytes = null; + int attestationProtocolInt = sqlEnclaveAttestationParameters.Protocol; - if (!EnclaveProviders.TryGetValue(attestationProtocol, out sqlColumnEncryptionEnclaveProvider)) + attestationProtocolBytes = GetUintBytes(enclaveType, attestationProtocolInt, "attestationProtocol"); + + if (attestationProtocolBytes == null) { - switch (attestationProtocol) - { - case SqlConnectionAttestationProtocol.AAS: - AzureAttestationEnclaveProvider azureAttestationEnclaveProvider = new AzureAttestationEnclaveProvider(); - EnclaveProviders[attestationProtocol] = (SqlColumnEncryptionEnclaveProvider)azureAttestationEnclaveProvider; - sqlColumnEncryptionEnclaveProvider = EnclaveProviders[attestationProtocol]; - break; + throw SQL.NullArgumentInternal(nameof(attestationProtocolBytes), nameof(EnclaveDelegate), nameof(GetSerializedAttestationParameters)); + } - case SqlConnectionAttestationProtocol.HGS: - HostGuardianServiceEnclaveProvider hostGuardianServiceEnclaveProvider = new HostGuardianServiceEnclaveProvider(); - EnclaveProviders[attestationProtocol] = (SqlColumnEncryptionEnclaveProvider)hostGuardianServiceEnclaveProvider; - sqlColumnEncryptionEnclaveProvider = EnclaveProviders[attestationProtocol]; - break; + byte[] attestationProtocolInputBytes = sqlEnclaveAttestationParameters.GetInput(); -#if ENCLAVE_SIMULATOR - case SqlConnectionAttestationProtocol.SIM: - SimulatorEnclaveProvider simulatorEnclaveProvider = new SimulatorEnclaveProvider(); - EnclaveProviders[attestationProtocol] = (SqlColumnEncryptionEnclaveProvider)simulatorEnclaveProvider; - sqlColumnEncryptionEnclaveProvider = EnclaveProviders[attestationProtocol]; - break; -#endif + attestationProtocolInputLengthBytes = GetUintBytes(enclaveType, attestationProtocolInputBytes.Length, "attestationProtocolInputLength"); - default: - break; - } + if (attestationProtocolInputLengthBytes == null) + { + throw SQL.NullArgumentInternal(nameof(attestationProtocolInputLengthBytes), nameof(EnclaveDelegate), nameof(GetSerializedAttestationParameters)); } - if (sqlColumnEncryptionEnclaveProvider == null) + byte[] clientDHPublicKey = KeyConverter.ECDHPublicKeyToECCKeyBlob(sqlEnclaveAttestationParameters.ClientDiffieHellmanKey.PublicKey); + + clientDHPublicKeyLengthBytes = GetUintBytes(enclaveType, clientDHPublicKey.Length, "clientDHPublicKeyLength"); + + if (clientDHPublicKeyLengthBytes == null) { - throw SQL.EnclaveProviderNotFound(enclaveType, ConvertAttestationProtocolToString(attestationProtocol)); + throw SQL.NullArgumentInternal(nameof(clientDHPublicKeyLengthBytes), nameof(EnclaveDelegate), nameof(GetSerializedAttestationParameters)); } - return sqlColumnEncryptionEnclaveProvider; + return CombineByteArrays(attestationProtocolBytes, attestationProtocolInputLengthBytes, attestationProtocolInputBytes, clientDHPublicKeyLengthBytes, clientDHPublicKey); } private string ConvertAttestationProtocolToString(SqlConnectionAttestationProtocol attestationProtocol) @@ -204,22 +225,5 @@ private string ConvertAttestationProtocolToString(SqlConnectionAttestationProtoc return "NotSpecified"; } } - - internal void GetEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, EnclaveSessionParameters enclaveSessionParameters, bool generateCustomData, out SqlEnclaveSession sqlEnclaveSession, out byte[] customData, out int customDataLength) - { - long counter; - GetEnclaveSession(attestationProtocol, enclaveType, enclaveSessionParameters, generateCustomData, out sqlEnclaveSession, out counter, out customData, out customDataLength, throwIfNull: false); - } - - private void GetEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, EnclaveSessionParameters enclaveSessionParameters, bool generateCustomData, out SqlEnclaveSession sqlEnclaveSession, out long counter, out byte[] customData, out int customDataLength, bool throwIfNull) - { - SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = GetEnclaveProvider(attestationProtocol, enclaveType); - sqlColumnEncryptionEnclaveProvider.GetEnclaveSession(enclaveSessionParameters, generateCustomData, out sqlEnclaveSession, out counter, out customData, out customDataLength); - - if (throwIfNull && sqlEnclaveSession == null) - { - throw SQL.NullEnclaveSessionDuringQueryExecution(enclaveType, enclaveSessionParameters.AttestationUrl); - } - } } } 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 3005105a14..f33dac5f78 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 @@ -7,10 +7,7 @@ namespace Microsoft.Data.SqlClient { - /// - /// A delegate for communicating with secure enclave - /// - internal partial class EnclaveDelegate + internal sealed partial class EnclaveDelegate { internal byte[] GetSerializedAttestationParameters( SqlEnclaveAttestationParameters sqlEnclaveAttestationParameters, string enclaveType) 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 020103207e..4a592f6fb5 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -136,15 +136,24 @@ Microsoft\Data\SqlClient\ActiveDirectoryAuthenticationTimeoutRetryHelper.cs - - Microsoft\Data\SqlClient\AzureManagedIdentityAuthenticationProvider.cs - Microsoft\Data\SqlClient\ApplicationIntent.cs Microsoft\Data\SqlClient\ActiveDirectoryAuthenticationProvider.cs + + Microsoft\Data\SqlClient\AlwaysEncryptedEnclaveProviderUtils.cs + + + Microsoft\Data\SqlClient\AzureManagedIdentityAuthenticationProvider.cs + + + Microsoft\Data\SqlClient\EnclaveDelegate.cs + + + Microsoft\Data\SqlClient\EnclavePackage.cs + Microsoft\Data\SqlClient\Server\ExtendedClrTypeCode.cs @@ -295,6 +304,9 @@ Microsoft\Data\SqlClient\SqlSymmetricKeyCache.cs + + Microsoft\Data\SqlClient\VirtualSecureModeEnclaveProvider.cs + Microsoft\Data\SqlTypes\SqlTypeWorkarounds.cs @@ -375,18 +387,16 @@ - - - - + + + - @@ -426,7 +436,9 @@ - + + Microsoft\Data\SqlClient\SqlEnclaveSession.cs + 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 a79c4900b8..84a92c7ccc 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 @@ -133,6 +133,7 @@ internal override void InvalidateEnclaveSession(EnclaveSessionParameters enclave #endregion #region Internal Class + // A model class representing the deserialization of the byte payload the client // receives from SQL Server while setting up a session. // Protocol format: @@ -280,6 +281,7 @@ internal byte[] PrepareAttestationParameters(string attestationUrl, byte[] attes } } + // 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; @@ -318,6 +320,7 @@ private void VerifyAzureAttestationInfo(string attestationUrl, EnclaveType encla ValidateAttestationClaims(enclaveType, attestationToken, enclavePublicKey, nonce); } + // Returns the innermost exception value private static string GetInnerMostExceptionMessage(Exception exception) { Exception exLocal = exception; @@ -329,6 +332,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. private OpenIdConnectConfiguration GetOpenIdConfigForSigningKeys(string url, bool forceUpdate) { OpenIdConnectConfiguration openIdConnectConfig = OpenIdConnectConfigurationCache[url] as OpenIdConnectConfiguration; @@ -353,12 +358,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 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) private static ICollection GenerateListOfIssuers(string tokenIssuerUrl) { List issuerUrls = new List(); @@ -378,6 +387,7 @@ private static ICollection GenerateListOfIssuers(string tokenIssuerUrl) return issuerUrls; } + // 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; @@ -424,6 +434,7 @@ private bool VerifyTokenSignature(string attestationToken, string tokenIssuerUrl return isSignatureValid; } + // Computes the SHA256 hash of the byte array private byte[] ComputeSHA256(byte[] data) { byte[] result = null; @@ -441,6 +452,7 @@ private byte[] ComputeSHA256(byte[] data) return result; } + // Validate the claims in the attestation token private void ValidateAttestationClaims(EnclaveType enclaveType, string attestationToken, EnclavePublicKey enclavePublicKey, byte[] nonce) { // Read the json token @@ -472,6 +484,7 @@ private void ValidateAttestationClaims(EnclaveType enclaveType, string attestati } } + // Validate the claim value against the actual data private void ValidateClaim(Dictionary claims, string claimName, byte[] actualData) { // Get required claim data diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveDelegate.CngCryto.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveDelegate.CngCryto.cs new file mode 100644 index 0000000000..e8eac7a519 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveDelegate.CngCryto.cs @@ -0,0 +1,230 @@ +// 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 +{ + internal sealed partial class EnclaveDelegate + { + private static readonly Dictionary s_enclaveProviders = new Dictionary(); + + /// + /// Create a new enclave session + /// + /// attestation protocol + /// enclave type + /// The set of parameters required for enclave session. + /// attestation info from SQL Server + /// attestation parameters + /// A set of extra data needed for attestating the enclave. + /// The length of the extra data needed for attestating the enclave. + internal void CreateEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, EnclaveSessionParameters enclaveSessionParameters, + byte[] attestationInfo, SqlEnclaveAttestationParameters attestationParameters, byte[] customData, int customDataLength) + { + lock (_lock) + { + SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = GetEnclaveProvider(attestationProtocol, enclaveType); + + sqlColumnEncryptionEnclaveProvider.GetEnclaveSession( + enclaveSessionParameters, + generateCustomData: false, + sqlEnclaveSession: out SqlEnclaveSession sqlEnclaveSession, + counter: out _, + customData: out _, + customDataLength: out _ + ); + + if (sqlEnclaveSession != null) + { + return; + } + + sqlColumnEncryptionEnclaveProvider.CreateEnclaveSession( + attestationInfo, + attestationParameters.ClientDiffieHellmanKey, + enclaveSessionParameters, + customData, + customDataLength, + out sqlEnclaveSession, + counter: out _ + ); + + if (sqlEnclaveSession == null) + { + throw SQL.NullEnclaveSessionReturnedFromProvider(enclaveType, enclaveSessionParameters.AttestationUrl); + } + } + } + + internal void GetEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, EnclaveSessionParameters enclaveSessionParameters, bool generateCustomData, out SqlEnclaveSession sqlEnclaveSession, out byte[] customData, out int customDataLength) + { + GetEnclaveSession(attestationProtocol, enclaveType, enclaveSessionParameters, generateCustomData, out sqlEnclaveSession, out _, out customData, out customDataLength, throwIfNull: false); + } + + private void GetEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, EnclaveSessionParameters enclaveSessionParameters, bool generateCustomData, out SqlEnclaveSession sqlEnclaveSession, out long counter, out byte[] customData, out int customDataLength, bool throwIfNull) + { + SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = GetEnclaveProvider(attestationProtocol, enclaveType); + sqlColumnEncryptionEnclaveProvider.GetEnclaveSession(enclaveSessionParameters, generateCustomData, out sqlEnclaveSession, out counter, out customData, out customDataLength); + + if (throwIfNull && sqlEnclaveSession == null) + { + throw SQL.NullEnclaveSessionDuringQueryExecution(enclaveType, enclaveSessionParameters.AttestationUrl); + } + } + + internal void InvalidateEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, EnclaveSessionParameters enclaveSessionParameters, SqlEnclaveSession enclaveSession) + { + SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = GetEnclaveProvider(attestationProtocol, enclaveType); + sqlColumnEncryptionEnclaveProvider.InvalidateEnclaveSession(enclaveSessionParameters, enclaveSession); + } + + + private SqlColumnEncryptionEnclaveProvider GetEnclaveProvider(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType) + { + if (!s_enclaveProviders.TryGetValue(attestationProtocol, out SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider)) + { + switch (attestationProtocol) + { + case SqlConnectionAttestationProtocol.AAS: + AzureAttestationEnclaveProvider azureAttestationEnclaveProvider = new AzureAttestationEnclaveProvider(); + s_enclaveProviders[attestationProtocol] = azureAttestationEnclaveProvider; + sqlColumnEncryptionEnclaveProvider = s_enclaveProviders[attestationProtocol]; + break; + + case SqlConnectionAttestationProtocol.HGS: + HostGuardianServiceEnclaveProvider hostGuardianServiceEnclaveProvider = new HostGuardianServiceEnclaveProvider(); + s_enclaveProviders[attestationProtocol] = hostGuardianServiceEnclaveProvider; + sqlColumnEncryptionEnclaveProvider = s_enclaveProviders[attestationProtocol]; + break; + +#if ENCLAVE_SIMULATOR + case SqlConnectionAttestationProtocol.SIM: + SimulatorEnclaveProvider simulatorEnclaveProvider = new SimulatorEnclaveProvider(); + EnclaveProviders[attestationProtocol] = (SqlColumnEncryptionEnclaveProvider)simulatorEnclaveProvider; + sqlColumnEncryptionEnclaveProvider = EnclaveProviders[attestationProtocol]; + break; +#endif + + default: + break; + } + } + + if (sqlColumnEncryptionEnclaveProvider == null) + { + throw SQL.EnclaveProviderNotFound(enclaveType, ConvertAttestationProtocolToString(attestationProtocol)); + } + + return sqlColumnEncryptionEnclaveProvider; + } + + /// + /// Generate the byte package that needs to be sent to the enclave + /// + /// attestation protocol + /// Keys to be sent to enclave + /// + /// enclave type + /// The set of parameters required for enclave session. + /// + internal EnclavePackage GenerateEnclavePackage(SqlConnectionAttestationProtocol attestationProtocol, Dictionary keysToBeSentToEnclave, string queryText, string enclaveType, EnclaveSessionParameters enclaveSessionParameters) + { + SqlEnclaveSession sqlEnclaveSession; + long counter; + + try + { + GetEnclaveSession( + attestationProtocol, + enclaveType, + enclaveSessionParameters, + generateCustomData: false, + sqlEnclaveSession: out sqlEnclaveSession, + counter: out counter, + customData: out _, + customDataLength: out _, + throwIfNull: true + ); + } + catch (Exception e) + { + throw new RetryableEnclaveQueryExecutionException(e.Message, e); + } + + List decryptedKeysToBeSentToEnclave = GetDecryptedKeysToBeSentToEnclave(keysToBeSentToEnclave, enclaveSessionParameters.ServerName); + byte[] queryStringHashBytes = ComputeQueryStringHash(queryText); + byte[] keyBytePackage = GenerateBytePackageForKeys(counter, queryStringHashBytes, decryptedKeysToBeSentToEnclave); + byte[] sessionKey = sqlEnclaveSession.GetSessionKey(); + byte[] encryptedBytePackage = EncryptBytePackage(keyBytePackage, sessionKey, enclaveSessionParameters.ServerName); + byte[] enclaveSessionHandle = BitConverter.GetBytes(sqlEnclaveSession.SessionId); + byte[] byteArrayToBeSentToEnclave = CombineByteArrays(enclaveSessionHandle, encryptedBytePackage); + return new EnclavePackage(byteArrayToBeSentToEnclave, sqlEnclaveSession); + } + + + internal SqlEnclaveAttestationParameters GetAttestationParameters(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, string attestationUrl, byte[] customData, int customDataLength) + { + SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = GetEnclaveProvider(attestationProtocol, enclaveType); + return sqlColumnEncryptionEnclaveProvider.GetAttestationParameters(attestationUrl, customData, customDataLength); + } + + internal byte[] GetSerializedAttestationParameters(SqlEnclaveAttestationParameters sqlEnclaveAttestationParameters, string enclaveType) + { + byte[] attestationProtocolBytes = null; + byte[] attestationProtocolInputLengthBytes = null; + byte[] clientDHPublicKeyLengthBytes = null; + int attestationProtocolInt = sqlEnclaveAttestationParameters.Protocol; + + attestationProtocolBytes = GetUintBytes(enclaveType, attestationProtocolInt, "attestationProtocol"); + + if (attestationProtocolBytes == null) + { + throw SQL.NullArgumentInternal(nameof(attestationProtocolBytes), nameof(EnclaveDelegate), nameof(GetSerializedAttestationParameters)); + } + + byte[] attestationProtocolInputBytes = sqlEnclaveAttestationParameters.GetInput(); + + attestationProtocolInputLengthBytes = GetUintBytes(enclaveType, attestationProtocolInputBytes.Length, "attestationProtocolInputLength"); + + if (attestationProtocolInputLengthBytes == null) + { + throw SQL.NullArgumentInternal(nameof(attestationProtocolInputLengthBytes), nameof(EnclaveDelegate), nameof(GetSerializedAttestationParameters)); + } + + byte[] clientDHPublicKey = sqlEnclaveAttestationParameters.ClientDiffieHellmanKey.Key.Export(CngKeyBlobFormat.EccPublicBlob); + + clientDHPublicKeyLengthBytes = GetUintBytes(enclaveType, clientDHPublicKey.Length, "clientDHPublicKeyLength"); + + if (clientDHPublicKeyLengthBytes == null) + { + throw SQL.NullArgumentInternal(nameof(clientDHPublicKeyLengthBytes), nameof(EnclaveDelegate), nameof(GetSerializedAttestationParameters)); + } + + return CombineByteArrays(attestationProtocolBytes, attestationProtocolInputLengthBytes, attestationProtocolInputBytes, clientDHPublicKeyLengthBytes, clientDHPublicKey); + } + + private string ConvertAttestationProtocolToString(SqlConnectionAttestationProtocol attestationProtocol) + { + switch (attestationProtocol) + { + case SqlConnectionAttestationProtocol.AAS: + return "AAS"; + + case SqlConnectionAttestationProtocol.HGS: + return "HGS"; + +#if ENCLAVE_SIMULATOR + case SqlConnectionAttestationProtocol.SIM: + return "SIM"; +#endif + + default: + return "NotSpecified"; + } + } + } +} diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs deleted file mode 100644 index 09ba954353..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs +++ /dev/null @@ -1,433 +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.Linq; -using System.Security.Cryptography; -using System.Text; - -namespace Microsoft.Data.SqlClient -{ - /// - /// A delegate for communicating with secure enclave - /// - internal class EnclaveDelegate - { - private static readonly SqlAeadAes256CbcHmac256Factory SqlAeadAes256CbcHmac256Factory = new SqlAeadAes256CbcHmac256Factory(); - private static readonly string GetAttestationInfoQueryString = String.Format(@"Select GetTrustedModuleIdentityAndAttestationInfo({0}) as attestationInfo", 0); - private static readonly EnclaveDelegate _EnclaveDelegate = new EnclaveDelegate(); - - private static readonly string ClassName = "EnclaveDelegate"; - private static readonly string GetDecryptedKeysToBeSentToEnclaveName = "GetDecryptedKeysToBeSentToEnclave"; - 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 - public static EnclaveDelegate Instance { get { return _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 - /// The set of parameters required for enclave session. - /// - internal EnclavePackage GenerateEnclavePackage(SqlConnectionAttestationProtocol attestationProtocol, Dictionary keysTobeSentToEnclave, string queryText, string enclaveType, EnclaveSessionParameters enclaveSessionParameters) - { - - SqlEnclaveSession sqlEnclaveSession = null; - long counter; - byte[] dummyCustomData = null; - int dummyCustomDataLength; - try - { - GetEnclaveSession(attestationProtocol, enclaveType, enclaveSessionParameters, false, out sqlEnclaveSession, out counter, out dummyCustomData, out dummyCustomDataLength, throwIfNull: true); - } - catch (Exception e) - { - throw new RetryableEnclaveQueryExecutionException(e.Message, e); - } - - List decryptedKeysToBeSentToEnclave = GetDecryptedKeysToBeSentToEnclave(keysTobeSentToEnclave, enclaveSessionParameters.ServerName); - byte[] queryStringHashBytes = ComputeQueryStringHash(queryText); - byte[] keyBytePackage = GenerateBytePackageForKeys(counter, queryStringHashBytes, decryptedKeysToBeSentToEnclave); - byte[] sessionKey = sqlEnclaveSession.GetSessionKey(); - byte[] encryptedBytePackage = EncryptBytePackage(keyBytePackage, sessionKey, enclaveSessionParameters.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, EnclaveSessionParameters enclaveSessionParameters, SqlEnclaveSession enclaveSession) - { - SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = GetEnclaveProvider(attestationProtocol, enclaveType); - sqlColumnEncryptionEnclaveProvider.InvalidateEnclaveSession(enclaveSessionParameters, enclaveSession); - } - - internal void GetEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, EnclaveSessionParameters enclaveSessionParameters, bool generateCustomData, out SqlEnclaveSession sqlEnclaveSession, out byte[] customData, out int customDataLength) - { - long counter; - GetEnclaveSession(attestationProtocol, enclaveType, enclaveSessionParameters, generateCustomData, out sqlEnclaveSession, out counter, out customData, out customDataLength, throwIfNull: false); - } - - private void GetEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, EnclaveSessionParameters enclaveSessionParameters, bool generateCustomData, out SqlEnclaveSession sqlEnclaveSession, out long counter, out byte[] customData, out int customDataLength, bool throwIfNull) - { - SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = GetEnclaveProvider(attestationProtocol, enclaveType); - sqlColumnEncryptionEnclaveProvider.GetEnclaveSession(enclaveSessionParameters, generateCustomData, out sqlEnclaveSession, out counter, out customData, out customDataLength); - - if (throwIfNull && sqlEnclaveSession == null) - { - throw SQL.NullEnclaveSessionDuringQueryExecution(enclaveType, enclaveSessionParameters.AttestationUrl); - } - } - - internal SqlEnclaveAttestationParameters GetAttestationParameters(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, string attestationUrl, byte[] customData, int customDataLength) - { - SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = GetEnclaveProvider(attestationProtocol, enclaveType); - return sqlColumnEncryptionEnclaveProvider.GetAttestationParameters(attestationUrl, customData, customDataLength); - } - - internal byte[] GetSerializedAttestationParameters(SqlEnclaveAttestationParameters sqlEnclaveAttestationParameters, string enclaveType) - { - byte[] attestationProtocolBytes = null; - byte[] attestationProtocolInputLengthBytes = null; - byte[] clientDHPublicKeyLengthBytes = null; - int attestationProtocolInt = sqlEnclaveAttestationParameters.Protocol; - - // attestation protocol - attestationProtocolBytes = GetUintBytes(enclaveType, attestationProtocolInt, "attestationProtocol"); - - if (attestationProtocolBytes == null) - { - throw SQL.NullArgumentInternal("attestationProtocolBytes", ClassName, GetSerializedAttestationParametersName); - } - - // attestationProtocolInput - byte[] attestationProtocolInputBytes = sqlEnclaveAttestationParameters.GetInput(); - - // attestationProtocolInput length - attestationProtocolInputLengthBytes = GetUintBytes(enclaveType, attestationProtocolInputBytes.Length, "attestationProtocolInputLength"); - - if (attestationProtocolInputLengthBytes == null) - { - throw SQL.NullArgumentInternal("attestationProtocolInputLengthBytes", ClassName, GetSerializedAttestationParametersName); - } - - // clientDHPublicKey - byte[] clientDHPublicKey = sqlEnclaveAttestationParameters.ClientDiffieHellmanKey.Key.Export(CngKeyBlobFormat.EccPublicBlob); - - // clientDHPublicKey length - clientDHPublicKeyLengthBytes = GetUintBytes(enclaveType, clientDHPublicKey.Length, "clientDHPublicKeyLength"); - - if (clientDHPublicKeyLengthBytes == null) - { - throw SQL.NullArgumentInternal("clientDHPublicKeyLengthBytes", ClassName, GetSerializedAttestationParametersName); - } - - return CombineByteArrays(new[] { attestationProtocolBytes, attestationProtocolInputLengthBytes, attestationProtocolInputBytes, clientDHPublicKeyLengthBytes, clientDHPublicKey }); - } - - private byte[] GetUintBytes(string enclaveType, int intValue, string variableName) - { - try - { - uint attestationProtocol = Convert.ToUInt32(intValue); - return BitConverter.GetBytes(attestationProtocol); - } - catch (Exception e) - { - throw SQL.InvalidAttestationParameterUnableToConvertToUnsignedInt( - variableName, intValue, enclaveType, e); - } - } - - /// - /// Create a new enclave session - /// - /// attestation protocol - /// enclave type - /// The set of parameters required for enclave session. - /// attestation info from SQL Server - /// attestation parameters - /// A set of extra data needed for attestating the enclave. - /// The length of the extra data needed for attestating the enclave. - internal void CreateEnclaveSession(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, EnclaveSessionParameters enclaveSessionParameters, - byte[] attestationInfo, SqlEnclaveAttestationParameters attestationParameters, byte[] customData, int customDataLength) - { - - lock (_lock) - { - SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = GetEnclaveProvider(attestationProtocol, enclaveType); - long counter; - SqlEnclaveSession sqlEnclaveSession = null; - byte[] dummyCustomData = null; - int dummyCustomDataLength; - sqlColumnEncryptionEnclaveProvider.GetEnclaveSession(enclaveSessionParameters, false, out sqlEnclaveSession, out counter, out dummyCustomData, out dummyCustomDataLength); - - if (sqlEnclaveSession != null) - { - return; - } - - sqlColumnEncryptionEnclaveProvider.CreateEnclaveSession(attestationInfo, attestationParameters.ClientDiffieHellmanKey, enclaveSessionParameters, customData, customDataLength, out sqlEnclaveSession, out counter); - - if (sqlEnclaveSession == null) - { - throw SQL.NullEnclaveSessionReturnedFromProvider(enclaveType, enclaveSessionParameters.AttestationUrl); - } - } - } - - 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; - -#if ENCLAVE_SIMULATOR - case SqlConnectionAttestationProtocol.SIM: - SimulatorEnclaveProvider simulatorEnclaveProvider = new SimulatorEnclaveProvider(); - EnclaveProviders[attestationProtocol] = (SqlColumnEncryptionEnclaveProvider)simulatorEnclaveProvider; - sqlColumnEncryptionEnclaveProvider = EnclaveProviders[attestationProtocol]; - break; -#endif - - 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"; - -#if ENCLAVE_SIMULATOR - case SqlConnectionAttestationProtocol.SIM: - return "SIM"; -#endif - - default: - return "NotSpecified"; - } - } - - /// - /// 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(); - - foreach (SqlTceCipherInfoEntry cipherInfo in keysTobeSentToEnclave.Values) - { - SqlClientSymmetricKey sqlClientSymmetricKey = null; - SqlEncryptionKeyInfo? encryptionkeyInfoChosen = null; - SqlSecurityUtility.DecryptSymmetricKey(cipherInfo, serverName, out sqlClientSymmetricKey, - out encryptionkeyInfoChosen); - - if (sqlClientSymmetricKey == null) - throw SQL.NullArgumentInternal("sqlClientSymmetricKey", ClassName, GetDecryptedKeysToBeSentToEnclaveName); - if (cipherInfo.ColumnEncryptionKeyValues == null) - throw SQL.NullArgumentInternal("ColumnEncryptionKeyValues", ClassName, GetDecryptedKeysToBeSentToEnclaveName); - if (!(cipherInfo.ColumnEncryptionKeyValues.Count > 0)) - throw SQL.ColumnEncryptionKeysNotFound(); - - //cipherInfo.CekId is always 0, hence used cipherInfo.ColumnEncryptionKeyValues[0].cekId. Even when cek has multiple ColumnEncryptionKeyValues - //the cekid and the plaintext value will remain the same, what varies is the encrypted cek value, since the cek can be encrypted by - //multiple CMKs - decryptedKeysToBeSentToEnclave.Add(new ColumnEncryptionKeyInfo(sqlClientSymmetricKey.RootKey, - cipherInfo.ColumnEncryptionKeyValues[0].databaseId, - cipherInfo.ColumnEncryptionKeyValues[0].cekMdVersion, cipherInfo.ColumnEncryptionKeyValues[0].cekId)); - } - 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) - { - - //Format GUID | counter | queryStringHash | key[1]id | key[1]Bytes | ...... key[n]id | key[n]bytes - Guid guid = Guid.NewGuid(); - byte[] guidBytes = guid.ToByteArray(); - byte[] counterBytes = BitConverter.GetBytes(enclaveSessionCounter); - - int lengthOfByteArrayToAllocate = guidBytes.Length; - lengthOfByteArrayToAllocate += counterBytes.Length; - lengthOfByteArrayToAllocate += queryStringHashBytes.Length; - - foreach (ColumnEncryptionKeyInfo key in keys) - { - lengthOfByteArrayToAllocate += key.GetLengthForSerialization(); - } - - byte[] bytePackage = new byte[lengthOfByteArrayToAllocate]; - int startOffset = 0; - - Buffer.BlockCopy(guidBytes, 0, bytePackage, startOffset, guidBytes.Length); - startOffset += guidBytes.Length; - - Buffer.BlockCopy(counterBytes, 0, bytePackage, startOffset, counterBytes.Length); - startOffset += counterBytes.Length; - - Buffer.BlockCopy(queryStringHashBytes, 0, bytePackage, startOffset, queryStringHashBytes.Length); - startOffset += queryStringHashBytes.Length; - - foreach (ColumnEncryptionKeyInfo key in keys) - { - startOffset = key.SerializeToBuffer(bytePackage, startOffset); - } - - 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) - throw SQL.NullArgumentInternal("sessionKey", ClassName, "EncryptBytePackage"); - if (sessionKey.Length == 0) - throw SQL.EmptyArgumentInternal("sessionKey", ClassName, "EncryptBytePackage"); - //bytePackage is created internally in this class and is guaranteed to be non null and non empty - - try - { - SqlClientSymmetricKey symmetricKey = new SqlClientSymmetricKey(sessionKey); - SqlClientEncryptionAlgorithm sqlClientEncryptionAlgorithm = - SqlAeadAes256CbcHmac256Factory.Create(symmetricKey, SqlClientEncryptionType.Randomized, - SqlAeadAes256CbcHmac256Algorithm.AlgorithmName); - return sqlClientEncryptionAlgorithm.EncryptData(bytePackage); - } - catch (Exception e) - { - throw SQL.FailedToEncryptRegisterRulesBytePackage(e); - } - } - - /// - /// 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)]; - int offset = 0; - foreach (byte[] byteArray in byteArraysToCombine) - { - Buffer.BlockCopy(byteArray, 0, combinedArray, offset, byteArray.Length); - offset += byteArray.Length; - } - return combinedArray; - } - - private byte[] ComputeQueryStringHash(string queryString) - { - // Validate the input parameters - if (string.IsNullOrWhiteSpace(queryString)) - { - string argumentName = "queryString"; - if (null == queryString) - { - throw SQL.NullArgumentInternal(argumentName, ClassName, ComputeQueryStringHashName); - } - else - { - throw SQL.EmptyArgumentInternal(argumentName, ClassName, ComputeQueryStringHashName); - } - } - - byte[] queryStringBytes = Encoding.Unicode.GetBytes(queryString); - - // Compute hash - byte[] hash; - using (SHA256Cng sha256 = new SHA256Cng()) - { - sha256.TransformFinalBlock(queryStringBytes, 0, queryStringBytes.Length); - hash = sha256.Hash; - } - return hash; - } - - /// - /// Exception when executing a enclave based Always Encrypted query - /// - internal class RetryableEnclaveQueryExecutionException : Exception - { - internal RetryableEnclaveQueryExecutionException(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 - { - - internal SqlEnclaveSession EnclaveSession { get; } - internal byte[] EnclavePackageBytes { get; } - - /// - /// Constructor - /// - /// byte package to be sent to enclave - /// enclave session to be used - internal EnclavePackage(byte[] enclavePackageBytes, SqlEnclaveSession enclaveSession) - { - EnclavePackageBytes = enclavePackageBytes; - EnclaveSession = enclaveSession; - } - } - } -} 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 17a988f1cb..9036dc493f 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 @@ -121,7 +121,7 @@ private enum EXECTYPE private Dictionary keysToBeSentToEnclave; private bool requiresEnclaveComputations = false; - internal EnclaveDelegate.EnclavePackage enclavePackage = null; + internal EnclavePackage enclavePackage = null; private SqlEnclaveAttestationParameters enclaveAttestationParameters = null; private byte[] customData = null; private int customDataLength = 0; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlEnclaveSession.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlEnclaveSession.cs deleted file mode 100644 index a2d0cf56a4..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlEnclaveSession.cs +++ /dev/null @@ -1,70 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace Microsoft.Data.SqlClient -{ - - /// - internal class SqlEnclaveSession - { - - private static readonly string _sessionKeyName = "SessionKey"; - private static readonly string _className = "EnclaveSession"; - - private readonly byte[] _sessionKey; - - /// - internal long SessionId { get; } - - /// - internal byte[] GetSessionKey() - { - return Clone(_sessionKey); - } - - /// - /// Deep copy the array into a new array - /// - /// - /// - private byte[] Clone(byte[] arrayToClone) - { - - byte[] returnValue = new byte[arrayToClone.Length]; - - for (int i = 0; i < arrayToClone.Length; i++) - { - returnValue[i] = arrayToClone[i]; - } - - return returnValue; - } - - /// - internal SqlEnclaveSession(byte[] sessionKey, long sessionId/*, long counter*/) - { - if (null == sessionKey) - { throw SQL.NullArgumentInConstructorInternal(_sessionKeyName, _className); } - if (0 == sessionKey.Length) - { throw SQL.EmptyArgumentInConstructorInternal(_sessionKeyName, _className); } - - _sessionKey = sessionKey; - SessionId = sessionId; - } - } - - internal class EnclaveSessionParameters - { - internal string ServerName { get; set; } // The name of the SQL Server instance containing the enclave. - internal string AttestationUrl { get; set; } // The endpoint of an attestation service for attesting the enclave. - internal string Database { get; set; } // The database that SqlClient contacts to request an enclave session. - - internal EnclaveSessionParameters(string serverName, string attestationUrl, string database) - { - ServerName = serverName; - AttestationUrl = attestationUrl; - Database = database; - } - } -} 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 deleted file mode 100644 index 212d7a0047..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.cs +++ /dev/null @@ -1,500 +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.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 - internal class HostGuardianServiceEnclaveProvider : VirtualizationBasedSecurityEnclaveProviderBase - { - #region Members - - // this is endpoint given to us by HGS team from windows - private const string AttestationUrlSuffix = @"/v2.0/signingCertificates"; - - public int MaxNumRetries { get; set; } - - private int enclaveRetrySleepInSeconds = 3; - - 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 - 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[] - 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/AlwaysEncryptedEnclaveProviderUtils.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs similarity index 63% rename from src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs index 066d33be25..3263031dde 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Linq; namespace Microsoft.Data.SqlClient { @@ -29,27 +28,26 @@ public EnclaveDiffieHellmanInfo(byte[] payload) { Size = payload.Length; - int offset = 0; - int publicKeySize = BitConverter.ToInt32(payload, offset); - offset += sizeof(int); + int publicKeySize = BitConverter.ToInt32(payload, 0); + int publicKeySignatureSize = BitConverter.ToInt32(payload, 4); - 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; + PublicKey = new byte[publicKeySize]; + PublicKeySignature = new byte[publicKeySignatureSize]; + Buffer.BlockCopy(payload, 8, PublicKey, 0, publicKeySize); + Buffer.BlockCopy(payload, 8 + publicKeySize, PublicKeySignature, 0, publicKeySignatureSize); } } internal enum EnclaveType { None = 0, - + /// + /// Virtualization Based Security + /// Vbs = 1, - + /// + /// Intel SGX based security + /// Sgx = 2 } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AlwaysEncryptedKeyConverter.cs similarity index 61% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AlwaysEncryptedKeyConverter.cs index 23abf6b247..6e66ec5128 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AlwaysEncryptedKeyConverter.cs @@ -4,57 +4,10 @@ using System; using System.Diagnostics; -using System.Linq; using System.Security.Cryptography; namespace Microsoft.Data.SqlClient { - internal class EnclavePublicKey - { - public byte[] PublicKey { get; set; } - - public EnclavePublicKey(byte[] payload) - { - PublicKey = payload; - } - } - - 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; - - 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; - } - } - - internal enum EnclaveType - { - None = 0, - - Vbs = 1, - - Sgx = 2 - } - // Contains methods to convert cryptography keys between different formats. internal sealed class KeyConverter { @@ -65,15 +18,15 @@ internal sealed class KeyConverter private readonly struct RSAPublicKeyBlob { // Size of an RSA public key blob - internal static readonly int Size = 539; + internal const int Size = 539; // Size of the BCRYPT_RSAKEY_BLOB header - internal static readonly int HeaderSize = 27; + internal const int HeaderSize = 27; // Size of the exponent (final 3 bytes of the header) - internal static readonly int ExponentSize = 3; + internal const int ExponentSize = 3; // Size of the modulus (remaining bytes after the header) - internal static readonly int ModulusSize = Size - HeaderSize; - internal static readonly int ExponentOffset = HeaderSize - ExponentSize; - internal static readonly int ModulusOffset = HeaderSize; + internal const int ModulusSize = Size - HeaderSize; + internal const int ExponentOffset = HeaderSize - ExponentSize; + internal const int ModulusOffset = HeaderSize; } // Extracts the public key's modulus and exponent from an RSA public key blob @@ -82,10 +35,16 @@ internal static RSAParameters RSAPublicKeyBlobToParams(byte[] keyBlob) { Debug.Assert(keyBlob.Length == RSAPublicKeyBlob.Size, $"RSA public key blob was not the expected length. Actual: {keyBlob.Length}. Expected: {RSAPublicKeyBlob.Size}"); + + byte[] exponent = new byte[RSAPublicKeyBlob.ExponentSize]; + byte[] modulus = new byte[RSAPublicKeyBlob.ModulusSize]; + Buffer.BlockCopy(keyBlob, RSAPublicKeyBlob.ExponentOffset, exponent, RSAPublicKeyBlob.ExponentOffset, RSAPublicKeyBlob.ExponentSize); + Buffer.BlockCopy(keyBlob, RSAPublicKeyBlob.ModulusSize, modulus, RSAPublicKeyBlob.ModulusOffset, RSAPublicKeyBlob.ModulusSize); + return new RSAParameters() { - Exponent = keyBlob.Skip(RSAPublicKeyBlob.ExponentOffset).Take(RSAPublicKeyBlob.ExponentSize).ToArray(), - Modulus = keyBlob.Skip(RSAPublicKeyBlob.ModulusOffset).Take(RSAPublicKeyBlob.ModulusSize).ToArray() + Exponent = exponent, + Modulus = modulus }; } @@ -96,11 +55,11 @@ internal static RSAParameters RSAPublicKeyBlobToParams(byte[] keyBlob) private readonly struct ECCPublicKeyBlob { // Size of an ECC public key blob - internal static readonly int Size = 104; + internal const int Size = 104; // Size of the BCRYPT_ECCKEY_BLOB header - internal static readonly int HeaderSize = 8; + internal const int HeaderSize = 8; // Size of each coordinate - internal static readonly int KeySize = (Size - HeaderSize) / 2; + internal const int KeySize = (Size - HeaderSize) / 2; } // Magic numbers identifying blob types @@ -116,13 +75,19 @@ internal static ECParameters ECCPublicKeyBlobToParams(byte[] keyBlob) { Debug.Assert(keyBlob.Length == ECCPublicKeyBlob.Size, $"ECC public key blob was not the expected length. Actual: {keyBlob.Length}. Expected: {ECCPublicKeyBlob.Size}"); + + byte[] x = new byte[ECCPublicKeyBlob.KeySize]; + byte[] y = new byte[ECCPublicKeyBlob.KeySize]; + Buffer.BlockCopy(keyBlob, ECCPublicKeyBlob.HeaderSize, x, 0, ECCPublicKeyBlob.KeySize); + Buffer.BlockCopy(keyBlob, ECCPublicKeyBlob.HeaderSize + ECCPublicKeyBlob.KeySize, y, 0, ECCPublicKeyBlob.KeySize); + return new ECParameters { Curve = ECCurve.NamedCurves.nistP384, Q = new ECPoint { - X = keyBlob.Skip(ECCPublicKeyBlob.HeaderSize).Take(ECCPublicKeyBlob.KeySize).ToArray(), - Y = keyBlob.Skip(ECCPublicKeyBlob.HeaderSize + ECCPublicKeyBlob.KeySize).Take(ECCPublicKeyBlob.KeySize).ToArray() + X = x, + Y = y }, }; } @@ -137,7 +102,7 @@ internal static byte[] ECDHPublicKeyToECCKeyBlob(ECDiffieHellmanPublicKey public byte[] keyBlob = new byte[ECCPublicKeyBlob.Size]; // Set magic number - Array.Copy(KeyBlobMagicNumber.ECDHPublicP384, 0, keyBlob, 0, 4); + Buffer.BlockCopy(KeyBlobMagicNumber.ECDHPublicP384, 0, keyBlob, 0, 4); // Set key size keyBlob[4] = (byte)ECCPublicKeyBlob.KeySize; @@ -145,8 +110,8 @@ internal static byte[] ECDHPublicKeyToECCKeyBlob(ECDiffieHellmanPublicKey public Debug.Assert(ecPoint.X.Length == ECCPublicKeyBlob.KeySize && ecPoint.Y.Length == ECCPublicKeyBlob.KeySize, $"ECDH public key was not the expected length. Actual (X): {ecPoint.X.Length}. Actual (Y): {ecPoint.Y.Length} Expected: {ECCPublicKeyBlob.Size}"); // Copy x and y coordinates to key blob - Array.Copy(ecPoint.X, 0, keyBlob, ECCPublicKeyBlob.HeaderSize, ECCPublicKeyBlob.KeySize); - Array.Copy(ecPoint.Y, 0, keyBlob, ECCPublicKeyBlob.HeaderSize + ECCPublicKeyBlob.KeySize, ECCPublicKeyBlob.KeySize); + Buffer.BlockCopy(ecPoint.X, 0, keyBlob, ECCPublicKeyBlob.HeaderSize, ECCPublicKeyBlob.KeySize); + Buffer.BlockCopy(ecPoint.Y, 0, keyBlob, ECCPublicKeyBlob.HeaderSize + ECCPublicKeyBlob.KeySize, ECCPublicKeyBlob.KeySize); return keyBlob; } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs similarity index 63% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs index 27c8bb18a9..ba9b91bffc 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Security.Cryptography; using System.Text; @@ -13,20 +12,19 @@ namespace Microsoft.Data.SqlClient /// /// A delegate for communicating with secure enclave /// - internal partial class EnclaveDelegate + internal sealed partial class EnclaveDelegate { - private static readonly SqlAeadAes256CbcHmac256Factory SqlAeadAes256CbcHmac256Factory = new SqlAeadAes256CbcHmac256Factory(); - private static readonly string GetAttestationInfoQueryString = String.Format(@"Select GetTrustedModuleIdentityAndAttestationInfo({0}) as attestationInfo", 0); - private static readonly string ClassName = "EnclaveDelegate"; - private static readonly string GetDecryptedKeysToBeSentToEnclaveName = "GetDecryptedKeysToBeSentToEnclave"; - private static readonly string ComputeQueryStringHashName = "ComputeQueryStringHash"; + private static readonly SqlAeadAes256CbcHmac256Factory s_sqlAeadAes256CbcHmac256Factory = new SqlAeadAes256CbcHmac256Factory(); + private static readonly EnclaveDelegate s_enclaveDelegate = new EnclaveDelegate(); - private readonly Object _lock = new Object(); + private readonly object _lock; - //singleton instance - internal static EnclaveDelegate Instance { get; } = new EnclaveDelegate(); + public static EnclaveDelegate Instance => s_enclaveDelegate; - private EnclaveDelegate() { } + private EnclaveDelegate() + { + _lock = new object(); + } private byte[] GetUintBytes(string enclaveType, int intValue, string variableName) { @@ -54,24 +52,32 @@ private List GetDecryptedKeysToBeSentToEnclave(Dictiona foreach (SqlTceCipherInfoEntry cipherInfo in keysTobeSentToEnclave.Values) { - SqlClientSymmetricKey sqlClientSymmetricKey = null; - SqlEncryptionKeyInfo? encryptionkeyInfoChosen = null; - SqlSecurityUtility.DecryptSymmetricKey(cipherInfo, serverName, out sqlClientSymmetricKey, - out encryptionkeyInfoChosen); + SqlSecurityUtility.DecryptSymmetricKey(cipherInfo, serverName, out SqlClientSymmetricKey sqlClientSymmetricKey, out SqlEncryptionKeyInfo? encryptionkeyInfoChosen); if (sqlClientSymmetricKey == null) - throw SQL.NullArgumentInternal("sqlClientSymmetricKey", ClassName, GetDecryptedKeysToBeSentToEnclaveName); + { + throw SQL.NullArgumentInternal(nameof(sqlClientSymmetricKey), nameof(EnclaveDelegate), nameof(GetDecryptedKeysToBeSentToEnclave)); + } if (cipherInfo.ColumnEncryptionKeyValues == null) - throw SQL.NullArgumentInternal("ColumnEncryptionKeyValues", ClassName, GetDecryptedKeysToBeSentToEnclaveName); + { + throw SQL.NullArgumentInternal(nameof(cipherInfo.ColumnEncryptionKeyValues), nameof(EnclaveDelegate), nameof(GetDecryptedKeysToBeSentToEnclave)); + } if (!(cipherInfo.ColumnEncryptionKeyValues.Count > 0)) + { throw SQL.ColumnEncryptionKeysNotFound(); + } //cipherInfo.CekId is always 0, hence used cipherInfo.ColumnEncryptionKeyValues[0].cekId. Even when cek has multiple ColumnEncryptionKeyValues //the cekid and the plaintext value will remain the same, what varies is the encrypted cek value, since the cek can be encrypted by //multiple CMKs - decryptedKeysToBeSentToEnclave.Add(new ColumnEncryptionKeyInfo(sqlClientSymmetricKey.RootKey, - cipherInfo.ColumnEncryptionKeyValues[0].databaseId, - cipherInfo.ColumnEncryptionKeyValues[0].cekMdVersion, cipherInfo.ColumnEncryptionKeyValues[0].cekId)); + decryptedKeysToBeSentToEnclave.Add( + new ColumnEncryptionKeyInfo( + sqlClientSymmetricKey.RootKey, + cipherInfo.ColumnEncryptionKeyValues[0].databaseId, + cipherInfo.ColumnEncryptionKeyValues[0].cekMdVersion, + cipherInfo.ColumnEncryptionKeyValues[0].cekId + ) + ); } return decryptedKeysToBeSentToEnclave; } @@ -130,17 +136,23 @@ private byte[] GenerateBytePackageForKeys(long enclaveSessionCounter, byte[] que private byte[] EncryptBytePackage(byte[] bytePackage, byte[] sessionKey, string serverName) { if (sessionKey == null) - throw SQL.NullArgumentInternal("sessionKey", ClassName, "EncryptBytePackage"); + { + throw SQL.NullArgumentInternal(nameof(sessionKey), nameof(EnclaveDelegate), nameof(EncryptBytePackage)); + } if (sessionKey.Length == 0) - throw SQL.EmptyArgumentInternal("sessionKey", ClassName, "EncryptBytePackage"); + { + throw SQL.EmptyArgumentInternal(nameof(sessionKey), nameof(EnclaveDelegate), nameof(EncryptBytePackage)); + } //bytePackage is created internally in this class and is guaranteed to be non null and non empty try { SqlClientSymmetricKey symmetricKey = new SqlClientSymmetricKey(sessionKey); - SqlClientEncryptionAlgorithm sqlClientEncryptionAlgorithm = - SqlAeadAes256CbcHmac256Factory.Create(symmetricKey, SqlClientEncryptionType.Randomized, - SqlAeadAes256CbcHmac256Algorithm.AlgorithmName); + SqlClientEncryptionAlgorithm sqlClientEncryptionAlgorithm = s_sqlAeadAes256CbcHmac256Factory.Create( + symmetricKey, + SqlClientEncryptionType.Randomized, + SqlAeadAes256CbcHmac256Algorithm.AlgorithmName + ); return sqlClientEncryptionAlgorithm.EncryptData(bytePackage); } catch (Exception e) @@ -149,20 +161,36 @@ 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) + private byte[] CombineByteArrays(byte[] arr1, byte[] arr2) { - byte[] combinedArray = new byte[byteArraysToCombine.Sum(ba => ba.Length)]; - int offset = 0; - foreach (byte[] byteArray in byteArraysToCombine) - { - Buffer.BlockCopy(byteArray, 0, combinedArray, offset, byteArray.Length); - offset += byteArray.Length; - } + // this complication avoids the usless allocation of a byte[][] to hold args + // it would be easier with spans so revisit if System.Memory is now a standard include + int length = arr1.Length + arr2.Length; + byte[] combinedArray = new byte[length]; + + Buffer.BlockCopy(arr1, 0, combinedArray, 0, arr1.Length); + Buffer.BlockCopy(arr2, 0, combinedArray, arr1.Length, arr2.Length); + + return combinedArray; + } + + private byte[] CombineByteArrays(byte[] arr1, byte[] arr2, byte[] arr3, byte[] arr4, byte[] arr5) + { + // this complication avoids the usless allocation of a byte[][] to hold args + // it would be easier with spans so revisit if System.Memory is now a standard include + int length = arr1.Length + arr2.Length + arr3.Length + arr4.Length + arr5.Length; + byte[] combinedArray = new byte[length]; + + Buffer.BlockCopy(arr1, 0, combinedArray, 0, arr1.Length); + int copied = arr1.Length; + Buffer.BlockCopy(arr2, 0, combinedArray, copied, arr2.Length); + copied += arr2.Length; + Buffer.BlockCopy(arr3, 0, combinedArray, copied, arr3.Length); + copied += arr3.Length; + Buffer.BlockCopy(arr4, 0, combinedArray, copied, arr4.Length); + copied += arr4.Length; + Buffer.BlockCopy(arr5, 0, combinedArray, copied, arr5.Length); + return combinedArray; } @@ -171,14 +199,13 @@ private byte[] ComputeQueryStringHash(string queryString) // Validate the input parameters if (string.IsNullOrWhiteSpace(queryString)) { - string argumentName = "queryString"; - if (null == queryString) + if (queryString == null) { - throw SQL.NullArgumentInternal(argumentName, ClassName, ComputeQueryStringHashName); + throw SQL.NullArgumentInternal(nameof(queryString), nameof(EnclaveDelegate), nameof(ComputeQueryStringHash)); } else { - throw SQL.EmptyArgumentInternal(argumentName, ClassName, ComputeQueryStringHashName); + throw SQL.EmptyArgumentInternal(nameof(queryString), nameof(EnclaveDelegate), nameof(ComputeQueryStringHash)); } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclavePackage.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclavePackage.cs similarity index 100% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclavePackage.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclavePackage.cs diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlEnclaveSession.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlEnclaveSession.cs similarity index 75% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlEnclaveSession.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlEnclaveSession.cs index fbafb58efa..b7b87112af 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlEnclaveSession.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlEnclaveSession.cs @@ -4,7 +4,7 @@ namespace Microsoft.Data.SqlClient { - /// + /// internal class SqlEnclaveSession { @@ -13,10 +13,10 @@ internal class SqlEnclaveSession private readonly byte[] _sessionKey; - /// + /// internal long SessionId { get; } - /// + /// internal byte[] GetSessionKey() { return Clone(_sessionKey); @@ -40,8 +40,8 @@ private byte[] Clone(byte[] arrayToClone) return returnValue; } - /// - internal SqlEnclaveSession(byte[] sessionKey, long sessionId/*, long counter*/) + /// + internal SqlEnclaveSession(byte[] sessionKey, long sessionId) { if (null == sessionKey) { diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.cs similarity index 100% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.cs From 6367d6ab2511c684377c2318a7defd92576a5fad Mon Sep 17 00:00:00 2001 From: Wraith2 Date: Mon, 25 Jan 2021 20:46:09 +0000 Subject: [PATCH 2/6] address feedback --- .../Microsoft/Data/SqlClient/AlwaysEncryptedKeyConverter.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AlwaysEncryptedKeyConverter.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AlwaysEncryptedKeyConverter.cs index 6e66ec5128..9a8d830cdc 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AlwaysEncryptedKeyConverter.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AlwaysEncryptedKeyConverter.cs @@ -38,8 +38,8 @@ internal static RSAParameters RSAPublicKeyBlobToParams(byte[] keyBlob) byte[] exponent = new byte[RSAPublicKeyBlob.ExponentSize]; byte[] modulus = new byte[RSAPublicKeyBlob.ModulusSize]; - Buffer.BlockCopy(keyBlob, RSAPublicKeyBlob.ExponentOffset, exponent, RSAPublicKeyBlob.ExponentOffset, RSAPublicKeyBlob.ExponentSize); - Buffer.BlockCopy(keyBlob, RSAPublicKeyBlob.ModulusSize, modulus, RSAPublicKeyBlob.ModulusOffset, RSAPublicKeyBlob.ModulusSize); + Buffer.BlockCopy(keyBlob, RSAPublicKeyBlob.ExponentOffset, exponent, 0, RSAPublicKeyBlob.ExponentSize); + Buffer.BlockCopy(keyBlob, RSAPublicKeyBlob.ModulusSize, modulus, 0, RSAPublicKeyBlob.ModulusSize); return new RSAParameters() { From 6c5fea1775585004a495bec7279d72c604a0f153 Mon Sep 17 00:00:00 2001 From: Wraith2 Date: Tue, 26 Jan 2021 08:51:15 +0000 Subject: [PATCH 3/6] address feedback --- .../src/Microsoft/Data/SqlClient/AlwaysEncryptedKeyConverter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AlwaysEncryptedKeyConverter.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AlwaysEncryptedKeyConverter.cs index 9a8d830cdc..6ccbd67fb6 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AlwaysEncryptedKeyConverter.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AlwaysEncryptedKeyConverter.cs @@ -39,7 +39,7 @@ internal static RSAParameters RSAPublicKeyBlobToParams(byte[] keyBlob) byte[] exponent = new byte[RSAPublicKeyBlob.ExponentSize]; byte[] modulus = new byte[RSAPublicKeyBlob.ModulusSize]; Buffer.BlockCopy(keyBlob, RSAPublicKeyBlob.ExponentOffset, exponent, 0, RSAPublicKeyBlob.ExponentSize); - Buffer.BlockCopy(keyBlob, RSAPublicKeyBlob.ModulusSize, modulus, 0, RSAPublicKeyBlob.ModulusSize); + Buffer.BlockCopy(keyBlob, RSAPublicKeyBlob.ModulusOffset, modulus, 0, RSAPublicKeyBlob.ModulusSize); return new RSAParameters() { From aa3ad3b28116e2a7ce18a8f43d36a13ee7241f45 Mon Sep 17 00:00:00 2001 From: Wraith2 Date: Fri, 5 Mar 2021 01:21:19 +0000 Subject: [PATCH 4/6] update Simulator provider creation --- .../Data/SqlClient/EnclaveDelegate.CrossPlatformCrypto.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.CrossPlatformCrypto.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.CrossPlatformCrypto.cs index 232843e99e..1f0d8ebc07 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.CrossPlatformCrypto.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.CrossPlatformCrypto.cs @@ -103,8 +103,8 @@ private SqlColumnEncryptionEnclaveProvider GetEnclaveProvider(SqlConnectionAttes #if ENCLAVE_SIMULATOR case SqlConnectionAttestationProtocol.SIM: SimulatorEnclaveProvider simulatorEnclaveProvider = new SimulatorEnclaveProvider(); - EnclaveProviders[attestationProtocol] = (SqlColumnEncryptionEnclaveProvider)simulatorEnclaveProvider; - sqlColumnEncryptionEnclaveProvider = EnclaveProviders[attestationProtocol]; + s_enclaveProviders[attestationProtocol] = (SqlColumnEncryptionEnclaveProvider)simulatorEnclaveProvider; + sqlColumnEncryptionEnclaveProvider = s_enclaveProviders[attestationProtocol]; break; #endif From ea9359aa705fc772b8a560b3635ee4049a876ddf Mon Sep 17 00:00:00 2001 From: Wraith2 Date: Fri, 5 Mar 2021 08:43:22 +0000 Subject: [PATCH 5/6] fix project file whitespace --- .../netfx/src/Microsoft.Data.SqlClient.csproj | 6 +++--- 1 file changed, 3 insertions(+), 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 4a592f6fb5..58e0037059 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -389,9 +389,9 @@ - - - + + + From 7c7dbe1f8cce57a981502c83f270bc31434266a3 Mon Sep 17 00:00:00 2001 From: Wraith2 Date: Mon, 8 Mar 2021 20:27:58 +0000 Subject: [PATCH 6/6] address feedback --- .../src/Microsoft/Data/SqlClient/EnclaveDelegate.CngCryto.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveDelegate.CngCryto.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveDelegate.CngCryto.cs index e8eac7a519..fb93672b20 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveDelegate.CngCryto.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveDelegate.CngCryto.cs @@ -104,8 +104,8 @@ private SqlColumnEncryptionEnclaveProvider GetEnclaveProvider(SqlConnectionAttes #if ENCLAVE_SIMULATOR case SqlConnectionAttestationProtocol.SIM: SimulatorEnclaveProvider simulatorEnclaveProvider = new SimulatorEnclaveProvider(); - EnclaveProviders[attestationProtocol] = (SqlColumnEncryptionEnclaveProvider)simulatorEnclaveProvider; - sqlColumnEncryptionEnclaveProvider = EnclaveProviders[attestationProtocol]; + s_enclaveProviders[attestationProtocol] = (SqlColumnEncryptionEnclaveProvider)simulatorEnclaveProvider; + sqlColumnEncryptionEnclaveProvider = s_enclaveProviders[attestationProtocol]; break; #endif