-
Notifications
You must be signed in to change notification settings - Fork 335
Implement performance benchmarks for Always Encrypted scenarios #4502
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 14 commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
9eec514
Create new RAII types for CMK and CEK objects
edwardneal 917a5ae
Designate certain types as compatible with Always Encrypted
edwardneal e560f4a
Refactor DataTypeReaderRunner
edwardneal 5e3ccd0
Refactor AsyncLargeDataReadRunner
edwardneal 7c69868
Documentation/style changes
edwardneal c21072c
Honour MdsPackageVersion in TestCommon package reference
cheenamalhotra b8c9246
Fix in-process timeout failure in large data read benchmarks
cheenamalhotra 386096d
Merge pull request #1 from cheenamalhotra/dev/cheena/benchmarks-ae
edwardneal a4c762e
Move ReadBufferBytes to Plaintext derived class
edwardneal e8b65a2
Use the existing SqlDataReader extension methods to flush the result set
edwardneal 66cc1ca
Reorder members by visibility
edwardneal cd1855b
Force SqlDataExtensions to explicitly flush the row data
edwardneal a1896e7
Replace Params with Arguments
edwardneal 227aa9a
Merge main
edwardneal ed08aa9
Lift Always Encrypted definition values to constants
edwardneal 434b01f
Calculate DefaultValue and EncryptionSupported properties
edwardneal e44ba1b
Remove VersionOverride for TestCommon
edwardneal File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
62 changes: 62 additions & 0 deletions
62
src/Microsoft.Data.SqlClient/tests/Common/Fixtures/DatabaseObjects/ColumnEncryptionKey.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| // 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.Security.Cryptography; | ||
|
|
||
| namespace Microsoft.Data.SqlClient.Tests.Common.Fixtures.DatabaseObjects; | ||
|
|
||
| /// <summary> | ||
| /// A column encryption key, created at the start of its scope and dropped when disposed. | ||
| /// </summary> | ||
| public sealed class ColumnEncryptionKey : DatabaseObject<ColumnMasterKey> | ||
| { | ||
| private const int PlaintextKeyLength = 32; | ||
|
|
||
| private const string DefinitionTemplate = "CREATE COLUMN ENCRYPTION KEY {0} WITH VALUES" + | ||
| " (COLUMN_MASTER_KEY = {1}, ALGORITHM = 'RSA_OAEP', ENCRYPTED_VALUE = 0x{2})"; | ||
|
|
||
| private ColumnMasterKey ColumnMasterKey => State; | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the ColumnEncryptionKey class using the specified SQL connection, | ||
| /// name and a column master key. | ||
| /// </summary> | ||
| /// <param name="connection">The SQL connection used to interact with the database.</param> | ||
| /// <param name="namePrefix">The column encryption key name.</param> | ||
| /// <param name="cmkOrigin">The column master key which backs this encryption key.</param> | ||
| public ColumnEncryptionKey(SqlConnection connection, string namePrefix, ColumnMasterKey cmkOrigin) | ||
| : base(connection, GenerateLongName(namePrefix), definition: DefinitionTemplate, | ||
| state: cmkOrigin, shouldCreate: true, shouldDrop: true) | ||
| { | ||
| } | ||
|
|
||
| protected override void CreateObject(string definition) | ||
| { | ||
| string encryptedValue; | ||
|
|
||
| using (RandomNumberGenerator rnd = RandomNumberGenerator.Create()) | ||
| { | ||
| byte[] randomPlaintext = new byte[PlaintextKeyLength]; | ||
| byte[] encryptedPlaintext; | ||
|
|
||
| rnd.GetBytes(randomPlaintext); | ||
| encryptedPlaintext = ColumnMasterKey.Encrypt(randomPlaintext); | ||
|
|
||
| encryptedValue = BitConverter.ToString(encryptedPlaintext).Replace("-", ""); | ||
| } | ||
|
|
||
| definition = string.Format(definition, Name, ColumnMasterKey.Name, encryptedValue); | ||
| using SqlCommand createCommand = new(definition, Connection); | ||
|
|
||
| createCommand.ExecuteNonQuery(); | ||
| } | ||
|
|
||
| protected override void DropObject() | ||
| { | ||
| using SqlCommand dropCommand = new($"IF EXISTS (SELECT 1 FROM sys.column_encryption_keys where name = @Name) DROP COLUMN ENCRYPTION KEY {Name}", Connection); | ||
| dropCommand.Parameters.AddWithValue("@Name", UnescapedName); | ||
|
|
||
| dropCommand.ExecuteNonQuery(); | ||
| } | ||
| } |
179 changes: 179 additions & 0 deletions
179
src/Microsoft.Data.SqlClient/tests/Common/Fixtures/DatabaseObjects/ColumnMasterKey.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,179 @@ | ||
| // 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.Text; | ||
|
|
||
| namespace Microsoft.Data.SqlClient.Tests.Common.Fixtures.DatabaseObjects; | ||
|
|
||
| /// <summary> | ||
| /// A column master key, created at the start of its scope and dropped when disposed. | ||
| /// </summary> | ||
| public abstract class ColumnMasterKey : DatabaseObject<ColumnMasterKey.CreationParameters> | ||
| { | ||
| private const string DefinitionTemplate = "CREATE COLUMN MASTER KEY {0} WITH (KEY_STORE_PROVIDER_NAME = '{1}', KEY_PATH = '{2}'{3})"; | ||
|
|
||
| public sealed class CreationParameters | ||
| { | ||
| public SqlColumnEncryptionKeyStoreProvider Provider { get; } | ||
|
|
||
| public string ProviderName { get; } | ||
|
|
||
| public string KeyPath { get; } | ||
|
|
||
| public bool AllowEnclaveComputations { get; } | ||
|
|
||
| internal CreationParameters(SqlColumnEncryptionKeyStoreProvider provider, | ||
| string providerName, | ||
| string keyPath, | ||
| bool allowEnclaveComputations) | ||
| { | ||
| Provider = provider; | ||
| ProviderName = providerName; | ||
| KeyPath = keyPath; | ||
| AllowEnclaveComputations = allowEnclaveComputations; | ||
| } | ||
| } | ||
|
|
||
| protected ColumnMasterKey(SqlConnection connection, string namePrefix, CreationParameters creationParameters) | ||
| : base(connection, name: GenerateLongName(namePrefix), definition: DefinitionTemplate, | ||
| state: creationParameters, shouldCreate: true, shouldDrop: true) | ||
| { | ||
| } | ||
|
|
||
| protected override void CreateObject(string definition) | ||
| { | ||
| string enclaveStatement; | ||
|
|
||
| if (State.AllowEnclaveComputations) | ||
| { | ||
| byte[] signature = State.Provider.SignColumnMasterKeyMetadata(State.KeyPath, State.AllowEnclaveComputations); | ||
| string signatureString = BitConverter.ToString(signature).Replace("-", ""); | ||
|
|
||
| enclaveStatement = ", ENCLAVE_COMPUTATIONS (SIGNATURE = 0x" + signatureString + ")"; | ||
| } | ||
| else | ||
| { | ||
| enclaveStatement = string.Empty; | ||
| } | ||
|
|
||
| definition = string.Format(definition, Name, State.ProviderName, State.KeyPath, enclaveStatement); | ||
|
|
||
| using SqlCommand createCommand = new(definition, Connection); | ||
|
|
||
| createCommand.ExecuteNonQuery(); | ||
| } | ||
|
|
||
| protected override void DropObject() | ||
| { | ||
| using SqlCommand dropCommand = new($"IF EXISTS (SELECT 1 FROM sys.column_master_keys where name = @Name) DROP COLUMN MASTER KEY {Name}", Connection); | ||
| dropCommand.Parameters.AddWithValue("@Name", UnescapedName); | ||
|
|
||
| dropCommand.ExecuteNonQuery(); | ||
| } | ||
|
|
||
| public byte[] Encrypt(byte[] columnEncryptionKey) => | ||
| State.Provider.EncryptColumnEncryptionKey(State.KeyPath, "RSA_OAEP", columnEncryptionKey); | ||
|
|
||
| public byte[] Decrypt(byte[] encryptedColumnEncryptionKey) => | ||
| State.Provider.DecryptColumnEncryptionKey(State.KeyPath, "RSA_OAEP", encryptedColumnEncryptionKey); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// A column master key backed by a Cryptographic Service Provider. Created at the start of its | ||
| /// scope and dropped when disposed. | ||
| /// </summary> | ||
| public sealed class CspProviderBackedColumnMasterKey : ColumnMasterKey | ||
| { | ||
| /// <summary> | ||
| /// Initializes a new instance of the CspProviderBackedColumnMasterKey class using the specified | ||
| /// SQL connection, name and a certificate containing a CSP-backed private key. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// <para> | ||
| /// If a column master key with the specified name already exists, it will be dropped automatically | ||
| /// before creation. | ||
| /// </para> | ||
| /// <para> | ||
| /// This column master key will be backed by the <see cref="SqlColumnEncryptionCspProvider"/> class. | ||
| /// </para> | ||
| /// </remarks> | ||
| /// <param name="connection">The SQL connection used to interact with the database.</param> | ||
| /// <param name="namePrefix">The column master key name.</param> | ||
| /// <param name="cspProvider">The certificate to wrap. Must contain a CSP-backed private key.</param> | ||
| /// <param name="allowEnclaveComputations"><c>true</c> to enable enclave computations.</param> | ||
| public CspProviderBackedColumnMasterKey(SqlConnection connection, string namePrefix, | ||
| CspCertificateFixture cspProvider, bool allowEnclaveComputations) | ||
| : base(connection, namePrefix, GenerateCreationParameters(cspProvider, allowEnclaveComputations)) | ||
| { | ||
| } | ||
|
|
||
| private static CreationParameters GenerateCreationParameters(CspCertificateFixture cspProvider, bool allowEnclaveComputations) => | ||
| new(provider: new SqlColumnEncryptionCspProvider(), | ||
| providerName: SqlColumnEncryptionCspProvider.ProviderName, | ||
| cspProvider.CspKeyPath ?? throw new InvalidOperationException("Certificate lacks a CSP key."), | ||
| allowEnclaveComputations); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// A column master key backed by a certificate. Created at the start of its scope and dropped when disposed. | ||
| /// </summary> | ||
| public sealed class CertificateBackedColumnMasterKey : ColumnMasterKey | ||
| { | ||
| /// <summary> | ||
| /// Initializes a new instance of the CertificateBackedColumnMasterKey class using the specified | ||
| /// SQL connection, name and a certificate. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// <para> | ||
| /// If a column master key with the specified name already exists, it will be dropped automatically | ||
| /// before creation. | ||
| /// </para> | ||
| /// <para> | ||
| /// This column master key will be backed by the <see cref="SqlColumnEncryptionCertificateStoreProvider"/> | ||
| /// class. | ||
| /// </para> | ||
| /// </remarks> | ||
| /// <param name="connection">The SQL connection used to interact with the database.</param> | ||
| /// <param name="namePrefix">The column master key name.</param> | ||
| /// <param name="cspCertificate">The certificate to wrap. Must contain a private key.</param> | ||
| /// <param name="allowEnclaveComputations"><c>true</c> to enable enclave computations.</param> | ||
| public CertificateBackedColumnMasterKey(SqlConnection connection, string namePrefix, | ||
| CspCertificateFixture cspCertificate, bool allowEnclaveComputations) | ||
| : base(connection, namePrefix, GenerateCreationParameters(cspCertificate.CspCertificatePath, allowEnclaveComputations)) | ||
| { | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the ColumnMasterKey class using the specified SQL connection, | ||
| /// name and a certificate. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// <para> | ||
| /// If a column master key with the specified name already exists, it will be dropped automatically | ||
| /// before creation. | ||
| /// </para> | ||
| /// <para> | ||
| /// This column master key will be backed by the <see cref="SqlColumnEncryptionCertificateStoreProvider"/> | ||
| /// class. | ||
| /// </para> | ||
| /// </remarks> | ||
| /// <param name="connection">The SQL connection used to interact with the database.</param> | ||
| /// <param name="namePrefix">The column master key name.</param> | ||
| /// <param name="cmkCertificate">The certificate to wrap. Must contain a private key.</param> | ||
| /// <param name="allowEnclaveComputations"><c>true</c> to enable enclave computations.</param> | ||
| public CertificateBackedColumnMasterKey(SqlConnection connection, string namePrefix, | ||
| ColumnMasterKeyCertificateFixture cmkCertificate, bool allowEnclaveComputations) | ||
| : base(connection, namePrefix, GenerateCreationParameters( | ||
| cmkCertificate.ColumnMasterKeyCertificatePath | ||
| ?? throw new InvalidOperationException("Certificate has not been created."), | ||
| allowEnclaveComputations)) | ||
| { | ||
| } | ||
|
|
||
| private static CreationParameters GenerateCreationParameters(string certificatePath, bool allowEnclaveComputations) => | ||
| new(provider: new SqlColumnEncryptionCertificateStoreProvider(), | ||
| providerName: SqlColumnEncryptionCertificateStoreProvider.ProviderName, | ||
| certificatePath, | ||
| allowEnclaveComputations); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.