diff --git a/build.proj b/build.proj index edebcec39e..32f1734db9 100644 --- a/build.proj +++ b/build.proj @@ -25,7 +25,7 @@ - + @@ -34,7 +34,7 @@ - + @@ -116,14 +116,14 @@ - + - + diff --git a/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.csproj b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.csproj index 9a32df9809..77b6e7e1be 100644 --- a/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.csproj +++ b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.csproj @@ -6,7 +6,7 @@ {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7} netcoreapp netfx - Debug;Release;net46-Release;net46-Debug;netcoreapp2.1-Debug;netcoreapp2.1-Release; + Debug;Release;net46-Release;net46-Debug;netcoreapp2.1-Debug;netcoreapp2.1-Release;netcoreapp3.1-Debug;netcoreapp3.1-Release AnyCPU;x86;x64 $(ObjFolder)$(Configuration).$(Platform)\$(AddOnName) $(BinFolder)$(Configuration).$(Platform)\$(AddOnName) diff --git a/src/Microsoft.Data.SqlClient/add-ons/Directory.Build.props b/src/Microsoft.Data.SqlClient/add-ons/Directory.Build.props index 4fc891665f..e2303ef132 100644 --- a/src/Microsoft.Data.SqlClient/add-ons/Directory.Build.props +++ b/src/Microsoft.Data.SqlClient/add-ons/Directory.Build.props @@ -19,6 +19,7 @@ netcoreapp2.1 + netcoreapp3.1 net46 diff --git a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj index dad15bc623..0902edd7c1 100644 --- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj @@ -1,7 +1,8 @@  false - netcoreapp2.1;netcoreapp3.1;netstandard2.0 + netcoreapp2.1;netcoreapp3.1;netstandard2.0;netstandard2.1 + netstandard2.1 $(ObjFolder)$(Configuration)\$(AssemblyName)\ref\ $(BinFolder)$(Configuration)\$(AssemblyName)\ref\ $(OutputPath)\$(TargetFramework)\Microsoft.Data.SqlClient.xml 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 9232078830..8074efc2e8 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -1,7 +1,8 @@  Microsoft.Data.SqlClient - netcoreapp2.1;netcoreapp3.1;netstandard2.0 + netcoreapp2.1;netcoreapp3.1;netstandard2.0;netstandard2.1 + netstandard2.1 Strings.PlatformNotSupported_DataSqlClient $(OS) true @@ -226,11 +227,27 @@ - - - + + + + + + + + + + + + + + + + + + + @@ -238,26 +255,12 @@ - - - - - - - - - - - - - - diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.cs similarity index 100% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.NetCoreApp.cs rename to src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.cs diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.NetCoreApp.cs deleted file mode 100644 index 066d33be25..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.NetCoreApp.cs +++ /dev/null @@ -1,55 +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.Linq; - -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 - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs new file mode 100644 index 0000000000..23abf6b247 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs @@ -0,0 +1,153 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics; +using System.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 + { + // The RSA public key blob is structured as follows: + // BCRYPT_RSAKEY_BLOB header + // byte[ExponentSize] publicExponent + // byte[ModulusSize] modulus + private readonly struct RSAPublicKeyBlob + { + // Size of an RSA public key blob + internal static readonly int Size = 539; + // Size of the BCRYPT_RSAKEY_BLOB header + internal static readonly int HeaderSize = 27; + // Size of the exponent (final 3 bytes of the header) + internal static readonly 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; + } + + // Extracts the public key's modulus and exponent from an RSA public key blob + // and returns an RSAParameters object + 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}"); + return new RSAParameters() + { + Exponent = keyBlob.Skip(RSAPublicKeyBlob.ExponentOffset).Take(RSAPublicKeyBlob.ExponentSize).ToArray(), + Modulus = keyBlob.Skip(RSAPublicKeyBlob.ModulusOffset).Take(RSAPublicKeyBlob.ModulusSize).ToArray() + }; + } + + // The ECC public key blob is structured as follows: + // BCRYPT_ECCKEY_BLOB header + // byte[KeySize] X + // byte[KeySize] Y + private readonly struct ECCPublicKeyBlob + { + // Size of an ECC public key blob + internal static readonly int Size = 104; + // Size of the BCRYPT_ECCKEY_BLOB header + internal static readonly int HeaderSize = 8; + // Size of each coordinate + internal static readonly int KeySize = (Size - HeaderSize) / 2; + } + + // Magic numbers identifying blob types + // https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-wcce/cba27df5-4880-4f95-a879-783f8657e53b + private readonly struct KeyBlobMagicNumber + { + internal static readonly byte[] ECDHPublicP384 = new byte[] { 0x45, 0x43, 0x4b, 0x33 }; + } + + // Extracts the public key's X and Y coordinates from an ECC public key blob + // and returns an ECParameters object + 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}"); + 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() + }, + }; + } + + // Serializes an ECDiffieHellmanPublicKey to an ECC public key blob + // "ECDiffieHellmanPublicKey.ToByteArray() doesn't have a (standards-)defined export + // format. The version used by ECDiffieHellmanPublicKeyCng is Windows-specific" + // from https://github.com/dotnet/runtime/issues/27276 + // => ECDiffieHellmanPublicKey.ToByteArray() is not supported in Unix + internal static byte[] ECDHPublicKeyToECCKeyBlob(ECDiffieHellmanPublicKey publicKey) + { + byte[] keyBlob = new byte[ECCPublicKeyBlob.Size]; + + // Set magic number + Array.Copy(KeyBlobMagicNumber.ECDHPublicP384, 0, keyBlob, 0, 4); + // Set key size + keyBlob[4] = (byte)ECCPublicKeyBlob.KeySize; + + ECPoint ecPoint = publicKey.ExportParameters().Q; + 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); + return keyBlob; + } + } +} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs similarity index 96% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.NetCoreApp.cs rename to src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs index a2a8270803..a4bff1a65d 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs @@ -73,15 +73,15 @@ internal override void GetEnclaveSession(EnclaveSessionParameters enclaveSession // Gets the information that SqlClient subsequently uses to initiate the process of attesting the enclave and to establish a secure session with the enclave. internal override SqlEnclaveAttestationParameters GetAttestationParameters(string attestationUrl, byte[] customData, int customDataLength) { - ECDiffieHellmanCng clientDHKey = new ECDiffieHellmanCng(DiffieHellmanKeySize); - clientDHKey.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash; - clientDHKey.HashAlgorithm = CngAlgorithm.Sha256; + // The key derivation function and hash algorithm name are specified when key derivation is performed + ECDiffieHellman clientDHKey = ECDiffieHellman.Create(); + clientDHKey.KeySize = DiffieHellmanKeySize; byte[] attestationParam = PrepareAttestationParameters(attestationUrl, customData, customDataLength); return new SqlEnclaveAttestationParameters(AzureBasedAttestationProtocolId, attestationParam, clientDHKey); } // When overridden in a derived class, performs enclave attestation, generates a symmetric key for the session, creates a an enclave session and stores the session information in the cache. - internal override void CreateEnclaveSession(byte[] attestationInfo, ECDiffieHellmanCng clientDHKey, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength, out SqlEnclaveSession sqlEnclaveSession, out long counter) + internal override void CreateEnclaveSession(byte[] attestationInfo, ECDiffieHellman clientDHKey, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength, out SqlEnclaveSession sqlEnclaveSession, out long counter) { sqlEnclaveSession = null; counter = 0; @@ -514,7 +514,7 @@ private void ValidateClaim(Dictionary claims, string claimName, } // Derives the shared secret between the client and enclave. - private byte[] GetSharedSecret(EnclavePublicKey enclavePublicKey, byte[] nonce, EnclaveType enclaveType, EnclaveDiffieHellmanInfo enclaveDHInfo, ECDiffieHellmanCng clientDHKey) + private byte[] GetSharedSecret(EnclavePublicKey enclavePublicKey, byte[] nonce, EnclaveType enclaveType, EnclaveDiffieHellmanInfo enclaveDHInfo, ECDiffieHellman clientDHKey) { byte[] enclaveRsaPublicKey = enclavePublicKey.PublicKey; @@ -529,17 +529,18 @@ private byte[] GetSharedSecret(EnclavePublicKey enclavePublicKey, byte[] nonce, } // Perform signature verification. The enclave's DiffieHellman public key was signed by the enclave's RSA public key. - CngKey cngkey = CngKey.Import(enclaveRsaPublicKey, CngKeyBlobFormat.GenericPublicBlob); - using (RSACng rsacng = new RSACng(cngkey)) + RSAParameters rsaParams = KeyConverter.RSAPublicKeyBlobToParams(enclaveRsaPublicKey); + using (RSA rsa = RSA.Create(rsaParams)) { - if (!rsacng.VerifyData(enclaveDHInfo.PublicKey, enclaveDHInfo.PublicKeySignature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)) + if (!rsa.VerifyData(enclaveDHInfo.PublicKey, enclaveDHInfo.PublicKeySignature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)) { throw new ArgumentException(Strings.GetSharedSecretFailed); } } - CngKey key = CngKey.Import(enclaveDHInfo.PublicKey, CngKeyBlobFormat.GenericPublicBlob); - return clientDHKey.DeriveKeyMaterial(key); + ECParameters ecParams = KeyConverter.ECCPublicKeyBlobToParams(enclaveDHInfo.PublicKey); + ECDiffieHellman enclaveDHKey = ECDiffieHellman.Create(ecParams); + return clientDHKey.DeriveKeyFromHash(enclaveDHKey.PublicKey, HashAlgorithmName.SHA256); } #endregion } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetCoreApp.cs index db9f7a68f2..e1cff2d0e0 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveDelegate.NetCoreApp.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; -using System.Security.Cryptography; namespace Microsoft.Data.SqlClient { @@ -44,7 +43,7 @@ internal byte[] GetSerializedAttestationParameters(SqlEnclaveAttestationParamete } // clientDHPublicKey - byte[] clientDHPublicKey = sqlEnclaveAttestationParameters.ClientDiffieHellmanKey.Key.Export(CngKeyBlobFormat.EccPublicBlob); + byte[] clientDHPublicKey = KeyConverter.ECDHPublicKeyToECCKeyBlob(sqlEnclaveAttestationParameters.ClientDiffieHellmanKey.PublicKey); // clientDHPublicKey length clientDHPublicKeyLengthBytes = GetUintBytes(enclaveType, clientDHPublicKey.Length, "clientDHPublicKeyLength"); @@ -54,7 +53,8 @@ internal byte[] GetSerializedAttestationParameters(SqlEnclaveAttestationParamete throw SQL.NullArgumentInternal("clientDHPublicKeyLengthBytes", ClassName, GetSerializedAttestationParametersName); } - return CombineByteArrays(new[] { attestationProtocolBytes, attestationProtocolInputLengthBytes, attestationProtocolInputBytes, clientDHPublicKeyLengthBytes, clientDHPublicKey }); + return CombineByteArrays(new[] { attestationProtocolBytes, attestationProtocolInputLengthBytes, + attestationProtocolInputBytes, clientDHPublicKeyLengthBytes, clientDHPublicKey }); } /// @@ -88,7 +88,7 @@ internal byte[] GetSerializedAttestationParameters(SqlEnclaveAttestationParamete sqlColumnEncryptionEnclaveProvider.CreateEnclaveSession(attestationInfo, attestationParameters.ClientDiffieHellmanKey, enclaveSessionParameters, customData, customDataLength, out sqlEnclaveSession, out counter); - if (sqlEnclaveSession == null) + if (sqlEnclaveSession == null) { throw SQL.NullEnclaveSessionReturnedFromProvider(enclaveType, enclaveSessionParameters.AttestationUrl); } @@ -137,7 +137,7 @@ internal void InvalidateEnclaveSession(SqlConnectionAttestationProtocol attestat sqlColumnEncryptionEnclaveProvider.InvalidateEnclaveSession(enclaveSessionParameters, enclaveSession); } - + internal SqlEnclaveAttestationParameters GetAttestationParameters(SqlConnectionAttestationProtocol attestationProtocol, string enclaveType, string attestationUrl, byte[] customData, int customDataLength) { SqlColumnEncryptionEnclaveProvider sqlColumnEncryptionEnclaveProvider = GetEnclaveProvider(attestationProtocol, enclaveType); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveProviderBase.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs similarity index 100% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveProviderBase.NetCoreApp.cs rename to src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveSessionCache.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveSessionCache.cs similarity index 100% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveSessionCache.NetCoreApp.cs rename to src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveSessionCache.cs diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SimulatorEnclaveProvider.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SimulatorEnclaveProvider.NetCoreApp.cs index 064742be3c..bed6ada9e3 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SimulatorEnclaveProvider.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SimulatorEnclaveProvider.NetCoreApp.cs @@ -30,15 +30,15 @@ internal override void GetEnclaveSession(EnclaveSessionParameters enclaveSession // Gets the information that SqlClient subsequently uses to initiate the process of attesting the enclave and to establish a secure session with the enclave. internal override SqlEnclaveAttestationParameters GetAttestationParameters(string attestationUrl, byte[] customData, int customDataLength) { - ECDiffieHellmanCng clientDHKey = new ECDiffieHellmanCng(384); - clientDHKey.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash; - clientDHKey.HashAlgorithm = CngAlgorithm.Sha256; + // The key derivation function and hash algorithm name are specified when key derivation is performed + ECDiffieHellman clientDHKey = ECDiffieHellman.Create(); + clientDHKey.KeySize = 384; return new SqlEnclaveAttestationParameters(2, new byte[] { }, clientDHKey); } // When overridden in a derived class, performs enclave attestation, generates a symmetric key for the session, creates a an enclave session and stores the session information in the cache. - internal override void CreateEnclaveSession(byte[] attestationInfo, ECDiffieHellmanCng clientDHKey, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength, out SqlEnclaveSession sqlEnclaveSession, out long counter) + internal override void CreateEnclaveSession(byte[] attestationInfo, ECDiffieHellman clientDHKey, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength, out SqlEnclaveSession sqlEnclaveSession, out long counter) { ////for simulator: enclave does not send public key, and sends an empty attestation info //// The only non-trivial content it sends is the session setup info (DH pubkey of enclave) @@ -84,8 +84,9 @@ internal override void CreateEnclaveSession(byte[] attestationInfo, ECDiffieHell Buffer.BlockCopy(attestationInfo, attestationInfoOffset, trustedModuleDHPublicKeySignature, 0, checked((int)sizeOfTrustedModuleDHPublicKeySignatureBuffer)); - CngKey k = CngKey.Import(trustedModuleDHPublicKey, CngKeyBlobFormat.EccPublicBlob); - byte[] sharedSecret = clientDHKey.DeriveKeyMaterial(k); + ECParameters ecParams = KeyConverter.ECCPublicKeyBlobToParams(trustedModuleDHPublicKey); + ECDiffieHellman enclaveDHKey = ECDiffieHellman.Create(ecParams); + byte[] sharedSecret = clientDHKey.DeriveKeyFromHash(enclaveDHKey.PublicKey, HashAlgorithmName.SHA256); long sessionId = BitConverter.ToInt64(enclaveSessionHandle, 0); sqlEnclaveSession = AddEnclaveSessionToCache(enclaveSessionParameters, sharedSecret, sessionId, out counter); } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCertificateStoreProvider.Windows.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCertificateStoreProvider.Windows.cs index e05025d2f8..c6f2786583 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCertificateStoreProvider.Windows.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCertificateStoreProvider.Windows.cs @@ -482,10 +482,11 @@ private byte[] RSAEncrypt(byte[] plainText, X509Certificate2 certificate) } /// - /// Encrypt the text using specified certificate. + /// Decrypt the data using specified certificate. /// /// Text to decrypt. /// Certificate object. + /// Returns a decrypted blob or throws an exception if there are any errors. private byte[] RSADecrypt(byte[] cipherText, X509Certificate2 certificate) { Debug.Assert((cipherText != null) && (cipherText.Length != 0)); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.NetCoreApp.cs index c608036c6e..cca04fc323 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.NetCoreApp.cs @@ -19,7 +19,7 @@ internal abstract partial class SqlColumnEncryptionEnclaveProvider /// The length of the extra data needed for attestating the enclave. /// The requested enclave session or null if the provider does not implement session caching. /// A counter that the enclave provider is expected to increment each time SqlClient retrieves the session from the cache. The purpose of this field is to prevent replay attacks. - internal abstract void CreateEnclaveSession(byte[] enclaveAttestationInfo, ECDiffieHellmanCng clientDiffieHellmanKey, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength, + internal abstract void CreateEnclaveSession(byte[] enclaveAttestationInfo, ECDiffieHellman clientDiffieHellmanKey, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength, out SqlEnclaveSession sqlEnclaveSession, out long counter); } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlEnclaveAttestationParameters.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlEnclaveAttestationParameters.NetCoreApp.cs index 1f59daf511..739187a7e9 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlEnclaveAttestationParameters.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlEnclaveAttestationParameters.NetCoreApp.cs @@ -14,10 +14,10 @@ internal partial class SqlEnclaveAttestationParameters private static readonly string _className = "EnclaveAttestationParameters"; /// - internal ECDiffieHellmanCng ClientDiffieHellmanKey { get; } + internal ECDiffieHellman ClientDiffieHellmanKey { get; } /// - internal SqlEnclaveAttestationParameters(int protocol, byte[] input, ECDiffieHellmanCng clientDiffieHellmanKey) + internal SqlEnclaveAttestationParameters(int protocol, byte[] input, ECDiffieHellman clientDiffieHellmanKey) { _input = input ?? throw SQL.NullArgumentInConstructorInternal(_inputName, _className); Protocol = protocol; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.cs similarity index 100% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.NetCoreApp.cs rename to src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.cs diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs similarity index 91% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.NetCoreApp.cs rename to src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs index 66f3f71a65..663b4a4e19 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs @@ -94,14 +94,15 @@ internal override void GetEnclaveSession(EnclaveSessionParameters enclaveSession // Gets the information that SqlClient subsequently uses to initiate the process of attesting the enclave and to establish a secure session with the enclave. internal override SqlEnclaveAttestationParameters GetAttestationParameters(string attestationUrl, byte[] customData, int customDataLength) { - ECDiffieHellmanCng clientDHKey = new ECDiffieHellmanCng(DiffieHellmanKeySize); - clientDHKey.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash; - clientDHKey.HashAlgorithm = CngAlgorithm.Sha256; + // The key derivation function and hash algorithm name are specified when key derivation is performed + ECDiffieHellman clientDHKey = ECDiffieHellman.Create(); + clientDHKey.KeySize = DiffieHellmanKeySize; + return new SqlEnclaveAttestationParameters(VsmHGSProtocolId, new byte[] { }, clientDHKey); } // When overridden in a derived class, performs enclave attestation, generates a symmetric key for the session, creates a an enclave session and stores the session information in the cache. - internal override void CreateEnclaveSession(byte[] attestationInfo, ECDiffieHellmanCng clientDHKey, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength, out SqlEnclaveSession sqlEnclaveSession, out long counter) + internal override void CreateEnclaveSession(byte[] attestationInfo, ECDiffieHellman clientDHKey, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength, out SqlEnclaveSession sqlEnclaveSession, out long counter) { sqlEnclaveSession = null; counter = 0; @@ -295,14 +296,12 @@ private void VerifyEnclaveReportSignature(EnclaveReportPackage enclaveReportPack } // IDK_S is contained in healthReport cert public key - RSA rsacsp = healthReportCert.GetRSAPublicKey(); - RSAParameters rsaparams = rsacsp.ExportParameters(includePrivateParameters: false); - RSACng rsacng = new RSACng(); - rsacng.ImportParameters(rsaparams); - - if (!rsacng.VerifyData(enclaveReportPackage.ReportAsBytes, enclaveReportPackage.SignatureBlob, HashAlgorithmName.SHA256, RSASignaturePadding.Pss)) + using (RSA rsa = healthReportCert.GetRSAPublicKey()) { - throw new ArgumentException(Strings.VerifyEnclaveReportFailed); + if (!rsa.VerifyData(enclaveReportPackage.ReportAsBytes, enclaveReportPackage.SignatureBlob, HashAlgorithmName.SHA256, RSASignaturePadding.Pss)) + { + throw new ArgumentException(Strings.VerifyEnclaveReportFailed); + } } } @@ -349,20 +348,22 @@ private void VerifyEnclavePolicyProperty(string property, uint actual, uint expe } // Derives the shared secret between the client and enclave. - private byte[] GetSharedSecret(EnclavePublicKey enclavePublicKey, EnclaveDiffieHellmanInfo enclaveDHInfo, ECDiffieHellmanCng clientDHKey) + private byte[] GetSharedSecret(EnclavePublicKey enclavePublicKey, EnclaveDiffieHellmanInfo enclaveDHInfo, ECDiffieHellman clientDHKey) { // Perform signature verification. The enclave's DiffieHellman public key was signed by the enclave's RSA public key. - CngKey cngkey = CngKey.Import(enclavePublicKey.PublicKey, CngKeyBlobFormat.GenericPublicBlob); - RSACng rsacng = new RSACng(cngkey); - if (!rsacng.VerifyData(enclaveDHInfo.PublicKey, enclaveDHInfo.PublicKeySignature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)) + RSAParameters rsaParams = KeyConverter.RSAPublicKeyBlobToParams(enclavePublicKey.PublicKey); + using (RSA rsa = RSA.Create(rsaParams)) { - throw new ArgumentException(Strings.GetSharedSecretFailed); + if (!rsa.VerifyData(enclaveDHInfo.PublicKey, enclaveDHInfo.PublicKeySignature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)) + { + throw new ArgumentException(Strings.GetSharedSecretFailed); + } } - CngKey key = CngKey.Import(enclaveDHInfo.PublicKey, CngKeyBlobFormat.GenericPublicBlob); - return clientDHKey.DeriveKeyMaterial(key); + ECParameters ecParams = KeyConverter.ECCPublicKeyBlobToParams(enclaveDHInfo.PublicKey); + ECDiffieHellman enclaveDHKey = ECDiffieHellman.Create(ecParams); + return clientDHKey.DeriveKeyFromHash(enclaveDHKey.PublicKey, HashAlgorithmName.SHA256); } - #endregion } } diff --git a/src/Microsoft.Data.SqlClient/tests/Directory.Build.props b/src/Microsoft.Data.SqlClient/tests/Directory.Build.props index 5d26340cf8..3225e9461a 100644 --- a/src/Microsoft.Data.SqlClient/tests/Directory.Build.props +++ b/src/Microsoft.Data.SqlClient/tests/Directory.Build.props @@ -7,6 +7,7 @@ net46 + netcoreapp3.1 netcoreapp2.1 Project @@ -15,20 +16,20 @@ - $(TargetNetCoreVersion) + $(TargetNetCoreVersion) - $(TargetNetCoreVersion) - $(TargetNetFxVersion) + $(TargetNetCoreVersion) + $(TargetNetFxVersion) - $(Configuration.Split('-')[0]) + $(Configuration.Split('-')[0]) diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj index ed7e701212..cbaa4a9520 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj @@ -10,20 +10,20 @@ $(BinFolder)$(Configuration).$(Platform).$(AssemblyName) - - - + + + + + - - diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionStringBuilderTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionStringBuilderTest.cs index f0b08da092..e1eae7b608 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionStringBuilderTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionStringBuilderTest.cs @@ -52,8 +52,10 @@ public class SqlConnectionStringBuilderTest [InlineData("PersistSecurityInfo = true")] [InlineData("Pooling = no")] [InlineData("Pooling = false")] +#if netcoreapp // PoolBlockingPeriod is not supported in .NET Standard [InlineData("PoolBlockingPeriod = Auto")] [InlineData("PoolBlockingperiod = NeverBlock")] +#endif [InlineData("Replication = true")] [InlineData("Transaction Binding = Explicit Unbind")] [InlineData("Trust Server Certificate = true")] diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs index bb74323fe0..5096d15af4 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs @@ -18,18 +18,16 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted { /// /// Always Encrypted public API Manual tests. - /// TODO: These tests are marked as Windows only for now but should be run for all platforms once the Master Key is accessible to this app from Azure Key Vault. /// - [PlatformSpecific(TestPlatforms.Windows)] - public class ApiShould : IClassFixture, IDisposable + public class ApiShould : IClassFixture, IDisposable { - private SQLSetupStrategyCertStoreProvider fixture; + private SQLSetupStrategy fixture; private readonly string tableName; - public ApiShould(SQLSetupStrategyCertStoreProvider fixture) + public ApiShould(PlatformSpecificTestContext context) { - this.fixture = fixture; + fixture = context.Fixture; tableName = fixture.ApiTestTable.Name; } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/BulkCopyAE.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/BulkCopyAE.cs index dccd65736a..dd2b2b2561 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/BulkCopyAE.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/BulkCopyAE.cs @@ -11,18 +11,16 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted { /// /// Always Encrypted public API Manual tests. - /// TODO: These tests are marked as Windows only for now but should be run for all platforms once the Master Key is accessible to this app from Azure Key Vault. /// - [PlatformSpecific(TestPlatforms.Windows)] - public class BulkCopyAE : IClassFixture, IDisposable + public class BulkCopyAE : IClassFixture, IDisposable { - private SQLSetupStrategyCertStoreProvider fixture; + private SQLSetupStrategy fixture; private readonly string tableName; - public BulkCopyAE(SQLSetupStrategyCertStoreProvider fixture) + public BulkCopyAE(PlatformSpecificTestContext context) { - this.fixture = fixture; + fixture = context.Fixture; tableName = fixture.BulkCopyAETestTable.Name; } @@ -34,7 +32,7 @@ public void TestBulkCopyString(string connectionString) dataTable.Columns.Add("c1", typeof(string)); var dataRow = dataTable.NewRow(); - String result = "stringValue"; + string result = "stringValue"; dataRow["c1"] = result; dataTable.Rows.Add(dataRow); dataTable.AcceptChanges(); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/BulkCopyAEErrorMessage.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/BulkCopyAEErrorMessage.cs index 111c64fd43..cf2da1c4b6 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/BulkCopyAEErrorMessage.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/BulkCopyAEErrorMessage.cs @@ -10,20 +10,18 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted { /// /// Always Encrypted public API Manual tests. - /// TODO: These tests are marked as Windows only for now but should be run for all platforms once the Master Key is accessible to this app from Azure Key Vault. /// - [PlatformSpecific(TestPlatforms.Windows)] - public class BulkCopyAEErrorMessage : IClassFixture + public class BulkCopyAEErrorMessage : IClassFixture { - private SQLSetupStrategyCertStoreProvider _fixture; - + private SQLSetupStrategy _fixture; + private readonly string _tableName; private readonly string _columnName; - public BulkCopyAEErrorMessage(SQLSetupStrategyCertStoreProvider fixture) + public BulkCopyAEErrorMessage(PlatformSpecificTestContext context) { - _fixture = fixture; - _tableName = fixture.BulkCopyAEErrorMessageTestTable.Name; + _fixture = context.Fixture; + _tableName = _fixture.BulkCopyAEErrorMessageTestTable.Name; _columnName = "c1"; } @@ -35,8 +33,8 @@ public void TextToIntErrorMessageTest(string connectionString) DataTable dataTable = CreateDataTable(value); Assert.True(StringToIntTest(connectionString, _tableName, dataTable, value, dataTable.Rows.Count), "Did not get any exceptions for DataTable when converting data from 'string' to 'int' datatype!"); - Assert.True(StringToIntTest(connectionString, _tableName, dataTable.Select(), value, dataTable.Rows.Count),"Did not get any exceptions for DataRow[] when converting data from 'string' to 'int' datatype!"); - Assert.True(StringToIntTest(connectionString, _tableName, dataTable.CreateDataReader(), value, -1),"Did not get any exceptions for DataReader when converting data from 'string' to 'int' datatype!"); + Assert.True(StringToIntTest(connectionString, _tableName, dataTable.Select(), value, dataTable.Rows.Count), "Did not get any exceptions for DataRow[] when converting data from 'string' to 'int' datatype!"); + Assert.True(StringToIntTest(connectionString, _tableName, dataTable.CreateDataReader(), value, -1), "Did not get any exceptions for DataReader when converting data from 'string' to 'int' datatype!"); } private DataTable CreateDataTable(string value) @@ -44,7 +42,7 @@ private DataTable CreateDataTable(string value) var dataTable = new DataTable(); dataTable.Columns.Add(_columnName, typeof(string)); - var dataRow = dataTable.NewRow(); + var dataRow = dataTable.NewRow(); dataRow[_columnName] = value; dataTable.Rows.Add(dataRow); dataTable.AcceptChanges(); @@ -90,7 +88,7 @@ private bool StringToIntTest(string connectionString, string targetTable, object catch (Exception ex) { string pattern; - object[] args = + object[] args = new object[] { string.Empty, value.GetType().Name, targetType, 0, _columnName, rowNo }; if (rowNo == -1) { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/EnclaveAzureDatabaseTests.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/EnclaveAzureDatabaseTests.cs index d90529976d..289ad6bc63 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/EnclaveAzureDatabaseTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/EnclaveAzureDatabaseTests.cs @@ -68,7 +68,6 @@ public EnclaveAzureDatabaseTests() } } - [PlatformSpecific(TestPlatforms.Windows)] [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsEnclaveAzureDatabaseSetup))] public void ConnectToAzureDatabaseWithEnclave() { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/End2EndSmokeTests.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/End2EndSmokeTests.cs index a8cbde4f1b..88fc0f6f66 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/End2EndSmokeTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/End2EndSmokeTests.cs @@ -11,16 +11,15 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted { - [PlatformSpecific(TestPlatforms.Windows)] - public class End2EndSmokeTests : IClassFixture, IDisposable + public class End2EndSmokeTests : IClassFixture, IDisposable { - private SQLSetupStrategyCertStoreProvider fixture; + private SQLSetupStrategy fixture; private readonly string tableName; - public End2EndSmokeTests(SQLSetupStrategyCertStoreProvider fixture) + public End2EndSmokeTests(PlatformSpecificTestContext context) { - this.fixture = fixture; + fixture = context.Fixture; tableName = fixture.End2EndSmokeTable.Name; } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ExceptionTestAKVStore.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ExceptionTestAKVStore.cs index 50eccd0b9e..a9503d69de 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ExceptionTestAKVStore.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ExceptionTestAKVStore.cs @@ -163,7 +163,7 @@ public void InvalidCertificatePath() Assert.Matches(errorMessage, ex2.Message); } - // [InlineData(true)] -> Enable with AE v2 + [InlineData(true)] [InlineData(false)] [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsAKVSetupAvailable))] public void AkvStoreProviderVerifyFunctionWithInvalidSignature(bool fEnclaveEnabled) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/SqlBulkCopyTruncation.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/SqlBulkCopyTruncation.cs index cd2c2dadc9..32dd86cd94 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/SqlBulkCopyTruncation.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/SqlBulkCopyTruncation.cs @@ -11,17 +11,16 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted { - [PlatformSpecific(TestPlatforms.Windows)] - public class SqlBulkCopyTruncation : IClassFixture + public class SqlBulkCopyTruncation : IClassFixture { - private SQLSetupStrategyCertStoreProvider _fixture; + private SQLSetupStrategy _fixture; private readonly Dictionary tableNames = new Dictionary(); - public SqlBulkCopyTruncation(SQLSetupStrategyCertStoreProvider fixture) + public SqlBulkCopyTruncation(PlatformSpecificTestContext context) { - _fixture = fixture; - tableNames = fixture.sqlBulkTruncationTableNames; + _fixture = context.Fixture; + tableNames = _fixture.sqlBulkTruncationTableNames; } [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/SqlNullValues.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/SqlNullValues.cs index 91d29836d0..caa174b3ce 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/SqlNullValues.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/SqlNullValues.cs @@ -11,17 +11,16 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted { - [PlatformSpecific(TestPlatforms.Windows)] - public class SqlNullValuesTests : IClassFixture, IDisposable + public class SqlNullValuesTests : IClassFixture, IDisposable { - private SQLSetupStrategyCertStoreProvider fixture; + private SQLSetupStrategy fixture; private readonly string tableName; private string UdfName = DatabaseHelper.GenerateUniqueName("SqlNullValuesRetVal"); private string UdfNameNotNull = DatabaseHelper.GenerateUniqueName("SqlNullValuesRetValNotNull"); - public SqlNullValuesTests(SQLSetupStrategyCertStoreProvider fixture) + public SqlNullValuesTests(PlatformSpecificTestContext context) { - this.fixture = fixture; + fixture = context.Fixture; tableName = fixture.SqlNullValuesTable.Name; // Disable the cache to avoid false failures. SqlConnection.ColumnEncryptionQueryMetadataCacheEnabled = false; @@ -33,7 +32,7 @@ public SqlNullValuesTests(SQLSetupStrategyCertStoreProvider fixture) { sqlConnection.Open(); - using (SqlCommand cmd = new SqlCommand(String.Format("INSERT INTO [{0}] (c1) VALUES (@c1)", tableName), sqlConnection, null, SqlCommandColumnEncryptionSetting.Enabled)) + using (SqlCommand cmd = new SqlCommand(string.Format("INSERT INTO [{0}] (c1) VALUES (@c1)", tableName), sqlConnection, null, SqlCommandColumnEncryptionSetting.Enabled)) { SqlParameter param = cmd.Parameters.Add("@c1", SqlDbType.Int); param.Value = DBNull.Value; @@ -79,7 +78,7 @@ public void NullValueTests(string connString, ConnStringColumnEncryptionSetting SqlParameter param; // Create a command similarly - using (SqlCommand cmd = new SqlCommand(String.Format("SELECT c1 FROM [{0}] ORDER BY c2 ASC", tableName), + using (SqlCommand cmd = new SqlCommand(string.Format("SELECT c1 FROM [{0}] ORDER BY c2 ASC", tableName), sqlConn, null, commandSetting)) { using (SqlDataReader reader = cmd.ExecuteReader()) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategy.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategy.cs index dc165a2747..7183655c41 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategy.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategy.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Runtime.InteropServices; using System.Security.Cryptography.X509Certificates; using Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted.Setup; using Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted.TestFixtures.Setup; @@ -253,4 +254,30 @@ public void Dispose() } } } + + // Use this class as the fixture for AE tests to ensure only one platform-specific fixture + // is created for each test class + public class PlatformSpecificTestContext : IDisposable + { + private SQLSetupStrategy certStoreFixture = null; + private SQLSetupStrategy akvFixture = null; + public SQLSetupStrategy Fixture => certStoreFixture ?? akvFixture; + + public PlatformSpecificTestContext() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + certStoreFixture = new SQLSetupStrategyCertStoreProvider(); + } + else + { + akvFixture = new SQLSetupStrategyAzureKeyVault(); + } + } + + public void Dispose() + { + Fixture.Dispose(); + } + } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/AkvColumnMasterKey.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/AkvColumnMasterKey.cs index a9469d79b6..353f7ac97a 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/AkvColumnMasterKey.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/AkvColumnMasterKey.cs @@ -17,7 +17,7 @@ public AkvColumnMasterKey(string name, string akvUrl, SqlColumnEncryptionKeyStor // For keys which allow enclave computation byte[] cmkSign = akvProvider.SignColumnMasterKeyMetadata(akvUrl, allowEnclaveComputations); - cmkSignStr = string.Concat("0x", BitConverter.ToString(cmkSign).Replace("-", string.Empty)); + CmkSignStr = string.Concat("0x", BitConverter.ToString(cmkSign).Replace("-", string.Empty)); } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ColumnMasterKey.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ColumnMasterKey.cs index 5eb02ffcd9..4e4cf8acf8 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ColumnMasterKey.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ColumnMasterKey.cs @@ -2,36 +2,29 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Data; -using System.Data.SqlTypes; -using Xunit; - namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted.Setup { public abstract class ColumnMasterKey : DbObject { - protected ColumnMasterKey(string name) : base(name) { } protected string KeyStoreProviderName { get; set; } - protected string cmkSignStr { get; set; } + protected string CmkSignStr { get; set; } public abstract string KeyPath { get; } public override void Create(SqlConnection sqlConnection) { string sql; - var connStrings = sqlConnection.ConnectionString; - if (DataTestUtility.EnclaveEnabled && !String.IsNullOrEmpty(cmkSignStr)) + if (DataTestUtility.EnclaveEnabled && !string.IsNullOrEmpty(CmkSignStr)) { sql = $@"CREATE COLUMN MASTER KEY [{Name}] WITH ( KEY_STORE_PROVIDER_NAME = N'{KeyStoreProviderName}', KEY_PATH = N'{KeyPath}', - ENCLAVE_COMPUTATIONS (SIGNATURE = {cmkSignStr}) + ENCLAVE_COMPUTATIONS (SIGNATURE = {CmkSignStr}) );"; } else diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CspColumnMasterKey.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CspColumnMasterKey.cs index 9e26b1105e..8a27b2b860 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CspColumnMasterKey.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/CspColumnMasterKey.cs @@ -21,7 +21,7 @@ public CspColumnMasterKey(string name, string certificateThumbprint, SqlColumnEn KeyPath = string.Concat(CertificateStoreLocation.ToString(), "/", CertificateStoreName.ToString(), "/", Thumbprint); byte[] cmkSign = certStoreProvider.SignColumnMasterKeyMetadata(KeyPath, allowEnclaveComputations); - cmkSignStr = string.Concat("0x", BitConverter.ToString(cmkSign).Replace("-", string.Empty)); + CmkSignStr = string.Concat("0x", BitConverter.ToString(cmkSign).Replace("-", string.Empty)); } public CspColumnMasterKey(string name, string providerName, string cspKeyPath, SqlColumnEncryptionKeyStoreProvider certStoreProvider, bool allowEnclaveComputations) : base(name) @@ -29,7 +29,7 @@ public CspColumnMasterKey(string name, string providerName, string cspKeyPath, S KeyStoreProviderName = providerName; KeyPath = cspKeyPath; byte[] cmkSign = certStoreProvider.SignColumnMasterKeyMetadata(KeyPath, allowEnclaveComputations); - cmkSignStr = string.Concat("0x", BitConverter.ToString(cmkSign).Replace("-", string.Empty)); + CmkSignStr = string.Concat("0x", BitConverter.ToString(cmkSign).Replace("-", string.Empty)); } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj b/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj index afbd056adb..810775db77 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj @@ -17,47 +17,47 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Common\System\Collections\DictionaryExtensions.cs @@ -278,7 +278,7 @@ - + diff --git a/src/Microsoft.Data.SqlClient/tests/NSLibrary/Microsoft.Data.SqlClient.NSLibrary.csproj b/src/Microsoft.Data.SqlClient/tests/NSLibrary/Microsoft.Data.SqlClient.NSLibrary.csproj index 5c1d2ce4c5..d526f2497c 100644 --- a/src/Microsoft.Data.SqlClient/tests/NSLibrary/Microsoft.Data.SqlClient.NSLibrary.csproj +++ b/src/Microsoft.Data.SqlClient/tests/NSLibrary/Microsoft.Data.SqlClient.NSLibrary.csproj @@ -1,7 +1,8 @@ - netstandard2.0 + netstandard2.0;netstandard2.1 + netstandard2.1 AnyCPU;x86;x64 $(ObjFolder)$(Configuration).$(Platform)\$(AssemblyName) $(BinFolder)$(Configuration).$(Platform)\$(AssemblyName) diff --git a/tools/specs/Microsoft.Data.SqlClient.nuspec b/tools/specs/Microsoft.Data.SqlClient.nuspec index 3e09d83333..362ffdd7c8 100644 --- a/tools/specs/Microsoft.Data.SqlClient.nuspec +++ b/tools/specs/Microsoft.Data.SqlClient.nuspec @@ -67,6 +67,17 @@ When using NuGet 3.x this package requires at least version 3.4. + + + + + + + + + + + @@ -93,6 +104,11 @@ When using NuGet 3.x this package requires at least version 3.4. + + + + + @@ -117,6 +133,10 @@ When using NuGet 3.x this package requires at least version 3.4. + + + + @@ -148,6 +168,10 @@ When using NuGet 3.x this package requires at least version 3.4. + + + + @@ -169,5 +193,10 @@ When using NuGet 3.x this package requires at least version 3.4. + + + + +